Skip to content

Commit

Permalink
man this is way too hard lmao aint no way
Browse files Browse the repository at this point in the history
  • Loading branch information
KentoNishi committed Jul 14, 2022
1 parent 0ccb9f3 commit 580072b
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 96 deletions.
14 changes: 11 additions & 3 deletions src/components/Message.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
showUserBadges,
hoveredItem,
port,
selfChannelId
selfChannelId,
hasMembershipGiftingEnabled
} from '../ts/storage';
import { chatUserActionsItems, Theme } from '../ts/chat-constants';
import { useBanHammer } from '../ts/chat-actions';
import { checkMembershipGifting, useBanHammer } from '../ts/chat-actions';
import { mdiGift } from '@mdi/js';
export let message: Ytc.ParsedMessage;
Expand Down Expand Up @@ -69,6 +70,13 @@
value: d.value.toString(),
onClick: () => useBanHammer(message, d.value, $port)
}));
const triggerGiftCheck = () => {
if ($hasMembershipGiftingEnabled === null) {
checkMembershipGifting($port);
}
return true;
};
</script>

<!-- svelte-ignore a11y-mouse-events-have-key-events -->
Expand Down Expand Up @@ -137,7 +145,7 @@
{forceTLColor}
class={message.membershipGiftRedeem ? 'text-gray-700 dark:text-gray-600 italic font-medium' : ''}
/>
{#if message.membershipGiftRedeem}
{#if message.membershipGiftRedeem && triggerGiftCheck()}
<svg
height="1em"
width="1em"
Expand Down
194 changes: 103 additions & 91 deletions src/scripts/chat-interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ function injectedFunction(): void {
}

const chatLoaded = async (): Promise<void> => {
function getCookie(name: string): string {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return (parts.pop() ?? '').split(';').shift() ?? '';
return '';
}

const warning = 'HC button detected, not injecting interceptor.';
if (!isLiveTL && checkInjected(warning)) return;

Expand Down Expand Up @@ -85,101 +92,106 @@ const chatLoaded = async (): Promise<void> => {

// eslint-disable-next-line @typescript-eslint/no-misused-promises
port.onMessage.addListener(async (msg) => {
if (msg.type !== 'executeChatAction') return;
const message = msg.message;
if (message.params == null) return;
let success = true;
try {
// const action = msg.action;
const apiKey = ytcfg.data_.INNERTUBE_API_KEY;
const contextMenuUrl = 'https://www.youtube.com/youtubei/v1/live_chat/get_item_context_menu?params=' +
`${encodeURIComponent(message.params)}&pbj=1&key=${apiKey}&prettyPrint=false`;
const baseContext = ytcfg.data_.INNERTUBE_CONTEXT;
function getCookie(name: string): string {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return (parts.pop() ?? '').split(';').shift() ?? '';
return '';
}
const time = Math.floor(Date.now() / 1000);
const SAPISID = getCookie('__Secure-3PAPISID');
const sha = sha1(`${time} ${SAPISID} https://www.youtube.com`);
const auth = `SAPISIDHASH ${time}_${sha}`;
const heads = {
headers: {
'Content-Type': 'application/json',
Accept: '*/*',
Authorization: auth
},
method: 'POST'
};
const res = await fetcher(contextMenuUrl, {
...heads,
body: JSON.stringify({ context: baseContext })
});
function parseServiceEndpoint(serviceEndpoint: any, prop: string): { params: string, context: any } {
const { clickTrackingParams, [prop]: { params } } = serviceEndpoint;
const clonedContext = JSON.parse(JSON.stringify(baseContext));
clonedContext.clickTracking = {
clickTrackingParams
};
return {
params,
context: clonedContext
};
}
if (msg.action === ChatUserActions.BLOCK) {
const { params, context } = parseServiceEndpoint(
res.liveChatItemContextMenuSupportedRenderers.menuRenderer.items[1]
.menuNavigationItemRenderer.navigationEndpoint.confirmDialogEndpoint
.content.confirmDialogRenderer.confirmButton.buttonRenderer.serviceEndpoint,
'moderateLiveChatEndpoint'
);
await fetcher(`https://www.youtube.com/youtubei/v1/live_chat/moderate?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
params,
context
})
switch (msg.type) {
case 'executeChatAction': {
const message = msg.message;
if (message.params == null) return;
let success = true;
try {
// const action = msg.action;
const apiKey = ytcfg.data_.INNERTUBE_API_KEY;
const contextMenuUrl = 'https://www.youtube.com/youtubei/v1/live_chat/get_item_context_menu?params=' +
`${encodeURIComponent(message.params)}&pbj=1&key=${apiKey}&prettyPrint=false`;
const baseContext = ytcfg.data_.INNERTUBE_CONTEXT;
const time = Math.floor(Date.now() / 1000);
const SAPISID = getCookie('__Secure-3PAPISID');
const sha = sha1(`${time} ${SAPISID} https://www.youtube.com`);
const auth = `SAPISIDHASH ${time}_${sha}`;
const heads = {
headers: {
'Content-Type': 'application/json',
Accept: '*/*',
Authorization: auth
},
method: 'POST'
};
const res = await fetcher(contextMenuUrl, {
...heads,
body: JSON.stringify({ context: baseContext })
});
function parseServiceEndpoint(serviceEndpoint: any, prop: string): { params: string, context: any } {
const { clickTrackingParams, [prop]: { params } } = serviceEndpoint;
const clonedContext = JSON.parse(JSON.stringify(baseContext));
clonedContext.clickTracking = {
clickTrackingParams
};
return {
params,
context: clonedContext
};
}
if (msg.action === ChatUserActions.BLOCK) {
const { params, context } = parseServiceEndpoint(
res.liveChatItemContextMenuSupportedRenderers.menuRenderer.items[1]
.menuNavigationItemRenderer.navigationEndpoint.confirmDialogEndpoint
.content.confirmDialogRenderer.confirmButton.buttonRenderer.serviceEndpoint,
'moderateLiveChatEndpoint'
);
await fetcher(`https://www.youtube.com/youtubei/v1/live_chat/moderate?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
params,
context
})
});
} else if (msg.action === ChatUserActions.REPORT_USER) {
const { params, context } = parseServiceEndpoint(
res.liveChatItemContextMenuSupportedRenderers.menuRenderer.items[0].menuServiceItemRenderer.serviceEndpoint,
'getReportFormEndpoint'
);
const modal = await fetcher(`https://www.youtube.com/youtubei/v1/flag/get_form?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
params,
context
})
});
const index = chatReportUserOptions.findIndex(d => d.value === msg.reportOption);
const options = modal.actions[0].openPopupAction.popup.reportFormModalRenderer.optionsSupportedRenderers.optionsRenderer.items;
const submitEndpoint = options[index].optionSelectableItemRenderer.submitEndpoint;
const clickTrackingParams = submitEndpoint.clickTrackingParams;
const flagAction = submitEndpoint.flagEndpoint.flagAction;
context.clickTracking = {
clickTrackingParams
};
await fetcher(`https://www.youtube.com/youtubei/v1/flag/flag?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
action: flagAction,
context
})
});
}
} catch (e) {
console.debug('Error executing chat action', e);
success = false;
}
port.postMessage({
type: 'chatUserActionResponse',
action: msg.action,
message,
success
});
} else if (msg.action === ChatUserActions.REPORT_USER) {
const { params, context } = parseServiceEndpoint(
res.liveChatItemContextMenuSupportedRenderers.menuRenderer.items[0].menuServiceItemRenderer.serviceEndpoint,
'getReportFormEndpoint'
);
const modal = await fetcher(`https://www.youtube.com/youtubei/v1/flag/get_form?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
params,
context
})
});
const index = chatReportUserOptions.findIndex(d => d.value === msg.reportOption);
const options = modal.actions[0].openPopupAction.popup.reportFormModalRenderer.optionsSupportedRenderers.optionsRenderer.items;
const submitEndpoint = options[index].optionSelectableItemRenderer.submitEndpoint;
const clickTrackingParams = submitEndpoint.clickTrackingParams;
const flagAction = submitEndpoint.flagEndpoint.flagAction;
context.clickTracking = {
clickTrackingParams
};
await fetcher(`https://www.youtube.com/youtubei/v1/flag/flag?key=${apiKey}&prettyPrint=false`, {
...heads,
body: JSON.stringify({
action: flagAction,
context
})
break;
}
case 'checkMembershipGifting': {
port.postMessage({
type: 'checkMembershipGiftingResponse',
result: false
});
break;
}
} catch (e) {
console.debug('Error executing chat action', e);
success = false;
}
port.postMessage({
type: 'chatUserActionResponse',
action: msg.action,
message,
success
});
});
});

