{"_id":"@adamjessop/tiktok-live-connector","name":"@adamjessop/tiktok-live-connector","dist-tags":{"latest":"2.0.1"},"versions":{"2.0.1":{"name":"@adamjessop/tiktok-live-connector","version":"2.0.1","description":"Node.js module to receive live stream chat events like comments and gifts from TikTok LIVE","main":"./dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=10.0.0"},"scripts":{"build":"npx tsx update-version.ts && npm run build:copy-package && npx tsc && npx tsc-alias","build:copy-package":"cpx package.json dist && cpx LICENSE dist"},"repository":{"type":"git","url":"git+https://github.com/zerodytrash/TikTok-Live-Connector.git"},"funding":["https://buymeacoffee.com/isaackogan","https://buymeacoffee.com/zerody"],"keywords":["tiktok","tiktok live","tiktok.com","live","stream","livestream","chat","connector","api","webcast","tracker","scraper","websocket","bot","broadcast","reader"],"author":{"name":"zerodytrash"},"license":"MIT","bugs":{"url":"https://github.com/zerodytrash/TikTok-Live-Connector/issues"},"homepage":"https://github.com/zerodytrash/TikTok-Live-Connector#readme","dependencies":{"@bufbuild/protobuf":"^2.2.5","@eulerstream/euler-api-sdk":"^0.0.4","axios":"^1.9.0","callable-instance":"^2.0.0","protobufjs":"^6.11.2","websocket":"^1.0.35"},"devDependencies":{"@babel/cli":"^7.17.0","@babel/core":"^7.17.2","@babel/preset-env":"^7.16.11","@types/node":"^22.15.3","@types/websocket":"^1.0.10","@types/ws":"^8.18.1","cpx":"^1.5.0","dotenv":"^16.5.0","prettier":"2.5.1","ts-proto":"^2.6.1","tsc-alias":"^1.8.15","typed-emitter":"^2.1.0","typescript":"^4.8.2"},"_id":"@adamjessop/tiktok-live-connector@2.0.1","gitHead":"045d68b289ec4ab18939bb6eed3e19f819c17b2e","_nodeVersion":"22.4.0","_npmVersion":"10.8.1","dist":{"integrity":"sha512-Bo+wJyHixkXMh1otANVIKonUPqtPjTRHNOOVaLeZatnA1gB0RTOvLpllhpFQndWEvA6GGIgfkdzxlcCpfaDktQ==","shasum":"868b7108fc0cf4082cee2d18317525294c042246","tarball":"https://registry.npmjs.org/@adamjessop/tiktok-live-connector/-/tiktok-live-connector-2.0.1.tgz","fileCount":125,"unpackedSize":873790,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCCrrYcvd+XHletxsO5qTrTLA5qNbLNvIdSOWpIr2geoAIhAIxCqDA4l8aeIlZGCUZOWBP6hkprhkWkiaOfHRBZdTWk"}]},"_npmUser":{"name":"adamjessop","email":"me@adamjessop.net"},"directories":{},"maintainers":[{"name":"adamjessop","email":"me@adamjessop.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tiktok-live-connector_2.0.1_1746633566508_0.16017449595569433"},"_hasShrinkwrap":false}},"time":{"created":"2025-05-07T15:59:26.422Z","2.0.1":"2025-05-07T15:59:26.753Z","modified":"2025-05-07T15:59:26.977Z"},"maintainers":[{"name":"adamjessop","email":"me@adamjessop.net"}],"description":"Node.js module to receive live stream chat events like comments and gifts from TikTok LIVE","homepage":"https://github.com/zerodytrash/TikTok-Live-Connector#readme","keywords":["tiktok","tiktok live","tiktok.com","live","stream","livestream","chat","connector","api","webcast","tracker","scraper","websocket","bot","broadcast","reader"],"repository":{"type":"git","url":"git+https://github.com/zerodytrash/TikTok-Live-Connector.git"},"author":{"name":"zerodytrash"},"bugs":{"url":"https://github.com/zerodytrash/TikTok-Live-Connector/issues"},"license":"MIT","readme":"# TikTok-Live-Connector\n\nA Node.js library to receive live stream events such as comments and gifts in realtime\nfrom [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal Webcast push service.\nThis package includes a wrapper that connects to the Webcast service using just the username (`@uniqueId`).\nThis allows you to connect to your own live chat as well as the live chat of other streamers. No credentials are\nrequired. Besides [Chat Comments](#chat), other events such\nas [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like)\nand [Battles](#linkmicbattle) can be tracked.\n\n![Connections](https://tiktok.eulerstream.com/analytics/pips/1?client=ttlive-node)\n![Downloads](https://img.shields.io/npm/dw/tiktok-live-connector?style=flat&color=0274b5&alt=1)\n![Stars](https://img.shields.io/github/stars/zerodytrash/tiktok-live-connector?style=flat&color=0274b5&alt=1)\n![Issues](https://img.shields.io/github/issues/zerodytrash/tiktok-live-connector?style=flat&color=0274b5&alt=1)\n![Forks](https://img.shields.io/github/forks/zerodytrash/tiktok-live-connector?style=flat&color=0274b5&alt=1)\n\n> [!NOTE]\n> This is not an official API. It is a reverse engineering project. You are responsible for your usage of this library.\n\n> [!TIP]\n> An example project is available\n> at https://tiktok-chat-reader.zerody.one/ - [View Source](https://github.com/zerodytrash/TikTok-Chat-Reader)\n\n> [!WARNING]\n> This JavaScript library is intended for use in [Node.js](https://nodejs.org/) environments. If you want to process or\n> display the data in the browser (client-side), you need to transfer the data from the Node.js environment to the\n> browser. A good approach for this is to use [Socket.IO](https://socket.io/) or a different low-latency communication\n> framework. A complete example project can be found\n> here: [TikTok-Chat-Reader](https://github.com/zerodytrash/TikTok-Chat-Reader)\n\n> [!WARNING]\n> Due to a change on the part of TikTok, versions prior **v1.1.7** are no longer functional. If you are using one of\n> these versions, upgrade to the latest version using the `npm i tiktok-live-connector` command.\n\n### 📌 2.0.0 Now Available - Upgrade Now!\n\nWe are thrilled to announce that the 2.0.0 release of TikTok Live Connector is now available!\nThis release converts the package to exclusively typescript-based, also providing complete backwards compatibility for users.\n\n#### Upgrade Guide\n\n- Install via `npm i tiktok-live-connector@2.0.0`\n- Switch `WebcastPushConnection` to `TikTokLiveConnection` (or keep for legacy support)\n- Switch Event Names to `WebcastEvent.EVENT_NAME` from `event_name` format\n- That's it! You're good to go!\n- If you have any issues, please open an issue on GitHub or ask on [Discord](https://discord.gg/2c6kX6g3Pa).\n\n#### Breaking Changes\n\n- The `sendMessage` function has been removed (for now)\n- `WebcastPushConnection` has been renamed to `TikTokLiveConnection`\n- CommonJS support has been removed\n- Data fetching functions have been renamed\n\n### Do you prefer other programming languages?\n\n- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive)\n  by [@isaackogan](https://github.com/isaackogan) and [@eulerstream](https://github.com/eulerstream/)\n- **Java** rewrite: [TikTokLiveJava](https://github.com/jwdeveloper/TikTokLiveJava)\n  by [@jwdeveloper](https://github.com/jwdeveloper) and [@kohlerpop1](https://github.com/kohlerpop1)\n- **Go** rewrite: [GoTikTokLive](https://github.com/steampoweredtaco/gotiktoklive)\n  by [@steampoweredtaco](https://github.com/steampoweredtaco) and [@davincible](https://github.com/davincible)\n- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp)\n  by [@frankvHoof93](https://github.com/frankvHoof93)\n\n### Table of Contents\n\n- [Getting Started](#getting-started)\n- [Params and Options](#params-and-options)\n- [Methods](#methods)\n- [Properties](#properties)\n- [Events](#events)\n- [Examples](#examples)\n- [Contributing](#contributing)\n\n## Getting Started\n\n1. Install the package via NPM\n\n```bash\nnpm i tiktok-live-connector\n```\n\n2. Create your first TikTok LIVE chat connection\n\n```ts\n// Username of someone who is currently live\nimport { TikTokLiveConnection, WebcastEvent } from 'tiktok-live-connector';\n\nconst tiktokUsername = 'officialgeilegisela';\n\n// Create a new wrapper object and pass the username\nconst connection = new TikTokLiveConnection(tiktokUsername);\n\n// Connect to the chat (await can be used as well)\nconnection.connect().then(state => {\n    console.info(`Connected to roomId ${state.roomId}`);\n}).catch(err => {\n    console.error('Failed to connect', err);\n});\n\n// Define the events that you want to handle\n// In this case we listen to chat messages (comments)\nconnection.on(WebcastEvent.CHAT, data => {\n    console.log(`${data.user.uniqueId} (userId:${data.user.uniqueId}) writes: ${data.comment}`);\n});\n\n// And here we receive gifts sent to the streamer\nconnection.on(WebcastEvent.GIFT, data => {\n    console.log(`${data.user.uniqueId} (userId:${data.user.userId}) sends ${data.giftId}`);\n});\n\n// ...and more events described in the documentation below\n```\n\n## Params and Options\n\nTo create a new `TikTokLiveConnection` object the following parameters can be specified.\n\n`TikTokLiveConnection(uniqueId, [options])`\n\n| Param Name | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| uniqueId   | Yes      | The unique username of the broadcaster. You can find this name in the URL.<br>Example: `https://www.tiktok.com/@officialgeilegisela/live` => `officialgeilegisela`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| options    | No       | Here you can set the following optional connection properties. If you do not specify a value, the default value will be used.<br><br>`processInitialData` (default: `true`) <br> Define if you want to process the initital data which includes old messages of the last seconds.<br><br>`fetchRoomInfoOnConnect` (default: `true`) <br> Define if you want to fetch all room information on [`connect()`](#methods). If this option is enabled, the connection to offline rooms will be prevented. If enabled, the connect result contains the room info via the `roomInfo` attribute. You can also manually retrieve the room info (even in an unconnected state) using the [`getRoomInfo()`](#methods) function.<br><br>`enableExtendedGiftInfo` (default: `false`) <br> Define if you want to receive extended information about gifts like gift name, cost and images. This information will be provided at the [gift event](#gift). <br><br>`requestPollingIntervalMs` (default: `1000`) <br> Request polling interval if WebSocket is not used.<br><br>`sessionId` (default: `null`) <br> Here you can specify the current Session ID of your TikTok account <br><br>`webClientParams` (default: `{}`) <br> Custom client params for Webcast API.<br><br>`webClientHeaders` (default: `{}`) <br> Custom request headers passed to [axios](https://github.com/axios/axios).<br><br>`websocketHeaders` (default: `{}`) <br> Custom websocket headers passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). <br><br>`webClientOptions` (default: `{}`) <br> Custom request options passed to [axios](https://github.com/axios/axios). Here you can specify an `httpsAgent` to use a proxy and a `timeout` value. See [Example](#proxied-connection). <br><br>`wsClientParams` (default: `${}`)<br/> WebSocket parameters to be appended to the connection url. <br><br>`wsClientHeaders` (default: {})<br/> Custom WebSocket headers to be sent when connecting. <br><br>`wsClientOptions` (default: `{}`) <br> Custom websocket options passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). Here you can specify an `agent` to use a proxy and a `timeout` value. See [Example](#proxied-connection). <br/><br/> `authenticateWs` (default: `false`) <br/> By default, WebSocket connections are not authenticated, even when passing a sessionid, for security. This is because 'signing' (generating the URL) for connecting is done by a 3rd-party freeware service. It must be manually enabled, and you assume the risks associated with sending over a session id when you enable it.<br/><br/>`preferredAgentIds` (default: `[]`)<br/>WebSocket URL generation is done by a 3rd-party service. This option allows you to specify a specific 'agent' on the 3rd-party service to handle the URL generation.<br/><br/>`connectWithUniqueId` (default: `false`)<br/>This option allows the 3rd-party service to determine the Room ID _for_ you, rather than retrieving it through scraping. This may be preferable on low-quality IPs, as it bypasses captchas.<br/><br/>`logFetchFallbackErrors` (default: false)<br/>This option logs exceptions to the console when fallbacks are required to resolve a Room Id from a uniqueId.<br/><br/>`signedWebSocketProvider` (default: `(props: FetchSignedWebSocketParams) => Promise<WebcastResponse>`)<br/> The function responsible for signing (generating a valid WebSocket URL) can be swapped out for your own backend, if you do not want to use the free 3rd-party service bundled into the client that generates them. |\n\n#### Example Options\n\n```ts\nconst tikTokLiveConnection = new TikTokLiveConnection(tiktokUsername, {\n    processInitialData: false,\n    enableExtendedGiftInfo: true,\n    requestPollingIntervalMs: 2000,\n    webClientParams: {\n        \"app_language\": \"en-US\",\n        \"device_platform\": \"web\"\n    },\n    webClientHeaders: {\n        \"headerName\": \"headerValue\"\n    },\n    wsClientHeaders: {\n        \"headerName\": \"headerValue\"\n    },\n    wsClientParams: {\n        \"app_language\": \"en-US\",\n    },\n    webClientOptions: {\n        timeout: 10000\n    },\n    wsClientOptions: {\n        timeout: 10000\n    }\n});\n```\n\n## Methods\n\n| Method Name               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| connect(`[roomId]`)       | Connects to the live stream chat.<br>Returns a `Promise` which will be resolved when the connection is successfully established. Optionally, you can provide a Room Id to bypass Room Id scraping.                                                                                                                                                                                                                                                                          |\n| disconnect()              | Disconnects from the stream if connected.                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n| fetchRoomId(`[uniqueId]`) | Fetch the Room Id associated with the currently configured `uniqueId`. Optionally, provide a `uniqueId` for a different user to check. Returns a `Promise` which is resolved when the Room Id is fetched.                                                                                                                                                                                                                                                                   |\n| fetchIsLive()             | Fetch whether the user is currently streaming. Returns a `Promise` which resolves when the state is fetched.                                                                                                                                                                                                                                                                                                                                                                |\n| waitUntilLive()           | Returns a blocking promise that resolves when the user goes live.                                                                                                                                                                                                                                                                                                                                                                                                           |\n| fetchRoomInfo()           | Gets the current room info from TikTok API including streamer info, room status and statistics.<br>Returns a `Promise` which will be resolved when the API request is done.<br>*<b>Note: </b>You can call this function even if you're not connected.*<br>[Example](#retrieve-room-info)                                                                                                                                                                                    |\n| fetchAvailableGifts()     | Gets a list of all available gifts including gift name, image url, diamont cost and a lot of other information.<br>Returns a `Promise` that will be resolved when all available gifts has been retrieved from the API.<br>*<b>Note: </b>You can call this function even if you're not connected.*<br>[Example](#retrieve-available-gifts)                                                                                                                                   |\n\n## Properties\n\n| Property Name                             | Description                                                                                                                               |\n|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|\n| webClient: `TikTokWebClient`              | An API wrapper for TikTok with routes for scraping the TikTok site & internal APIs. This is used to fetch the initial data and room info. |\n| wsClient: `TikTokWsClient \\| null`        | The WebSocket client object. This manages the lifecycle of the WebSocket connection to TikTok, and is where all events originate from.    |\n| options: `TikTokLiveConnectionOptions`    | Options described in [Params and Options](#params-and-options)                                                                            |\n| roomInfo: `RoomInfoResponse`              | The room info object. This contains all information about the current room.                                                               |\n| availableGifts: `RoomGiftInfo`            | The list of available gifts. This is only filled if `enableExtendedGiftInfo` is set to `true`.                                            |\n| isConnecting: `boolean`                   | Indicates whether the connection is currently being established.                                                                          |\n| isConnected: `boolean`                    | Indicates whether the connection is currently established.                                                                                |\n| webClientParams: `Record<string, string>` | Base URI parameters that are sent with every request of `TikTokWebClient`.<br/>                                                           |\n| roomId: `string`                          | The current room id. This is only available after a successful connection.                                                                |\n| state: `TikTokLiveConnectionState`        | The current state of the connection. Includes info such as connection status, Room Id, room info, etc.                                    |\n\n## Signing Configuration\n\nIt is possible to configure the [3rd-party library](https://github.com/EulerStream/EulerApiSdk) used to generate\nWebSocket URLs, a process referred to as 'signing'. An example is provided below:\n\n```ts\n// SignConfig is an instance of Partial<ClientConfiguration>\nimport { SignConfig } from 'tiktok-live-connector';\n\nSignConfig.apiKey = \"your api key\" // An API key created at https://www.eulerstream.com\nSignConfig.basePath = \"https://your-custom-sign-server.com\" // Optionally, you can even define your own server\nSignConfig.baseOptions.headers['X-Custom-Header'] = 'Custom-Header-Value'\n```\n\n## Accessing TikTok LIVE Routes\n\nThe `TikTokWebClient` object is used to access TikTok's internal API routes. This object is available via\nthe `webClient` property of the `TikTokLiveConnection` object.\n\nThe following routes come bundled with the web client:\n\n- `connection.webClient.fetchRoomInfo`\n- `connection.webClient.sendRoomChat`\n- `connection.webClient.fetchRoomInfoFromApiLive`\n- `connection.webClient.fetchRoomInfoFromHtml`\n- `connection.webClient.fetchSignedWebSocketFromEuler`\n- `connection.webClient.fetchRoomIdFromEuler`\n- `connection.webClient.fetchRoomInfoFromEuler`\n\n### Example Usage:\n\nAll routes are callable classes that accepts a singular object with the required parameters, and return a promise\nthat resolves with the route data. For instance, here is how you can fetch Room Info from the page HTML:\n\n```ts\nconst connection = new TikTokLiveConnection('officialgeilegisela');\n\nconnection.webClient.fetchRoomInfoFromHtml({ uniqueId: uniqueId })\n    .then((response: FetchRoomInfoFromHtmlRouteResponse) => {\n        console.log('Room Info:', response.data);\n    });\n```\n\n## Accessing 3rd-Party Routes\n\nThe `TikTokWebClient` comes bundled with an instance of `EulerSigner`, a 3rd-party library that provides the WebSocket\nconnection URL used to\nconnect to TikTok LIVE.\n\nThis is publicly accessible via `connection.webClient.webSigner` and exposes additional 3rd-party routes related to\nTikTok LIVE.\n\n### Example 1: Fetching Rate Limits\n\nFor example, here's how you can fetch the rate limits for your current API key:\n\n```ts\nimport { SignConfig } from 'tiktok-live-connector';\nimport { IGetRateLimits } from '@eulerstream/euler-api-sdk';\n\n// Configure an API Key\nSignConfig.apiKey = 'your api key'; // An API key created at https://www.eulerstream.com\n\n// Create a connection\nconst connection = new TikTokLiveConnection();\n\n// Fetch the limits\nconnection.webClient.webSigner.webcast.getRateLimits()\n    .then((response: AxiosResponse<IGetRateLimits>) => {\n        console.log('Rate Limits:', response.data);\n    });\n```\n\n### Example 2: Using JWT Authentication\n\nIf you intend to run the TikTok-Live-Connector in a client environment (e.g. a bundled desktop app), you won't want to\ngive the user your API key. Instead, you can create a JWT for the user to connect to the API.\n\n### Server-Side:\n\nFirst, generate the JWT and return it to your user:\n\n```ts\nimport { SignConfig, TikTokWebClient } from './src';\n\nSignConfig.apiKey = 'your_api_key_here';\nconst connection = new TikTokWebClient();\n\nconnection.webSigner.authentication.createJWT(\n    122, // Your account ID\n    {\n        limits: {\n            minute: 5,\n            day: 5,\n            hour: 5\n        },\n        expireAfter: 60 * 60 * 2 // 2 hours is the max accepted value\n    }\n).then((res) => {\n    console.log('Generated JWT:', res.data.token);\n});\n```\n\n### Client-Side:\n\nThen, use the JWT to connect to the API in the client-side NodeJS application:\n\n```ts\nimport { SignConfig } from './config';\nimport { TikTokLiveConnection } from './client';\n\nSignConfig.baseOptions.headers['x-jwt-key'] = 'generated-jwt-key';\nconst connection = new TikTokLiveConnection('tv_asahi_news');\n```\n\n## Events\n\nA `TikTokLiveConnection` object has the following events which can be handled with an event listener.\n\nThe simplest event handler is a connect event:\n\n```ts\nconst connection = new TikTokLiveConnection('officialgeilegisela');\nconnection.on(ControlEvent.CONNECTED, () => console.log(\"Connected!\"));\n```\n\n### Control Events:\n\n- [`ControleEvent.CONNECTED`](#connected) or `\"connected\"`\n- [`ControleEvent.DISCONNECTED`](#disconnected) or `\"disconnected\"`\n- [`ControleEvent.STREAM_END`](#streamend) or `\"streamEnd\"`\n- [`ControleEvent.RAW_DATA`](#rawdata) or `\"rawData\"`\n- [`ControleEvent.DECODED_DATA`](#decodeddata) or `\"decodedData\"`\n- [`ControleEvent.WEBSOCKET_CONNECTED`](#websocketconnected) or `\"websocketConnected\"`\n- [`ControleEvent.ERROR`](#error) or `\"error\"`\n\n### Message Events:\n\n- [`WebcastEvent.CHAT`](#chat) or `\"chat\"`\n- [`WebcastEvent.GIFT`](#gift) or `\"gift\"`\n- [`WebcastEvent.MEMBER`](#member) or `\"member\"`\n- [`WebcastEvent.LIKE`](#like) or `\"like\"`\n- [`WebcastEvent.SOCIAL`](#social) or `\"social\"`\n- [`WebcastEvent.ENVELOPE`](#envelope) or `\"envelope\"`\n- [`WebcastEvent.QUESTION_NEW`](#questionnew) or `\"questionNew\"`\n- [`WebcastEvent.LINK_MIC_BATTLE`](#linkmicbattle) or `\"linkMicBattle\"`\n- [`WebcastEvent.LINK_MIC_ARMIES`](#linkmicarmies) or `\"linkMicArmies\"`\n- [`WebcastEvent.LIVE_INTRO`](#liveintro) or `\"liveIntro\"`\n- [`WebcastEvent.SUBSCRIBE`](#subscribe) or `\"subscribe\"`\n- [`WebcastEvent.FOLLOW`](#follow) or `\"follow\"`\n- [`WebcastEvent.SHARE`](#share) or `\"share\"`\n- [`WebcastEvent.STREAM_END`](#streamend) or `\"streamEnd\"`\n- [`WebcastEvent.ROOM_USER`](#roomuser) or `\"roomUser\"`\n- [`WebcastEvent.EMOTE`](#emote) or `\"emote\"`\n- [`WebcastEvent.FOLLOW`](#follow) or `\"follow\"`\n- [`WebcastEvent.SHARE`](#share) or `\"share\"`\n\n## Control Events\n\n### `connected`\n\nTriggered when the connection is successfully established.\n\n```ts\nclient.on(\n    WebcastEvent.CONNECTED,\n    (state: TikTokLiveConnectionState) => console.log('Hurray! Connected!', state)\n);\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    isConnected: true,\n    upgradedToWebsocket: true,\n    roomId: '7137682087200557829',\n    roomInfo: {\n        AnchorABMap: {\n        },\n        admin_user_ids: [],\n        anchor_scheduled_time_text: '',\n        anchor_share_text: '',\n        anchor_tab_type: 7,\n        answering_question_content: '',\n        app_id: 1233,\n        audio_mute: 0,\n        auto_cover: 0,\n        book_end_time: 0,\n        book_time: 0,\n        business_live: 0,\n        challenge_info: '',\n        client_version: 250701,\n        comment_has_text_emoji_emote: 0,\n        comment_name_mode: 0,\n        commerce_info: {\n            commerce_permission: 0,\n            oec_live_enter_room_init_data: '',\n            use_async_load: false\n        },\n        common_label_list: '',\n        content_tag: '',\n        cover: {\n            avg_color: '',\n            height: 0,\n            image_type: 0,\n            is_animated: false,\n            open_web_url: '',\n            uri: '720x720/tos-maliva-avt-0068/4e64db7f7c37caf9b2df71df8580a9b0',\n            url_list: [\n                \"<Array>\"\n            ],\n            width: 0\n        },\n        create_time: 1661871149,\n        deco_list: [],\n        deprecated10: '',\n        deprecated11: '',\n        deprecated12: '',\n        deprecated13: '',\n        deprecated14: 0,\n        deprecated15: 0,\n        deprecated16: 0,\n        deprecated17: [],\n        deprecated18: 0,\n        deprecated19: '',\n        deprecated195: false,\n        deprecated2: '',\n        deprecated20: 0,\n        deprecated21: false,\n        deprecated22: 0,\n        deprecated23: '',\n        deprecated24: 0,\n        deprecated26: '',\n        deprecated28: '',\n        deprecated3: {\n        },\n        deprecated30: '',\n        deprecated31: false,\n        deprecated32: '',\n        deprecated35: 0,\n        deprecated36: 0,\n        deprecated39: '',\n        deprecated4: 0,\n        deprecated41: 0,\n        deprecated43: false,\n        deprecated44: 0,\n        deprecated5: false,\n        deprecated6: '',\n        deprecated7: 0,\n        deprecated8: '',\n        deprecated9: '',\n        disable_preload_stream: false,\n        drawer_tab_position: '',\n        effect_info: [],\n        existed_commerce_goods: false,\n        fansclub_msg_style: 2,\n        feed_room_label: {\n            avg_color: '#F1FFEB',\n            height: 0,\n            image_type: 0,\n            is_animated: false,\n            open_web_url: '',\n            uri: 'webcast-sg/2ea90002aca1159b5c67',\n            url_list: [\n                \"<Array>\"\n            ],\n            width: 0\n        },\n        feed_room_labels: [],\n        filter_msg_rules: [],\n        finish_reason: 0,\n        finish_time: 1661878842,\n        finish_url: '',\n        finish_url_v2: '',\n        follow_msg_style: 2,\n        forum_extra_data: '',\n        game_tag: [],\n        gift_msg_style: 2,\n        gift_poll_vote_enabled: false,\n        group_source: 0,\n        has_commerce_goods: false,\n        have_wishlist: false,\n        hot_sentence_info: '',\n        id: 7137682087200558000,\n        id_str: '7137682087200557829',\n        indicators: [],\n        interaction_question_version: 0,\n        introduction: '',\n        is_gated_room: false,\n        is_replay: false,\n        is_show_user_card_switch: false,\n        last_ping_time: 1661878842,\n        layout: 0,\n        like_count: 0,\n        link_mic: {\n            audience_id_list: [],\n            battle_scores: [],\n            battle_settings: [\n                \"<Object>\"\n            ],\n            channel_id: 0,\n            followed_count: 0,\n            linked_user_list: [],\n            multi_live_enum: 1,\n            rival_anchor_id: 0,\n            show_user_list: []\n        },\n        linker_map: {\n        },\n        linkmic_layout: 0,\n        live_distribution: [],\n        live_id: 12,\n        live_reason: '',\n        live_room_mode: 0,\n        live_sub_only: 0,\n        live_type_audio: false,\n        live_type_linkmic: false,\n        live_type_normal: true,\n        live_type_sandbox: false,\n        live_type_screenshot: false,\n        live_type_social_live: false,\n        live_type_third_party: false,\n        living_room_attrs: {\n            admin_flag: 0,\n            rank: 0,\n            room_id: 7137682087200558000,\n            room_id_str: '7137682087200557829',\n            silence_flag: 0\n        },\n        lottery_finish_time: 0,\n        mosaic_status: 0,\n        os_type: 1,\n        owner: {\n            allow_find_by_contacts: false,\n            allow_others_download_video: false,\n            allow_others_download_when_sharing_video: false,\n            allow_share_show_profile: false,\n            allow_show_in_gossip: false,\n            allow_show_my_action: false,\n            allow_strange_comment: false,\n            allow_unfollower_comment: false,\n            allow_use_linkmic: false,\n            avatar_large: [\n                \"<Object>\"\n            ],\n            avatar_medium: [\n                \"<Object>\"\n            ],\n            avatar_thumb: [\n                \"<Object>\"\n            ],\n            badge_image_list: [],\n            badge_list: [],\n            bg_img_url: '',\n            bio_description: 'HH📍🇩🇪تابعوني انستغرام\\nاذا سقطت سأخذ الجميع معي\\n👻Alin_issa22👻',\n            block_status: 0,\n            border_list: [],\n            comment_restrict: 0,\n            commerce_webcast_config_ids: [],\n            constellation: '',\n            create_time: 0,\n            deprecated1: 0,\n            deprecated12: 0,\n            deprecated13: 0,\n            deprecated15: 0,\n            deprecated16: false,\n            deprecated17: false,\n            deprecated18: '',\n            deprecated19: false,\n            deprecated2: 0,\n            deprecated21: 0,\n            deprecated28: false,\n            deprecated29: '',\n            deprecated3: 0,\n            deprecated4: 0,\n            deprecated5: '',\n            deprecated6: 0,\n            deprecated7: '',\n            deprecated8: 0,\n            disable_ichat: 0,\n            display_id: 'alin.i7',\n            enable_ichat_img: 0,\n            exp: 0,\n            fan_ticket_count: 0,\n            fold_stranger_chat: false,\n            follow_info: [\n                \"<Object>\"\n            ],\n            follow_status: 0,\n            ichat_restrict_type: 0,\n            id: 6672446849804223000,\n            id_str: '6672446849804223493',\n            is_follower: false,\n            is_following: false,\n            link_mic_stats: 0,\n            media_badge_image_list: [],\n            modify_time: 1661427082,\n            need_profile_guide: false,\n            new_real_time_icons: [],\n            nickname: '🦋ALIN🦋',\n            own_room: [\n                \"<Object>\"\n            ],\n            pay_grade: [\n                \"<Object>\"\n            ],\n            pay_score: 0,\n            pay_scores: 0,\n            push_comment_status: false,\n            push_digg: false,\n            push_follow: false,\n            push_friend_action: false,\n            push_ichat: false,\n            push_status: false,\n            push_video_post: false,\n            push_video_recommend: false,\n            real_time_icons: [],\n            sec_uid: 'MS4wLjABAAAAuUKuWAiw0GQO2_zOeyns0YCBRK7ztdoDWAAQ6gPFLBNSdTs-g5BsgScwTD9jWeK_',\n            secret: 0,\n            share_qrcode_uri: '',\n            special_id: '',\n            status: 1,\n            ticket_count: 0,\n            top_fans: [],\n            top_vip_no: 0,\n            upcoming_event_list: [],\n            user_attr: [\n                \"<Object>\"\n            ],\n            user_role: 0,\n            verified: false,\n            verified_content: '',\n            verified_reason: '',\n            with_car_management_permission: false,\n            with_commerce_permission: false,\n            with_fusion_shop_entry: false\n        },\n        owner_device_id: 0,\n        owner_device_id_str: '',\n        owner_user_id: 6672446849804223000,\n        owner_user_id_str: '',\n        pre_enter_time: 0,\n        preview_flow_tag: 0,\n        ranklist_audience_type: 0,\n        relation_tag: '',\n        replay: true,\n        room_audit_status: 0,\n        room_auth: {\n            Banner: 1,\n            BroadcastMessage: 0,\n            Chat: true,\n            ChatL2: false,\n            ChatSubOnly: false,\n            CommercePermission: 0,\n            CustomizablePoll: 0,\n            Danmaku: false,\n            Digg: true,\n            DonationSticker: 2,\n            EventPromotion: 0,\n            Gift: true,\n            GiftAnchorMt: 1,\n            GiftPoll: 0,\n            GoldenEnvelope: 0,\n            GoldenEnvelopeActivity: 0,\n            InteractionQuestion: true,\n            Landscape: 2,\n            LandscapeChat: 0,\n            LuckMoney: true,\n            Pictionary: 0,\n            Poll: 0,\n            Promote: false,\n            PromoteOther: 0,\n            Props: false,\n            PublicScreen: 1,\n            QuickChat: 0,\n            Rank: 0,\n            RoomContributor: false,\n            Share: true,\n            ShareEffect: 0,\n            ShoppingRanking: 0,\n            UserCard: true,\n            UserCount: 0,\n            Viewers: false,\n            deprecated1: false,\n            deprecated2: 0,\n            deprecated3: 0,\n            deprecated4: 0,\n            deprecated5: 0,\n            deprecated6: 0,\n            deprecated7: 0,\n            deprecated8: 0,\n            deprecated9: 0,\n            transaction_history: 1,\n            use_user_pv: false\n        },\n        room_create_ab_param: '',\n        room_layout: 0,\n        room_sticker_list: [],\n        room_tabs: [],\n        room_tag: 0,\n        scroll_config: '',\n        search_id: 0,\n        share_msg_style: 2,\n        share_url: 'https://m.tiktok.com/share/live/7137682087200557829/?language=en',\n        short_title: '',\n        short_touch_items: [],\n        social_interaction: {\n            linkmic_scene_linker: {\n            },\n            multi_live: [\n                \"<Object>\"\n            ]\n        },\n        start_time: 0,\n        stats: {\n            deprecated1: 0,\n            deprecated2: '',\n            digg_count: 0,\n            enter_count: 0,\n            fan_ticket: 0,\n            follow_count: 686,\n            gift_uv_count: 0,\n            id: 7137682087200558000,\n            id_str: '7137682087200557829',\n            like_count: 0,\n            replay_fan_ticket: 0,\n            replay_viewers: 64076,\n            share_count: 0,\n            total_user: 104582,\n            total_user_desp: '',\n            user_count_composition: [\n                \"<Object>\"\n            ],\n            watermelon: 0\n        },\n        status: 2,\n        sticker_list: [],\n        stream_id: 2993830046178738000,\n        stream_id_str: '2993830046178738249',\n        stream_status: 0,\n        stream_url: {\n            candidate_resolution: [\n                \"<Array>\"\n            ],\n            complete_push_urls: [],\n            default_resolution: 'ORIGION',\n            extra: [\n                \"<Object>\"\n            ],\n            flv_pull_url: [\n                \"<Object>\"\n            ],\n            flv_pull_url_params: [\n                \"<Object>\"\n            ],\n            hls_pull_url: 'https://pull-hls-f16-va01.tiktokcdn.com/stage/stream-2993830046178738249_or4/index.m3u8',\n            hls_pull_url_map: {\n            },\n            hls_pull_url_params: '{\"VCodec\":\"h264\"}',\n            id: 2993830046178738000,\n            id_str: '2993830046178738249',\n            live_core_sdk_data: [\n                \"<Object>\"\n            ],\n            provider: 0,\n            push_urls: [],\n            resolution_name: [\n                \"<Object>\"\n            ],\n            rtmp_pull_url: 'https://pull-f5-va01.tiktokcdn.com/stage/stream-2993830046178738249_or4.flv',\n            rtmp_pull_url_params: '{\"VCodec\":\"h264\"}',\n            rtmp_push_url: '',\n            rtmp_push_url_params: '',\n            stream_control_type: 0\n        },\n        stream_url_filtered_info: {\n            is_gated_room: false,\n            is_paid_event: false\n        },\n        title: 'انا جيت😍 🥰',\n        top_fans: [\n            [\n                \"<Object>\"\n            ],\n            [\n                \"<Object>\"\n            ],\n            [\n                \"<Object>\"\n            ]\n        ],\n        use_filter: false,\n        user_count: 1136,\n        user_share_text: '',\n        video_feed_tag: '',\n        webcast_comment_tcs: 0,\n        webcast_sdk_version: 0,\n        with_draw_something: false,\n        with_ktv: false,\n        with_linkmic: true\n    },\n    availableGifts: []\n}\n```\n\n</p></details>\n\n### `disconnected`\n\nTriggered when the connection gets disconnected. In that case you can call `connect()` again to have a reconnect logic.\nNote that you should wait a little bit before attempting a reconnect to to avoid being rate-limited.\n\n```ts\nconnection.on(ControlEvent.DISCONNECTED, () => console.log('Disconnected :('));\n```\n\n### `streamEnd`\n\nTriggered when the live stream gets terminated by the host. Will also trigger the [`disconnected`](#disconnected) event.\n\n```ts\nconnection.on(ControlEvent.STREAM_END, ({ action }: { action: ControlAction }) => {\n    if (action === ControlAction.CONTROL_ACTION_STREAM_ENDED) {\n        console.log('Stream ended by user');\n    }\n    if (action === ControlAction.CONTROL_ACTION_STREAM_SUSPENDED) {\n        console.log('Stream ended by platform moderator (ban)');\n    }\n});\n```\n\n### `rawData`\n\nTriggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the\nuse case with <a href=\"https://www.npmjs.com/package/protobufjs\">protobufjs</a>.\n\n```ts\nconnection.on(ControlEvent.RAW_DATA, (messageTypeName, binary) => {\n    console.log(messageTypeName, binary);\n});\n```\n\n### `decodedData`\n\nTriggered every time a decoded message arrives. This is the same as the `rawData` event but the binary object is already\ndecoded. You can use this event to handle the protobuf messages without the need of deserializing them yourself.\n\n```ts\nconnection.on(ControlEvent.DECODED_DATA, (event: string, decodedData: any, binary: Uint8Array) => {\n    console.log(event, decodedData);\n});\n```\n\n### `websocketConnected`\n\nWill be triggered as soon as a WebSocket connection is established. The WebSocket client object is passed.\n\n```ts\nconnection.on(ControlEvent.WEBSOCKET_CONNECTED, (client: TikTokWsClient) => {\n    console.log('WebSocket Client:', websocketClient.connection);\n});\n```\n\n### `error`\n\nGeneral error event. You should handle this.\n\n```ts\nconnection.on(ControlEvent.ERROR, err => {\n    console.error('Error!', err);\n});\n```\n\n## Message Events\n\n### `member`\n\nTriggered every time a new viewer joins the live stream.\n\n```ts\nconnection.on(WebcastEvent.MEMBER, (data: WebcastMemberMessage) => {\n    console.log(`${data.uniqueId} joins the stream!`);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    actionId: 1,\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n    followRole: 0,\n    // 0 = none; 1 = follower; 2 = friends\n    userBadges: [\n        {\n            type: \"pm_mt_moderator_im\",\n            name: \"Moderator\"\n        },\n        {\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/rankl...image\"\n        },\n        {\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/....~...image\"\n        }\n    ],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 2139,\n        followerCount: 853,\n        followStatus: 0,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: false,\n    topGifterRank: null,\n    msgId: \"7137750885996120859\",\n    createTime: \"1661887134195\",\n    displayType: \"live_room_enter_toast\",\n    label: \"{0:user} joined\"\n}\n```\n\n</p></details>\n\n### `chat`\n\nTriggered every time a new chat comment arrives.\n\n```ts\nconnection.on(WebcastEvent.CHAT, (data: WebcastChatMessage) => {\n    console.log(`${data.uniqueId} -> ${data.comment}`);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    comment: \"How are you?\",\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n    followRole: 0,\n    // 0 = none; 1 = follower; 2 = friends\n    userBadges: [\n        {\n            // Moderator badge\n            type: \"pm_mt_moderator_im\",\n            name: \"Moderator\"\n        },\n        {\n            // Top Gifter badge\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/rankl...image\"\n        },\n        {\n            // Subscriber Badge\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/....~...image\"\n        }\n    ],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 10000,\n        followerCount: 606,\n        followStatus: 0,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: false,\n    topGifterRank: null,\n    msgId: \"7137750790064065286\",\n    createTime: \"1661887134718\"\n}\n```\n\n</p></details>\n\n### `gift`\n\nTriggered every time a gift arrives. You will receive additional information via the `extendedGiftInfo` attribute when\nyou enable the [`enableExtendedGiftInfo`](#params-and-options) option.\n\n> **NOTE:** Users have the capability to send gifts in a streak. This increases the `repeatCount` value until the user\n> terminates the streak. During this time new gift events are triggered again and again with an increased `repeatCount`\n> value. It should be noted that after the end of the streak, another gift event is triggered, which signals the end of\n> the streak via `repeatEnd`:`true`. This applies only to gifts with `giftType`:`1`. This means that even if the user\n> sends a `giftType`:`1` gift only once, you will receive the event twice. Once with `repeatEnd`:`false` and once\n> with `repeatEnd`:`true`. Therefore, the event should be handled as follows:\n\n```ts\nconnection.on(WebcastEvent.GIFT, (event: WebcastGiftMessage) => {\n    if (data.giftType === 1 && !data.repeatEnd) {\n        // Streak in progress => show only temporary\n        console.log(`${data.uniqueId} is sending gift ${data.giftName} x${data.repeatCount}`);\n    } else {\n        // Streak ended or non-streakable gift => process the gift with final repeat_count\n        console.log(`${data.uniqueId} has sent gift ${data.giftName} x${data.repeatCount}`);\n    }\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    // Gift Details\n    giftId: 5953,\n    repeatCount: 1,\n    repeatEnd: true,\n    groupId: \"1661887131074\",\n    monitorExtra: {\n        anchor_id: 7087613897129494000,\n        from_idc: \"maliva\",\n        from_user_id: 7044640112358049000,\n        gift_id: 5953,\n        gift_type: 1,\n        log_id: \"20220830191849010192055159174B7670\",\n        msg_id: 7137749190944230000,\n        repeat_count: 1,\n        repeat_end: 1,\n        room_id: 7137728632142843000,\n        send_gift_profit_core_start_ms: 0,\n        send_gift_send_message_success_ms: 1661887134397,\n        to_user_id: 7087613897129494000\n    },\n    // Sender Details\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n    followRole: 0,\n    // 0 = none; 1 = follower; 2 = friends\n    userBadges: [],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...webp\",\n            \"https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 360,\n        followerCount: 740,\n        followStatus: 0,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: false,\n    topGifterRank: null,\n    msgId: \"7137749190944230150\",\n    createTime: \"1661887134397\",\n    displayType: \"webcast_aweme_gift_send_message\",\n    label: \"{0:user} sent {1:gift} {2:string}\",\n    gift: {\n        gift_id: 5953,\n        repeat_count: 1,\n        repeat_end: 1,\n        gift_type: 1\n    },\n    describe: \"Sent Nevalyashka doll\",\n    giftType: 1,\n    diamondCount: 25,\n    giftName: \"Nevalyashka doll\",\n    giftPictureUrl: \"https://p19-webcast.tiktokcdn.com/img/maliva/webca...png\",\n    timestamp: 1661887134397,\n    extendedGiftInfo: {\n        // This will be filled when you enable the `enableExtendedGiftInfo` option\n    },\n    // Receiver Details (can also be a guest broadcaster)\n    receiverUserId: \"7087613897129493510\"\n}\n```\n\n</p></details>\n\n### `roomUser`\n\nTriggered every time a statistic message arrives. This message currently contains the viewer count and a top gifter\nlist.\n\n```ts\nconnection.on(WebcastEvent.ROOM_USER, data => {\n    console.log(`Viewer Count: ${data.viewerCount}`);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    topViewers: [\n        {\n            user: {\n                userId: \"6822565897317303297\",\n                secUid: \"MS4wLjABAAAALIKFhzvmiCws6B6KWfRgWr5MbyGVPXevakvnP8xc7VLkWtcqNeEe9coyRA74KNxm\",\n                uniqueId: \"linmjh\",\n                nickname: \"gì z má ( ^ㆍㅅㆍ^)\",\n                profilePictureUrl: \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...webp\",\n                followRole: 0,\n                userBadges: [],\n                userDetails: {\n                    createTime: \"1588502711\",\n                    bioDescription: \"\",\n                    profilePictureUrls: [\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...webp\",\n                        \"https://p9-sign-sg.tiktokcdn.com/aweme/100x100/tos...webp\",\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...jpeg\"\n                    ]\n                },\n                followInfo: {\n                    followingCount: 781,\n                    followerCount: 51,\n                    followStatus: 0,\n                    pushStatus: 0\n                },\n                isModerator: false,\n                isNewGifter: false,\n                isSubscriber: false,\n                topGifterRank: null\n            },\n            coinCount: 0\n        },\n        {\n            user: {\n                userId: \"6828542044454863874\",\n                secUid: \"MS4wLjABAAAAxP4NgzG7uJz1tcB8o3JN8PxHWej20NJWCHP1IG1PZ0OmQLB6SVORRSoX0Ool4dwj\",\n                uniqueId: \"xuanthainguyen0\",\n                nickname: \"Xuan Thai Nguyen\",\n                profilePictureUrl: \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...webp\",\n                followRole: 0,\n                userBadges: [],\n                userDetails: {\n                    createTime: \"1593865836\",\n                    bioDescription: \"\",\n                    profilePictureUrls: [\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...webp\",\n                        \"https://p9-sign-sg.tiktokcdn.com/aweme/100x100/tik...webp\",\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...jpeg\"\n                    ]\n                },\n                followInfo: {\n                    followingCount: 6,\n                    followerCount: 6,\n                    followStatus: 0,\n                    pushStatus: 0\n                },\n                isModerator: false,\n                isNewGifter: false,\n                isSubscriber: false,\n                topGifterRank: null\n            },\n            coinCount: 0\n        },\n        {\n            user: {\n                userId: \"7014684709204624385\",\n                secUid: \"MS4wLjABAAAAnVMJ9MXN5HqjnpyEwgEhjv97Pc_ixtG4Iwnnagbrd99WhEATfhZLW6McX-uErTp9\",\n                uniqueId: \"dyip0c3sbo2t\",\n                nickname: \"Huu Trân572\",\n                profilePictureUrl: \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...webp\",\n                followRole: 0,\n                userBadges: [],\n                userDetails: {\n                    createTime: \"1640318249\",\n                    bioDescription: \"\",\n                    profilePictureUrls: [\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...webp\",\n                        \"https://p9-sign-sg.tiktokcdn.com/aweme/100x100/tik...webp\",\n                        \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/ti...jpeg\"\n                    ]\n                },\n                followInfo: {\n                    followingCount: 35,\n                    followerCount: 21,\n                    followStatus: 0,\n                    pushStatus: 0\n                },\n                isModerator: false,\n                isNewGifter: false,\n                isSubscriber: false,\n                topGifterRank: null\n            },\n            coinCount: 0\n        },\n        {\n            user: {\n                userId: \"7133413217468187675\",\n                secUid: \"MS4wLjABAAAA2u64n6KnroBOMQo4pR9bLv0twyCIy0X-wd7S__WR4d2VObktWAfs_ck08pjD4hIV\",\n                uniqueId: \"uservay64gw9d5\",\n                nickname: \"uservay64gw9d5\",\n                profilePictureUrl: \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n                followRole: 0,\n                userBadges: [],\n                userDetails: {\n                    createTime: \"1660877330\",\n                    bioDescription: \"\",\n                    profilePictureUrls: [\n                        \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n                        \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...jpeg\"\n                    ]\n                },\n                followInfo: {\n                    followingCount: 2,\n                    followerCount: 0,\n                    followStatus: 0,\n                    pushStatus: 0\n                },\n                isModerator: false,\n                isNewGifter: false,\n                isSubscriber: false,\n                topGifterRank: null\n            },\n            coinCount: 0\n        },\n        {\n            user: {\n                userId: \"6800374961430791170\",\n                secUid: \"MS4wLjABAAAAF3tD_kSi9qas_10I5I5YUIBfXKd0KlKvKTKACzfXS1Wwp04e03xJCTswwzCMRgEu\",\n                uniqueId: \"hungtran0293\",\n                nickname: \"Trần Hùng\",\n                profilePictureUrl: \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n                followRole: 0,\n                userBadges: [],\n                userDetails: {\n                    createTime: \"1585370455\",\n                    bioDescription: \"\",\n                    profilePictureUrls: [\n                        \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...webp\",\n                        \"https://p16-sign-va.tiktokcdn.com/tos-useast2a-avt...jpeg\"\n                    ]\n                },\n                followInfo: {\n                    followingCount: 1735,\n                    followerCount: 313,\n                    followStatus: 0,\n                    pushStatus: 0\n                },\n                isModerator: false,\n                isNewGifter: false,\n                isSubscriber: false,\n                topGifterRank: null\n            },\n            coinCount: 0\n        }\n    ],\n    viewerCount: 630\n}\n```\n\n</p></details>\n\n### `like`\n\nTriggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered\nby TikTok.\n\n```ts\nconnection.on(WebcastEvent.LIKE, data => {\n    console.log(`${data.uniqueId} sent ${data.likeCount} likes, total likes: ${data.totalLikeCount}`);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    likeCount: 6,\n    // likes given by the user (taps on screen)\n    totalLikeCount: 21349,\n    // likes that this stream has received in total (from all users)\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n    followRole: 0,\n    // 0 = none; 1 = follower; 2 = friends,\n    userBadges: [\n        {\n            type: \"pm_mt_moderator_im\",\n            name: \"Moderator\"\n        },\n        {\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/rankl...image\"\n        },\n        {\n            type: \"image\",\n            displayType: 1,\n            url: \"https://p19-webcast.tiktokcdn.com/webcast-va/....~...image\"\n        }\n    ],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p19-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 617,\n        followerCount: 112,\n        followStatus: 1,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: false,\n    topGifterRank: null,\n    msgId: \"7137750883651619630\",\n    createTime: \"1661887134554\",\n    displayType: \"pm_mt_msg_viewer\",\n    label: \"{0:user} liked the LIVE\"\n}\n```\n\n</p></details>\n\n### `social`\n\nTriggered every time someone shares the stream or follows the host.\n\n```ts\nconnection.on(WebcastEvent.SOCIAL, data => {\n    console.log('social event data:', data);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n    followRole: 1,\n    userBadges: [],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p19-sign.tiktokcdn-us.com/tos-useast5-avt-...webp\",\n            \"https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 277,\n        followerCount: 96,\n        followStatus: 1,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: false,\n    topGifterRank: null,\n    msgId: \"7137750889884076842\",\n    createTime: \"1661887134629\",\n    displayType: \"pm_main_follow_message_viewer_2\",\n    // or pm_mt_guidance_share\n    label: \"{0:user} followed the host\"\n}\n```\n\n</p></details>\n\n### `emote`\n\nTriggered every time a subscriber sends an emote (sticker).\n\n```ts\nconnection.on(WebcastEvent.EMOTE, (data: WebcastEmoteChatMessage) => {\n    console.log('emote received', data);\n});\n```\n\n<details><summary>⚡ See Data Structure</summary><p>\n\n```json5\n{\n    userId: \"6813181309701719620\",\n    secUid: \"<redacted>\",\n    uniqueId: \"zerodytester\",\n    nickname: \"Zerody Tester\",\n    profilePictureUrl: \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...webp\",\n    followRole: 0,\n    // 0 = none; 1 = follower; 2 = friends,\n    userBadges: [],\n    userDetails: {\n        createTime: \"0\",\n        bioDescription: \"\",\n        profilePictureUrls: [\n            \"https://p16-sign-sg.tiktokcdn.com/tos-alisg-avt-00...webp\",\n            \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...webp\",\n            \"https://p16-sign-sg.tiktokcdn.com/aweme/100x100/to...jpeg\"\n        ]\n    },\n    followInfo: {\n        followingCount: 14,\n        followerCount: 6,\n        followStatus: 1,\n        pushStatus: 0\n    },\n    isModerator: false,\n    isNewGifter: false,\n    isSubscriber: true,\n    topGifterRank: null,\n    emoteId: \"7121025198379731714\",\n    emoteImageUrl: \"https://p19-webcast.tiktokcdn.com/webcast-sg/61964...image\"\n}\n```\n","readmeFilename":"README.md","_rev":"1-70fc79ecd24487632f886197faa28084"}