mirror of
https://github.com/younesaassila/ttv-lol-pro.git
synced 2025-04-29 14:04:26 +02:00
Improve implementation of auto whitelist feature
This commit is contained in:
parent
be39a5344f
commit
ac50e0c8b2
@ -1,7 +1,7 @@
|
||||
export const passportHostRegex = /^passport\.twitch\.tv$/i;
|
||||
export const twitchApiChannelNameRegex = /\/hls\/(.+)\.m3u8/i;
|
||||
export const twitchChannelNameRegex =
|
||||
/^https?:\/\/(?:www|m)\.twitch\.tv\/(?:videos\/|popout\/|moderator\/)?((?!(?:directory|jobs|p|privacy|store|turbo)\b)\w+)/i;
|
||||
/^https?:\/\/(?:www|m)\.twitch\.tv\/(?:videos\/|popout\/|moderator\/)?((?!(?:directory|downloads|jobs|p|privacy|search|settings|store|turbo)\b)\w+)/i;
|
||||
export const twitchGqlHostRegex = /^gql\.twitch\.tv$/i;
|
||||
export const twitchTvHostRegex = /^(?:www|m)\.twitch\.tv$/i;
|
||||
export const usherHostRegex = /^usher\.ttvnw\.net$/i;
|
||||
|
@ -1,11 +0,0 @@
|
||||
import store from "../../store";
|
||||
|
||||
export default function wasChannelSubscriber(
|
||||
channelName: string | null
|
||||
): boolean {
|
||||
if (!channelName) return false;
|
||||
const channelNameLower = channelName.toLowerCase();
|
||||
return store.state.activeChannelSubscriptions.some(
|
||||
c => c.toLowerCase() === channelNameLower
|
||||
);
|
||||
}
|
@ -158,6 +158,7 @@ function onPageMessage(event: MessageEvent) {
|
||||
console.log(`[TTV LOL PRO] Adding '${channelName}' to whitelist.`);
|
||||
store.state.whitelistedChannels.push(channelName);
|
||||
}
|
||||
location.reload();
|
||||
} else if (wasSubscribed && !isSubscribed) {
|
||||
store.state.activeChannelSubscriptions =
|
||||
store.state.activeChannelSubscriptions.filter(
|
||||
@ -173,17 +174,9 @@ function onPageMessage(event: MessageEvent) {
|
||||
c => c.toLowerCase() !== channelName.toLowerCase()
|
||||
);
|
||||
}
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
window.postMessage({
|
||||
type: responseType,
|
||||
message: {
|
||||
type: responseMessageType,
|
||||
whitelistedChannels: JSON.parse(
|
||||
JSON.stringify(store.state.whitelistedChannels)
|
||||
),
|
||||
},
|
||||
});
|
||||
}
|
||||
// ---
|
||||
else if (message.type === MessageType.UsherResponse) {
|
||||
|
@ -49,24 +49,6 @@ export function getFetch(pageState: PageState): typeof fetch {
|
||||
newPlaybackAccessToken,
|
||||
});
|
||||
break;
|
||||
case MessageType.ChannelSubStatusQuery:
|
||||
try {
|
||||
const req = getSubStatusRequest(message.channelName);
|
||||
const res = await NATIVE_FETCH(req);
|
||||
const body = await res.json();
|
||||
const isSubscribed =
|
||||
body.data.user.self.subscriptionBenefit != null;
|
||||
pageState.sendMessageToWorkerScripts(pageState.twitchWorkers, {
|
||||
type: MessageType.ChannelSubStatusQueryResponse,
|
||||
isSubscribed,
|
||||
});
|
||||
} catch (error) {
|
||||
pageState.sendMessageToWorkerScripts(pageState.twitchWorkers, {
|
||||
type: MessageType.ChannelSubStatusQueryResponse,
|
||||
error: `${error}`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -282,14 +264,6 @@ export function getFetch(pageState: PageState): typeof fetch {
|
||||
encodeURIComponent('"player_type":"frontpage"')
|
||||
);
|
||||
const channelName = findChannelFromUsherUrl(url);
|
||||
if (
|
||||
pageState.state?.whitelistChannelSubscriptions &&
|
||||
isLivestream &&
|
||||
channelName != null
|
||||
) {
|
||||
// TODO: Maybe also check before PlaybackAccessToken requests? (But then that's a LOT of overhead.)
|
||||
await checkChannelSubStatus(channelName, pageState);
|
||||
}
|
||||
const isWhitelisted = isChannelWhitelisted(channelName, pageState);
|
||||
if (!isLivestream || isFrontpage || isWhitelisted) {
|
||||
console.log(
|
||||
@ -425,6 +399,65 @@ export function getFetch(pageState: PageState): typeof fetch {
|
||||
|
||||
//#region Responses
|
||||
|
||||
graphqlRes: if (
|
||||
host != null &&
|
||||
twitchGqlHostRegex.test(host) &&
|
||||
response.status < 400
|
||||
) {
|
||||
responseBody ??= await readResponseBody();
|
||||
// Preliminary check to avoid parsing the response body if possible.
|
||||
if (
|
||||
!responseBody.includes('"UserSelfConnection"') ||
|
||||
!responseBody.includes('"subscriptionBenefit"') ||
|
||||
!responseBody.includes('"login"')
|
||||
) {
|
||||
break graphqlRes;
|
||||
}
|
||||
try {
|
||||
let channelName: string;
|
||||
let isSubscribed: boolean;
|
||||
const body = JSON.parse(responseBody);
|
||||
if (Array.isArray(body)) {
|
||||
const match = body.find(
|
||||
(obj: any) =>
|
||||
obj.data &&
|
||||
obj.data.user &&
|
||||
obj.data.user.login != null &&
|
||||
obj.data.user.self &&
|
||||
"subscriptionBenefit" in obj.data.user.self
|
||||
);
|
||||
if (match == null) break graphqlRes;
|
||||
channelName = match.data.user.login;
|
||||
isSubscribed = match.data.user.self.subscriptionBenefit != null;
|
||||
} else {
|
||||
const isMatch =
|
||||
body.data &&
|
||||
body.data.user &&
|
||||
body.data.user.login != null &&
|
||||
body.data.user.self &&
|
||||
"subscriptionBenefit" in body.data.user.self;
|
||||
if (!isMatch) break graphqlRes;
|
||||
channelName = body.data.user.login;
|
||||
isSubscribed = body.data.user.self.subscriptionBenefit != null;
|
||||
}
|
||||
const isLivestream = !/^\d+$/.test(channelName); // VODs have numeric IDs.
|
||||
if (!isLivestream) break graphqlRes;
|
||||
const wasSubscribed = wasChannelSubscriber(channelName, pageState);
|
||||
const hasSubStatusChanged =
|
||||
(wasSubscribed && !isSubscribed) || (!wasSubscribed && isSubscribed);
|
||||
if (hasSubStatusChanged) {
|
||||
pageState.sendMessageToContentScript({
|
||||
type: MessageType.ChannelSubStatusChange,
|
||||
channelName,
|
||||
wasSubscribed,
|
||||
isSubscribed,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[TTV LOL PRO] Failed to parse GraphQL response:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Twitch Usher responses.
|
||||
usherRes: if (
|
||||
host != null &&
|
||||
@ -717,89 +750,6 @@ async function sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function getSubStatusRequest(channelName: string): Request {
|
||||
const cookieMap = new Map<string, string>(
|
||||
document.cookie
|
||||
.split(";")
|
||||
.map(cookie => cookie.trim().split("="))
|
||||
.map(([name, value]) => [name, decodeURIComponent(value)])
|
||||
);
|
||||
const headersMap = new Map<string, string>([
|
||||
[
|
||||
"Authorization",
|
||||
cookieMap.has("auth-token")
|
||||
? `OAuth ${cookieMap.get("auth-token")}`
|
||||
: "undefined",
|
||||
],
|
||||
["Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko"],
|
||||
["Device-ID", generateRandomString(32)],
|
||||
]);
|
||||
|
||||
return new Request("https://gql.twitch.tv/gql", {
|
||||
method: "POST",
|
||||
headers: Object.fromEntries(headersMap),
|
||||
body: JSON.stringify({
|
||||
operationName: "ChannelPage_SubscribeButton_User",
|
||||
variables: {
|
||||
login: channelName,
|
||||
},
|
||||
extensions: {
|
||||
persistedQuery: {
|
||||
version: 1,
|
||||
sha256Hash:
|
||||
"a1da17caf3041632c3f9b4069dfc8d93ff10b5b5023307ec0a694a9d8eae991e",
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async function checkChannelSubStatus(
|
||||
channelName: string,
|
||||
pageState: PageState
|
||||
) {
|
||||
try {
|
||||
const channelSubStatus =
|
||||
await pageState.sendMessageToPageScriptAndWaitForResponse(
|
||||
pageState.scope,
|
||||
{
|
||||
type: MessageType.ChannelSubStatusQuery,
|
||||
channelName,
|
||||
},
|
||||
MessageType.ChannelSubStatusQueryResponse
|
||||
);
|
||||
if (!channelSubStatus || channelSubStatus.error) {
|
||||
throw new Error(
|
||||
`Error querying channel sub status: ${channelSubStatus.error}`
|
||||
);
|
||||
}
|
||||
const wasSubscribed = wasChannelSubscriber(channelName, pageState);
|
||||
const isSubscribed = channelSubStatus.isSubscribed;
|
||||
const hasSubStatusChanged =
|
||||
(wasSubscribed && !isSubscribed) || (!wasSubscribed && isSubscribed);
|
||||
if (hasSubStatusChanged) {
|
||||
try {
|
||||
const response =
|
||||
await pageState.sendMessageToContentScriptAndWaitForResponse(
|
||||
pageState.scope,
|
||||
{
|
||||
type: MessageType.ChannelSubStatusChange,
|
||||
channelName,
|
||||
wasSubscribed,
|
||||
isSubscribed,
|
||||
},
|
||||
MessageType.ChannelSubStatusChangeResponse
|
||||
);
|
||||
if (typeof response.whitelistedChannels === "object") {
|
||||
pageState.state!.whitelistedChannels = response.whitelistedChannels;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[TTV LOL PRO] Failed to check channel sub status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
//#region Video Weaver URL replacement
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,7 @@ const pageState: PageState = {
|
||||
sendMessageToWorkerScriptsAndWaitForResponse,
|
||||
};
|
||||
|
||||
const NATIVE_FETCH = window.fetch;
|
||||
window.fetch = getFetch(pageState);
|
||||
|
||||
const NATIVE_WORKER = window.Worker;
|
||||
|
Loading…
x
Reference in New Issue
Block a user