From 4f3c2e50dfc11f9c28fff65d48188e03429b118d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Fern=C3=A1ndez=20Aldana?= Date: Sun, 30 Oct 2022 22:34:06 +0100 Subject: [PATCH] =?UTF-8?q?Refactorizaci=C3=B3n=20de=20como=20se=20escoge?= =?UTF-8?q?=20si=20un=20stream=20cumple=20y=20ahora=20en=20Twitch=20se=20m?= =?UTF-8?q?ira=20las=20etiquetas=20como=20alternativa=20al=20t=C3=ADtulo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/data/env.template.json | 3 +- src/ts/background.ts | 7 +- .../background/streamAdapter/streamAdapter.ts | 10 +-- .../streamAdapter/twitchStreamAdapter.ts | 79 +++++++++++++++---- .../streamAdapter/youtubeStreamAdapter.ts | 37 ++++----- src/ts/types/env.ts | 1 + src/ts/types/stream.ts | 10 +-- 7 files changed, 94 insertions(+), 53 deletions(-) diff --git a/assets/data/env.template.json b/assets/data/env.template.json index 8d5c2b2..19c2a77 100644 --- a/assets/data/env.template.json +++ b/assets/data/env.template.json @@ -1,3 +1,4 @@ { - "twitchClientId": "SOME-ID" + "twitchClientId": "SOME-ID", + "twitchGraphQlClientId": "SOME-ID2" } diff --git a/src/ts/background.ts b/src/ts/background.ts index 7c990dc..b577fd9 100644 --- a/src/ts/background.ts +++ b/src/ts/background.ts @@ -1,6 +1,6 @@ import Data from '/assets/data/data.json' -import { ApiStream, EnrichedStream, IslandType, PartialApiStream } from './types/stream' +import { ApiStream, EnrichedStream, IslandType } from './types/stream' import { DataType } from './types/data' import { ConnectDisconnectResponse, ConnectMessage, DisconnectMessage, Message, Response, StatusResponse } from './types/messaging' import PlatformList from './platform/background/BackgroundPlatformListCompile' @@ -37,10 +37,6 @@ async function validate(): Promise { } } -function acceptStream( stream: PartialApiStream ) { - return stream.subtitle.toLocaleLowerCase().includes( 'tortill' ) -} - async function getAllStreams(): Promise { const channelIds: Record> = {} const islands: Record = {} @@ -71,7 +67,6 @@ async function getAllStreams(): Promise { const apiStreams = await platform.getStreamAdapter().getStreams( client, channelIds[identifier], - acceptStream ) as Record for ( const [channel, apiStream] of Object.entries( apiStreams ) ) { streams.push( { diff --git a/src/ts/platform/background/streamAdapter/streamAdapter.ts b/src/ts/platform/background/streamAdapter/streamAdapter.ts index b53046c..1771aff 100644 --- a/src/ts/platform/background/streamAdapter/streamAdapter.ts +++ b/src/ts/platform/background/streamAdapter/streamAdapter.ts @@ -1,5 +1,5 @@ import OauthClient from "../../../oauthClient/oauthClient"; -import { PartialApiStream, ApiStream } from "../../../types/stream"; +import { ApiStream } from "../../../types/stream"; export type CachedStream = { time: number, @@ -7,15 +7,15 @@ export type CachedStream = { } export default abstract class StreamAdapter { - protected abstract doGetStreams( client: Client, channelIds: Set, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise> + protected abstract doGetStreams( client: Client, channelIds: Set ): Promise> - async getStreams( client: Client, channelIds: Set, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise> { + async getStreams( client: Client, channelIds: Set ): Promise> { const cacheMinutes = this.cacheMinutes() if ( cacheMinutes > 0 ) { const data = await client.getDataCache() const currentDate = Date.now() if ( 'streams' in data && currentDate - (data.streams as CachedStream).time < cacheMinutes * 60 * 1000 ) { - const apiStreams = await this.doGetStreams( client, channelIds, acceptStream ) + const apiStreams = await this.doGetStreams( client, channelIds ) const data = await client.getDataCache() data.streams = { time: currentDate, @@ -25,7 +25,7 @@ export default abstract class StreamAdapter { return apiStreams } } - return this.doGetStreams( client, channelIds, acceptStream ) + return this.doGetStreams( client, channelIds ) } protected cacheMinutes(): number { diff --git a/src/ts/platform/background/streamAdapter/twitchStreamAdapter.ts b/src/ts/platform/background/streamAdapter/twitchStreamAdapter.ts index ff28298..9b4fb83 100644 --- a/src/ts/platform/background/streamAdapter/twitchStreamAdapter.ts +++ b/src/ts/platform/background/streamAdapter/twitchStreamAdapter.ts @@ -1,9 +1,13 @@ +import Env from '/assets/data/env.json' +import { EnvType } from "../../../types/env"; import OauthTwitchAPIClient from "../../../oauthClient/twitch/main"; import { Scope } from "../../../oauthClient/twitch/scope"; import { User } from "../../../oauthClient/twitch/types"; -import { ApiStream, PartialApiStream } from "../../../types/stream"; +import { ApiStream } from "../../../types/stream"; import StreamAdapter from "./streamAdapter"; +const env = Env as EnvType + export default class TwitchStreamAdapter extends StreamAdapter { private static broadcasterPriority = { partner: 2, @@ -11,23 +15,27 @@ export default class TwitchStreamAdapter extends StreamAdapter, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise> { + protected async doGetStreams( client: OauthTwitchAPIClient, channelIds: Set ): Promise> { + const partialStreams: Record = {} const apiStreams: Record = {} const streams = await client.getStreams( channelIds ) if ( streams ) { for ( const s of streams ) { - const baseStream: PartialApiStream = { - subtitle: s.title + let acceptStream = s.title.toLocaleLowerCase().includes( 'tortill' ) + if ( !acceptStream ) { + const tags = await this.getTags( s.user_login ) + acceptStream = [...tags].some( t => t.toLocaleLowerCase().includes( 'tortill' ) ) } - if ( !acceptStream || acceptStream( baseStream ) ) { - const apiStream = baseStream as ApiStream - apiStream.title = s.user_name - apiStream.viewers = s.viewer_count - apiStream.url = `https://twitch.tv/${ s.user_login }` - apiStreams[s.user_login] = apiStream + if ( acceptStream ) { + partialStreams[s.user_login] = { + title: s.user_name, + subtitle: s.title, + viewers: s.viewer_count, + url: `https://twitch.tv/${ s.user_login }` + } } } - if ( Object.keys( apiStreams ).length > 0 ) { + if ( Object.keys( partialStreams ).length > 0 ) { const followedLogins: Set = new Set() const scope = client.scopes as Set | undefined if ( scope?.has( Scope.USER_READ_FOLLOWS ) ) { @@ -37,15 +45,54 @@ export default class TwitchStreamAdapter extends StreamAdapter = {}; - ( ( await client.getUsers( new Set( Object.keys( apiStreams ) ) ) ) as User[] ).forEach( u => { userLoginsRecord[u.login] = u } ) - for ( const [login, apiStream] of Object.entries( apiStreams ) ) { + ( ( await client.getUsers( new Set( Object.keys( partialStreams ) ) ) ) as User[] ).forEach( u => { userLoginsRecord[u.login] = u } ) + for ( const [login, partialStream] of Object.entries( partialStreams ) ) { const u = userLoginsRecord[login] - apiStream.imageUrl = u.profile_image_url - apiStream.favourite = followedLogins.has( u.login ) - apiStream.priority = TwitchStreamAdapter.broadcasterPriority[u.broadcaster_type as keyof typeof TwitchStreamAdapter.broadcasterPriority] + apiStreams[login] = { + ...partialStream, + imageUrl: u.profile_image_url, + favourite: followedLogins.has( login ), + priority: TwitchStreamAdapter.broadcasterPriority[u.broadcaster_type] + } } } } return apiStreams } + + private async getTags( channelId: string ): Promise> { + let tags: string[] = [] + try { + const response = await fetch( + 'https://gql.twitch.tv/gql', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Client-Id': env.twitchGraphQlClientId + }, + body: JSON.stringify( + [{ + operationName: 'StreamTagsTrackingChannel', + variables: { + channel: channelId + }, + extensions: { + persistedQuery: { + version: 1, + sha256Hash: '6aa3851aaaf88c320d514eb173563d430b28ed70fdaaf7eeef6ed4b812f48608' + } + } + }] ) + } + ) + if ( response.ok ) { + const json = await response.json() + tags = ( json[0]?.data?.user?.stream?.freeformTags ?? [] ).map( ( t: { name: string } ) => t.name ) + } + + // eslint-disable-next-line no-empty + } catch { } + return new Set( tags ) + } } diff --git a/src/ts/platform/background/streamAdapter/youtubeStreamAdapter.ts b/src/ts/platform/background/streamAdapter/youtubeStreamAdapter.ts index 1d12d07..eb7483b 100644 --- a/src/ts/platform/background/streamAdapter/youtubeStreamAdapter.ts +++ b/src/ts/platform/background/streamAdapter/youtubeStreamAdapter.ts @@ -1,9 +1,10 @@ import OauthYoutubeAPIClient from "../../../oauthClient/youtube/main"; -import { ApiStream, PartialApiStream } from "../../../types/stream"; +import { ApiStream } from "../../../types/stream"; import StreamAdapter from "./streamAdapter"; export default class YoutubeStreamAdapter extends StreamAdapter { - protected async doGetStreams( client: OauthYoutubeAPIClient, channelIds: Set, acceptStream?: ( baseStream: PartialApiStream ) => boolean ) { + protected async doGetStreams( client: OauthYoutubeAPIClient, channelIds: Set ) { + const partialStreams: Record = {} const apiStreams: Record = {} const streams = await client.getStreams( channelIds ) if ( Object.keys( streams ).length > 0 ) { @@ -11,27 +12,27 @@ export default class YoutubeStreamAdapter extends StreamAdapter { const videoId = streams[channelId] const video = videos[videoId] - const baseStream: PartialApiStream = { - subtitle: video.title - } - if ( !acceptStream || acceptStream( baseStream ) ) { - const apiStream = baseStream as ApiStream - apiStream.url = `https://www.youtube.com/watch?v=${ videoId }` - apiStream.viewers = video.viewers - apiStreams[channelId] = apiStream + if ( video.title.toLocaleLowerCase().includes( 'tortill' ) ) { + partialStreams[channelId] = { + subtitle: video.title, + url: `https://www.youtube.com/watch?v=${ videoId }`, + viewers: video.viewers, + } } } ) - if ( Object.keys( apiStreams ).length > 0 ) { - const users = await client.getUsers( new Set(Object.keys( apiStreams )) ) + if ( Object.keys( partialStreams ).length > 0 ) { + const users = await client.getUsers( new Set( Object.keys( partialStreams ) ) ) const followedChannels = await client.getSubscribedChannels() - Object.entries( apiStreams ).forEach( apiStreamEntry => { - const [channelId, apiStream] = apiStreamEntry + for ( const [channelId, partialStream] of Object.entries( partialStreams ) ) { const user = users[channelId] - apiStream.favourite = followedChannels.has( channelId ) - apiStream.imageUrl = user.profileImage.toString() - apiStream.title = user.name - } ) + apiStreams[channelId] = { + ...partialStream, + favourite: followedChannels.has( channelId ), + imageUrl: user.profileImage.toString(), + title: user.name + } + } } } diff --git a/src/ts/types/env.ts b/src/ts/types/env.ts index 7b4bce7..d10ceab 100644 --- a/src/ts/types/env.ts +++ b/src/ts/types/env.ts @@ -1,3 +1,4 @@ export type EnvType = { twitchClientId: string + twitchGraphQlClientId: string } diff --git a/src/ts/types/stream.ts b/src/ts/types/stream.ts index 6d75978..1a3c928 100644 --- a/src/ts/types/stream.ts +++ b/src/ts/types/stream.ts @@ -8,8 +8,9 @@ type ExtraStream = { }; -type RemainingApiStream = { +export type ApiStream = { title: string; + subtitle: string; url: string; imageUrl: string; viewers: number; @@ -17,9 +18,4 @@ type RemainingApiStream = { priority?: number; } -export type PartialApiStream = { - subtitle: string; -} - -export type EnrichedStream = ApiStream & ExtraStream -export type ApiStream = RemainingApiStream & PartialApiStream +export type EnrichedStream = ApiStream & ExtraStream \ No newline at end of file