diff --git a/examples/livechat/README.md b/examples/livechat/README.md index 7b8ab796..213a6ab6 100644 --- a/examples/livechat/README.md +++ b/examples/livechat/README.md @@ -1,16 +1,16 @@ ## Live Chat -The library's Live Chat parser and poller were heavily based on YouTube's original compiled code, this makes it behave in a similar if not identical way to YouTube's Live Chat. Here you can do all sorts of funny things, ex; track messages, donations, polls, and much more. +Represents a livestream chat. ## Usage -Before fetching a Live Chat, you have to retrieve the target livestream's info: +Before fetching a live chat, you have to retrieve the target livestream's info: ```js const info = await yt.getInfo('video_id'); ``` -Then you may request a Live Chat instance: +Then you may request a live chat instance: ```js const livechat = await info.getLiveChat(); ``` @@ -21,6 +21,7 @@ const livechat = await info.getLiveChat(); * [.ev](#ev) ⇒ `EventEmitter` * [.start](#start) ⇒ `function` * [.stop](#stop) ⇒ `function` + * [.applyFilter](#applyfilter) ⇒ `function` * [.getItemMenu](#getitemmenu) ⇒ `function` * [.sendMessage](#sendmessage) ⇒ `function` @@ -31,6 +32,8 @@ Live Chat's EventEmitter. **Events:** - `start` + + Fired when the live chat is started. Arguments: | Type | Description | @@ -38,18 +41,35 @@ Live Chat's EventEmitter. | `LiveChatContinuation` | Initial chat data, actions, info, etc. | - `chat-update` - + + Fired when a new chat action is received. + Arguments: | Type | Description | | --- | --- | - | `ChatAction` | Chat Action | + | `ChatAction` | Chat action | - `metadata-update` + + Fired when the livestream's metadata is updated. Arguments: | Type | Description | | --- | --- | - | `LiveMetadata` | LiveStream Metadata | + | `LiveMetadata` | Livestream metadata | + +- `error` + + Fired when an error occurs. + + Arguments: + | Type | Description | + | --- | --- | + | `Error` | Details about the error | + +- `end` + + Fired when the livestream ends. ### start() @@ -59,6 +79,15 @@ Starts the Live Chat. ### stop() Stops the Live Chat. + +### applyFilter(filter) + +Applies given filter to the live chat. + +| Param | Type | Description | +| --- | --- | --- | +| filter | `string` | Can be `TOP_CHAT` or `LIVE_CHAT` | + ### getItemMenu(item) Retrieves given chat item's menu. diff --git a/examples/livechat/index.ts b/examples/livechat/index.ts index b6fa96c3..bbe5a325 100644 --- a/examples/livechat/index.ts +++ b/examples/livechat/index.ts @@ -1,25 +1,38 @@ import { Innertube, UniversalCache, YTNodes } from 'youtubei.js'; - import { LiveChatContinuation } from 'youtubei.js/dist/src/parser'; import { ChatAction, LiveMetadata } from 'youtubei.js/dist/src/parser/youtube/LiveChat'; (async () => { - const yt = await Innertube.create({ cache: new UniversalCache() }); + const yt = await Innertube.create({ cache: new UniversalCache(), generate_session_locally: true }); - - const search = await yt.search('Lofi girl live'); + const search = await yt.search('lofi hip hop radio - beats to relax/study to'); const info = await yt.getInfo(search.videos[0].as(YTNodes.Video).id); const livechat = info.getLiveChat(); livechat.on('start', (initial_data: LiveChatContinuation) => { /** - * Initial info is what you see when you first open a Live Chat — this is; inital actions (pinned messages, top donations..), account's info and so on. + * Initial info is what you see when you first open a a live chat — this is; initial actions (pinned messages, top donations..), account's info and so forth. */ + console.info(`Hey ${initial_data.viewer_name || 'Guest'}, welcome to Live Chat!`); - console.info(`Hey ${initial_data.viewer_name || 'N/A'}, welcome to Live Chat!`); + const pinned_action = initial_data.actions.firstOfType(YTNodes.AddBannerToLiveChatCommand); + + if (pinned_action) { + if (pinned_action.banner?.contents?.is(YTNodes.LiveChatTextMessage)) { + console.info( + '\n', 'Pinned message:\n', + pinned_action.banner.contents.author?.name.toString(), '-', pinned_action?.banner.contents.message.toString(), + '\n' + ); + } + } }); + livechat.on('error', (error: Error) => console.info('Live chat error:', error)); + + livechat.on('end', () => console.info('This live stream has ended.')); + livechat.on('chat-update', (action: ChatAction) => { /** * An action represents what is being added to @@ -43,24 +56,42 @@ import { ChatAction, LiveMetadata } from 'youtubei.js/dist/src/parser/youtube/Li switch (item.type) { case 'LiveChatTextMessage': console.info( + `${item.as(YTNodes.LiveChatTextMessage).author?.is_moderator ? '[MOD]' : ''}`, `${hours} - ${item.as(YTNodes.LiveChatTextMessage).author?.name.toString()}:\n` + `${item.as(YTNodes.LiveChatTextMessage).message.toString()}\n` ); break; case 'LiveChatPaidMessage': console.info( + `${item.as(YTNodes.LiveChatPaidMessage).author?.is_moderator ? '[MOD]' : ''}`, `${hours} - ${item.as(YTNodes.LiveChatPaidMessage).author.name.toString()}:\n` + + `${item.as(YTNodes.LiveChatPaidMessage).message.toString()}\n`, `${item.as(YTNodes.LiveChatPaidMessage).purchase_amount}\n` ); break; + case 'LiveChatPaidSticker': + console.info( + `${item.as(YTNodes.LiveChatPaidSticker).author?.is_moderator ? '[MOD]' : ''}`, + `${hours} - ${item.as(YTNodes.LiveChatPaidSticker).author.name.toString()}:\n` + + `${item.as(YTNodes.LiveChatPaidSticker).purchase_amount}\n` + ); + break; default: console.debug(action); break; } } - if (action.is(YTNodes.MarkChatItemAsDeletedAction)) { - console.warn(`Message ${action.target_item_id} just got deleted and should be replaced with ${action.deleted_state_message.toString()}!`, '\n'); + if (action.is(YTNodes.AddBannerToLiveChatCommand)) { + console.info('Message pinned:', action.banner?.contents); + } + + if (action.is(YTNodes.RemoveBannerForLiveChatCommand)) { + console.info(`Message with action id ${action.target_action_id} was unpinned.`); + } + + if (action.is(YTNodes.RemoveChatItemAction)) { + console.warn(`Message with action id ${action.target_item_id} just got deleted!`, '\n'); } }); diff --git a/src/parser/classes/LiveChatHeader.ts b/src/parser/classes/LiveChatHeader.ts index d1cd1613..06547ef9 100644 --- a/src/parser/classes/LiveChatHeader.ts +++ b/src/parser/classes/LiveChatHeader.ts @@ -1,18 +1,21 @@ import Parser from '../index'; +import type Menu from './menus/Menu'; +import type Button from './Button'; +import type SortFilterSubMenu from './SortFilterSubMenu'; import { YTNode } from '../helpers'; class LiveChatHeader extends YTNode { static type = 'LiveChatHeader'; - overflow_menu; - collapse_button; - view_selector; + overflow_menu: Menu | null; + collapse_button: Button | null; + view_selector: SortFilterSubMenu | null; constructor(data: any) { super(); - this.overflow_menu = Parser.parse(data.overflowMenu); - this.collapse_button = Parser.parse(data.collapseButton); - this.view_selector = Parser.parse(data.viewSelector); + this.overflow_menu = Parser.parseItem(data.overflowMenu); + this.collapse_button = Parser.parseItem