Expand Down
8 changes: 8 additions & 0 deletions src/ts/chat-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ export function useBanHammer(
});
}
}

export function checkMembershipGifting(
port: Chat.Port | null
): void {
port?.postMessage({
type: 'checkMembershipGifting'
});
}
1 change: 1 addition & 0 deletions src/ts/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ export const currentProgress = writable(null as null | number);
export const enableStickySuperchatBar = stores.addSyncStore('hc.enableStickySuperchatBar', true);
export const enableHighlightedMentions = stores.addSyncStore('hc.enableHighlightedMentions', true);
export const lastOpenedVersion = stores.addSyncStore('hc.lastOpenedVersion', '');
export const hasMembershipGiftingEnabled = writable(null as null | boolean);
17 changes: 15 additions & 2 deletions src/ts/typings/chat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,13 @@ declare namespace Chat {
success: boolean;
}

type InterceptorActions =
executeChatActionMsg | chatUserActionResponse |
checkMembershipGiftingMsg | checkMembershipGiftingResponse;

type BackgroundResponse =
Actions | InitialData | ThemeUpdate | LtlMessageResponse |
registerClientResponse | executeChatActionMsg | chatUserActionResponse;
registerClientResponse | InterceptorActions;

type InterceptorSource = 'ytc' | 'ltlMessage';

Expand Down Expand Up @@ -139,10 +143,19 @@ declare namespace Chat {
reportOption?: ChatReportUserOptions;
}

interface checkMembershipGiftingMsg {
type: 'checkMembershipGifting';
}

interface checkMembershipGiftingResponse {
type: 'checkMembershipGiftingResponse';
result: boolean;
}

type BackgroundMessage =
RegisterInterceptorMsg | RegisterClientMsg | processJsonMsg |
setInitialDataMsg | updatePlayerProgressMsg | setThemeMsg | getThemeMsg |
RegisterYtcInterceptorMsg | sendLtlMessageMsg | executeChatActionMsg | chatUserActionResponse;
RegisterYtcInterceptorMsg | sendLtlMessageMsg | InterceptorActions;

type Port = Omit<chrome.runtime.Port, 'postMessage' | 'onMessage'> & {
postMessage: (message: BackgroundMessage | BackgroundResponse) => void;
Expand Down

0 comments on commit 580072b

Please sign in to comment.