11import { fixLeaks } from '../ts/ytc-fix-memleaks' ;
22import { frameIsReplay as isReplay , checkInjected } from '../ts/chat-utils' ;
33import sha1 from 'sha-1' ;
4- import { chatReportUserOptions , ChatUserActions , isLiveTL } from '../ts/chat-constants' ;
4+ import { chatReportUserOptions , ChatUserActions , ChatPollActions , isLiveTL } from '../ts/chat-constants' ;
55
66function injectedFunction ( ) : void {
77 const currentDomain = ( location . protocol + '//' + location . host ) ;
@@ -84,9 +84,69 @@ const chatLoaded = async (): Promise<void> => {
8484 } ) ;
8585 } ;
8686
87- // eslint-disable-next-line @typescript-eslint/no-misused-promises
88- port . onMessage . addListener ( async ( msg ) => {
89- if ( msg . type !== 'executeChatAction' ) return ;
87+ function getCookie ( name : string ) : string {
88+ const value = `; ${ document . cookie } ` ;
89+ const parts = value . split ( `; ${ name } =` ) ;
90+ if ( parts . length === 2 ) return ( parts . pop ( ) ?? '' ) . split ( ';' ) . shift ( ) ?? '' ;
91+ return '' ;
92+ }
93+
94+ function parseServiceEndpoint ( baseContext : any , serviceEndpoint : any , prop : string ) : { params : string , context : any } {
95+ const { clickTrackingParams, [ prop ] : { params } } = serviceEndpoint ;
96+ const clonedContext = JSON . parse ( JSON . stringify ( baseContext ) ) ;
97+ clonedContext . clickTracking = {
98+ clickTrackingParams
99+ } ;
100+ return {
101+ params,
102+ context : clonedContext
103+ } ;
104+ }
105+
106+ /**
107+ * Executes a poll action (e.g., ending a poll)
108+ */
109+ async function handlePollAction ( msg : any , fetcher : ( ...args : any [ ] ) => Promise < any > ) : Promise < void > {
110+ try {
111+ const currentDomain = ( location . protocol + '//' + location . host ) ;
112+ const apiKey = ytcfg . data_ . INNERTUBE_API_KEY ;
113+ const baseContext = ytcfg . data_ . INNERTUBE_CONTEXT ;
114+ const time = Math . floor ( Date . now ( ) / 1000 ) ;
115+ const SAPISID = getCookie ( '__Secure-3PAPISID' ) ;
116+ const sha = sha1 ( `${ time } ${ SAPISID } ${ currentDomain } ` ) ;
117+ const auth = `SAPISIDHASH ${ time } _${ sha } ` ;
118+ const heads = {
119+ headers : {
120+ 'Content-Type' : 'application/json' ,
121+ Accept : '*/*' ,
122+ Authorization : auth
123+ } ,
124+ method : 'POST'
125+ } ;
126+
127+ if ( msg . action === ChatPollActions . END_POLL ) {
128+ const poll = msg . poll ;
129+ const params = poll . item . action ?. params || '' ;
130+ const url = poll . item . action ?. url || '/youtubei/v1/live_chat/live_chat_action' ;
131+
132+ // Call YouTube API to end the poll
133+ await fetcher ( `${ currentDomain } ${ url } ?key=${ apiKey } &prettyPrint=false` , {
134+ ...heads ,
135+ body : JSON . stringify ( {
136+ params,
137+ context : baseContext
138+ } )
139+ } ) ;
140+ }
141+ } catch ( e ) {
142+ console . debug ( 'Error executing poll action' , e ) ;
143+ }
144+ }
145+
146+ /**
147+ * Executes a chat action (e.g., blocking or reporting a user)
148+ */
149+ async function handleChatAction ( msg : any , fetcher : ( ...args : any [ ] ) => Promise < any > ) : Promise < void > {
90150 const message = msg . message ;
91151 if ( message . params == null ) return ;
92152 let success = true ;
@@ -97,12 +157,6 @@ const chatLoaded = async (): Promise<void> => {
97157 const contextMenuUrl = `${ currentDomain } /youtubei/v1/live_chat/get_item_context_menu?params=` +
98158 `${ encodeURIComponent ( message . params ) } &pbj=1&key=${ apiKey } &prettyPrint=false` ;
99159 const baseContext = ytcfg . data_ . INNERTUBE_CONTEXT ;
100- function getCookie ( name : string ) : string {
101- const value = `; ${ document . cookie } ` ;
102- const parts = value . split ( `; ${ name } =` ) ;
103- if ( parts . length === 2 ) return ( parts . pop ( ) ?? '' ) . split ( ';' ) . shift ( ) ?? '' ;
104- return '' ;
105- }
106160 const time = Math . floor ( Date . now ( ) / 1000 ) ;
107161 const SAPISID = getCookie ( '__Secure-3PAPISID' ) ;
108162 const sha = sha1 ( `${ time } ${ SAPISID } ${ currentDomain } ` ) ;
@@ -119,19 +173,8 @@ const chatLoaded = async (): Promise<void> => {
119173 ...heads ,
120174 body : JSON . stringify ( { context : baseContext } )
121175 } ) ;
122- function parseServiceEndpoint ( serviceEndpoint : any , prop : string ) : { params : string , context : any } {
123- const { clickTrackingParams, [ prop ] : { params } } = serviceEndpoint ;
124- const clonedContext = JSON . parse ( JSON . stringify ( baseContext ) ) ;
125- clonedContext . clickTracking = {
126- clickTrackingParams
127- } ;
128- return {
129- params,
130- context : clonedContext
131- } ;
132- }
133176 if ( msg . action === ChatUserActions . BLOCK ) {
134- const { params, context } = parseServiceEndpoint (
177+ const { params, context } = parseServiceEndpoint ( baseContext ,
135178 res . liveChatItemContextMenuSupportedRenderers . menuRenderer . items [ 1 ]
136179 . menuNavigationItemRenderer . navigationEndpoint . confirmDialogEndpoint
137180 . content . confirmDialogRenderer . confirmButton . buttonRenderer . serviceEndpoint ,
@@ -145,7 +188,7 @@ const chatLoaded = async (): Promise<void> => {
145188 } )
146189 } ) ;
147190 } else if ( msg . action === ChatUserActions . REPORT_USER ) {
148- const { params, context } = parseServiceEndpoint (
191+ const { params, context } = parseServiceEndpoint ( baseContext ,
149192 res . liveChatItemContextMenuSupportedRenderers . menuRenderer . items [ 0 ] . menuServiceItemRenderer . serviceEndpoint ,
150193 'getReportFormEndpoint'
151194 ) ;
@@ -182,6 +225,17 @@ const chatLoaded = async (): Promise<void> => {
182225 message,
183226 success
184227 } ) ;
228+ }
229+
230+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
231+ port . onMessage . addListener ( async ( msg : any ) => {
232+ if ( msg . type === 'executePollAction' ) {
233+ return handlePollAction ( msg , fetcher ) ;
234+ }
235+ if ( msg . type === 'executeChatAction' ) {
236+ return handleChatAction ( msg , fetcher ) ;
237+ }
238+ return ;
185239 } ) ;
186240 } ) ;
187241
0 commit comments