|
// ==UserScript== |
|
// @name EFT Drop Stream Guide |
|
// @namespace https://kldzj.dev |
|
// @version 0.1.1 |
|
// @description Automatically navigate to the next best stream during EFT Christmas Drops |
|
// @author kldzj |
|
// @include /^https:\/\/(www\.)?twitch\.tv\/([a-zA-Z0-9_]*)?$/ |
|
// @exclude /^https:\/\/(www\.)?twitch\.tv\/drops/ |
|
// @exclude /^https:\/\/(www\.)?twitch\.tv\/settings/ |
|
// @icon https://www.google.com/s2/favicons?domain=twitch.tv |
|
// @downloadURL https://gist.github.com/kldzj/bacf30f50d3264d1245c8ab4af8e9262/raw/eft-drop-guide.user.js |
|
// @installURL https://gist.github.com/kldzj/bacf30f50d3264d1245c8ab4af8e9262/raw/eft-drop-guide.user.js |
|
// @updateURL https://gist.github.com/kldzj/bacf30f50d3264d1245c8ab4af8e9262/raw/eft-drop-guide.user.js |
|
// @grant GM_getTab |
|
// @grant GM_saveTab |
|
// ==/UserScript== |
|
|
|
const dropsEnabledTag = 'c2542d6d-cd10-4532-919b-3d19f30a768b'; |
|
|
|
function __getTab() { |
|
return new Promise(resolve => GM_getTab(resolve)); |
|
} |
|
|
|
async function __setTab(data) { |
|
const current = await __getTab(); |
|
GM_saveTab({ ...current, ...data }); |
|
} |
|
|
|
function getTwitchToken() { |
|
const t = document.cookie.match(/(?:^|; )auth-token=([^;]*)/); |
|
return t[1] || ''; |
|
} |
|
|
|
async function fetchOnlineStreams() { |
|
const response = await fetch('https://gql.twitch.tv/gql', { |
|
method: 'post', |
|
headers: { |
|
Authorization: `OAuth ${getTwitchToken()}` |
|
}, |
|
body: JSON.stringify([{ |
|
operationName: "DirectoryPage_Game", |
|
extensions: { |
|
persistedQuery: { |
|
sha256Hash: "d5c5df7ab9ae65c3ea0f225738c08a36a4a76e4c6c31db7f8c4b8dc064227f9e", |
|
version: 1 |
|
} |
|
}, |
|
variables: { |
|
name: "escape from tarkov", |
|
sortTypeIsRecency: false, |
|
limit: 100, |
|
options: { |
|
sort: "RELEVANCE", |
|
includeRestricted: ["SUB_ONLY_LIVE"], |
|
tags: [dropsEnabledTag] |
|
} |
|
} |
|
}]) |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error('Failed to fetch online streams!'); |
|
} |
|
|
|
const result = await response.json(); |
|
if (!result[0] || !result[0].data) { |
|
throw new Error('Invalid streams response!'); |
|
} |
|
|
|
return result[0].data.game.streams.edges.map(({ node }) => node); |
|
} |
|
|
|
function getCurrentStream() { |
|
const [_, __, match] = window.location.href.match(/https?:\/\/(www\.)?twitch.tv\/([a-z0-9_]*)/i); |
|
if (!match || match === '') return null; |
|
return match; |
|
} |
|
|
|
function getFirstStreamBroadcaster(streams) { |
|
return streams.find(stream => stream.type === 'live').broadcaster.login; |
|
} |
|
|
|
async function addButton() { |
|
const btn = document.createElement('button'); |
|
const baseStyle = 'position:fixed;bottom:5px;left:8px;background:white;border-radius:2px;padding:2px 5px;z-index:99999;'; |
|
|
|
const styles = { |
|
active: 'color:green', |
|
inactive: 'color:red' |
|
}; |
|
|
|
const texts = { |
|
active: 'Guiding...', |
|
inactive: 'Guide Me' |
|
}; |
|
|
|
const { guiding } = await __getTab(); |
|
btn.innerText = guiding ? texts.active : texts.inactive; |
|
btn.style = baseStyle + (guiding ? styles.active : styles.inactive); |
|
|
|
btn.onclick = async (e) => { |
|
e.preventDefault(); |
|
const { guiding: active } = await __getTab(); |
|
if (active) { |
|
await __setTab({ guiding: false }); |
|
btn.innerText = texts.inactive; |
|
btn.style = baseStyle + styles.inactive; |
|
} else { |
|
await __setTab({ guiding: true }); |
|
btn.innerText = texts.active; |
|
btn.style = baseStyle + styles.active; |
|
await handler(); |
|
} |
|
}; |
|
|
|
document.getElementsByTagName('body')[0].appendChild(btn); |
|
} |
|
|
|
async function handler() { |
|
const { guiding } = await __getTab(); |
|
if (!guiding) return console.log('Currently not guiding...'); |
|
|
|
const currentStream = getCurrentStream(); |
|
const streams = await fetchOnlineStreams(); |
|
if (!currentStream || !streams.find(({ broadcaster }) => broadcaster.login.toLowerCase() === currentStream)) { |
|
const newStream = getFirstStreamBroadcaster(streams); |
|
console.log(`Current stream is none or offline, sending you to ${newStream}`); |
|
window.location.href = `https://twitch.tv/${newStream}`; |
|
return; |
|
} |
|
|
|
console.log('You should still be receiving drop progress...'); |
|
} |
|
|
|
let initialized = false; |
|
async function initialize() { |
|
if (initialized) return; |
|
|
|
await addButton(); |
|
setInterval(handler, 1 * 60 * 1000); |
|
initialized = true; |
|
|
|
console.log('EFT Drop Stream Guide loaded...'); |
|
await handler(); |
|
} |
|
|
|
initialize().catch(e => console.error(e)); |