Skip to content

Commit

Permalink
feat: add twitch support
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardobl committed Jun 9, 2022
1 parent 52c104b commit f22af2f
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
80 changes: 80 additions & 0 deletions src/extension/content-script/batteries/Twitch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import getOriginData from "../originData";
import setLightningData from "../setLightningData";

const urlMatcher = /^https:\/\/(?:www\.)?twitch.tv\/([^/]+).+$/;
const validationRegex = /^[a-z0-9_.-]+$/i;
const clientIdExtractor = /clientId="([^"]+)"/i;

const battery = async (): Promise<void> => {
const urlParts = document.location.pathname.split("/");
const channel = urlParts[1];

if (!channel) return; // not a channel
if (!validationRegex.test(channel)) return; // invalid channel

// Grab client ID from the page HTML
const clientIdMatch =
document.documentElement.outerHTML.match(clientIdExtractor);
const clientId = clientIdMatch ? clientIdMatch[1] : "";
if (!clientId) return; // client id not found

// Simulate internal API call to obtain userData.
// A public api should be used here instead, but i couldn't find an alternative
// that didn't require oauth (thus breaking user experience).
let userData = await fetch(`https://gql.twitch.tv/gql`, {
headers: {
"Client-Id": clientId,
"User-Agent":
"Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0",
},
method: "POST",
body: JSON.stringify([
{
operationName: "ChannelRoot_AboutPanel",
variables: {
channelLogin: channel,
skipSchedule: true,
},
extensions: {
persistedQuery: {
version: 1,
sha256Hash:
"6089531acef6c09ece01b440c41978f4c8dc60cb4fa0124c9a9d3f896709b6c6",
},
},
},
]),
}).then((res) => res.json());

if (!userData || !userData[0] || !userData[0].data.user) return; // not a valid user?

userData = userData[0].data.user;

const userDescription = userData.description ?? "";
const lightningAddr = findLightningAddressInText(userDescription);

if (lightningAddr) {
setLightningData([
{
method: "lnurl",
recipient: lightningAddr,
...getOriginData(),
description: userDescription,
name: userData.displayName ?? channel,
icon: userData.profileImageURL ?? "",
},
]);
}
};

function findLightningAddressInText(text: string) {
const match = text.match(/(⚡:?|lightning:|lnurl:)\s?(\S+@\S+)/i);
if (match) return match[2];
}

const Twitch = {
urlMatcher,
battery,
};

export default Twitch;
2 changes: 2 additions & 0 deletions src/extension/content-script/batteries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Medium from "./Medium";
import Monetization from "./Monetization";
import Peertube from "./Peertube";
import Reddit from "./Reddit";
import Twitch from "./Twitch";
import Twitter from "./Twitter";
// import YouTubeChannel from "./YouTubeChannel";
import VimeoVideo from "./VimeoVideo";
Expand All @@ -21,6 +22,7 @@ const enhancements = [
VimeoVideo,
Medium,
GitHub,
Twitch,

// Monetization must likely always be the last one as this is the fallback option if no specific enhancement matched
Monetization,
Expand Down

0 comments on commit f22af2f

Please sign in to comment.