mirror of
https://github.com/younesaassila/ttv-lol-pro.git
synced 2025-04-29 22:14:27 +02:00
Fix auto whitelist subs feature
- The token in the Usher req lies about the sub status (always false)
This commit is contained in:
parent
2a79647fae
commit
be39a5344f
@ -44,14 +44,28 @@ export function getFetch(pageState: PageState): typeof fetch {
|
|||||||
cachedPlaybackTokenRequestHeaders,
|
cachedPlaybackTokenRequestHeaders,
|
||||||
cachedPlaybackTokenRequestBody
|
cachedPlaybackTokenRequestBody
|
||||||
);
|
);
|
||||||
const message = {
|
pageState.sendMessageToWorkerScripts(pageState.twitchWorkers, {
|
||||||
type: MessageType.NewPlaybackAccessTokenResponse,
|
type: MessageType.NewPlaybackAccessTokenResponse,
|
||||||
newPlaybackAccessToken,
|
newPlaybackAccessToken,
|
||||||
};
|
});
|
||||||
pageState.sendMessageToWorkerScripts(
|
break;
|
||||||
pageState.twitchWorkers,
|
case MessageType.ChannelSubStatusQuery:
|
||||||
message
|
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;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -199,7 +213,7 @@ export function getFetch(pageState: PageState): typeof fetch {
|
|||||||
pageState.state?.anonymousMode === true ||
|
pageState.state?.anonymousMode === true ||
|
||||||
(shouldFlagRequest && willFailIntegrityCheckIfProxied);
|
(shouldFlagRequest && willFailIntegrityCheckIfProxied);
|
||||||
if (shouldOverrideRequest) {
|
if (shouldOverrideRequest) {
|
||||||
const newRequest = await getDefaultPlaybackAccessTokenRequest(
|
const newRequest = getDefaultPlaybackAccessTokenRequest(
|
||||||
channelName,
|
channelName,
|
||||||
pageState.state?.anonymousMode === true
|
pageState.state?.anonymousMode === true
|
||||||
);
|
);
|
||||||
@ -273,31 +287,8 @@ export function getFetch(pageState: PageState): typeof fetch {
|
|||||||
isLivestream &&
|
isLivestream &&
|
||||||
channelName != null
|
channelName != null
|
||||||
) {
|
) {
|
||||||
const wasSubscribed = wasChannelSubscriber(channelName, pageState);
|
// TODO: Maybe also check before PlaybackAccessToken requests? (But then that's a LOT of overhead.)
|
||||||
const isSubscribed = url.includes(
|
await checkChannelSubStatus(channelName, pageState);
|
||||||
encodeURIComponent('"subscriber":true')
|
|
||||||
);
|
|
||||||
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 {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const isWhitelisted = isChannelWhitelisted(channelName, pageState);
|
const isWhitelisted = isChannelWhitelisted(channelName, pageState);
|
||||||
if (!isLivestream || isFrontpage || isWhitelisted) {
|
if (!isLivestream || isFrontpage || isWhitelisted) {
|
||||||
@ -726,6 +717,89 @@ async function sleep(ms: number): Promise<void> {
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
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
|
//#region Video Weaver URL replacement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -734,10 +808,10 @@ async function sleep(ms: number): Promise<void> {
|
|||||||
* @param anonymousMode
|
* @param anonymousMode
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async function getDefaultPlaybackAccessTokenRequest(
|
function getDefaultPlaybackAccessTokenRequest(
|
||||||
channel: string | null = null,
|
channel: string | null = null,
|
||||||
anonymousMode: boolean = false
|
anonymousMode: boolean = false
|
||||||
): Promise<Request | null> {
|
): Request | null {
|
||||||
// We can use `location.href` because we're in the page script.
|
// We can use `location.href` because we're in the page script.
|
||||||
const channelName = channel ?? findChannelFromTwitchTvUrl(location.href);
|
const channelName = channel ?? findChannelFromTwitchTvUrl(location.href);
|
||||||
if (!channelName) return null;
|
if (!channelName) return null;
|
||||||
@ -793,7 +867,7 @@ async function fetchReplacementPlaybackAccessToken(
|
|||||||
): Promise<PlaybackAccessToken | null> {
|
): Promise<PlaybackAccessToken | null> {
|
||||||
// Not using the cached request because we'd need to check if integrity requests are proxied.
|
// Not using the cached request because we'd need to check if integrity requests are proxied.
|
||||||
try {
|
try {
|
||||||
let request = await getDefaultPlaybackAccessTokenRequest(
|
let request = getDefaultPlaybackAccessTokenRequest(
|
||||||
null,
|
null,
|
||||||
pageState.state?.anonymousMode === true
|
pageState.state?.anonymousMode === true
|
||||||
);
|
);
|
||||||
|
@ -79,6 +79,8 @@ export const enum MessageType {
|
|||||||
EnableFullMode = "TLP_EnableFullMode",
|
EnableFullMode = "TLP_EnableFullMode",
|
||||||
EnableFullModeResponse = "TLP_EnableFullModeResponse",
|
EnableFullModeResponse = "TLP_EnableFullModeResponse",
|
||||||
DisableFullMode = "TLP_DisableFullMode",
|
DisableFullMode = "TLP_DisableFullMode",
|
||||||
|
ChannelSubStatusQuery = "TLP_ChannelSubStatusQuery",
|
||||||
|
ChannelSubStatusQueryResponse = "TLP_ChannelSubStatusQueryResponse",
|
||||||
ChannelSubStatusChange = "TLP_ChannelSubStatusChange",
|
ChannelSubStatusChange = "TLP_ChannelSubStatusChange",
|
||||||
ChannelSubStatusChangeResponse = "TLP_ChannelSubStatusChangeResponse",
|
ChannelSubStatusChangeResponse = "TLP_ChannelSubStatusChangeResponse",
|
||||||
UsherResponse = "TLP_UsherResponse",
|
UsherResponse = "TLP_UsherResponse",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user