mirror of
https://codeberg.org/SafeTwitch/safetwitch-backend.git
synced 2024-12-22 05:02:58 -05:00
Add endpoint for the emotes
This commit is contained in:
parent
075c5ea9f7
commit
17c63912e2
7 changed files with 186 additions and 4 deletions
|
@ -87,4 +87,14 @@ profileRouter.get('/search', async (req, res, next) => {
|
|||
})
|
||||
})
|
||||
|
||||
profileRouter.get('/emotes/:streamerName', async (req, res, next) => {
|
||||
const emotes = await twitch.getEmotes(req.params.streamerName)
|
||||
.catch(next)
|
||||
|
||||
if(emotes)
|
||||
res.send(emotes)
|
||||
})
|
||||
|
||||
|
||||
|
||||
export default profileRouter
|
|
@ -82,8 +82,6 @@ proxyRouter.get('/stream/segment/:encodedUrl', async (req: Request, res: Respons
|
|||
res.send(buf)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const twitchChatServer = new TwitchChatServer();
|
||||
export const wsServer = new ws.Server({ noServer: true });
|
||||
twitchChatServer.startWebSocketServer(wsServer);
|
||||
|
|
18
types/scraping/emotes/bttv.ts
Normal file
18
types/scraping/emotes/bttv.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
export type bttvData = SharedEmotesEntity[]
|
||||
|
||||
export interface SharedEmotesEntity {
|
||||
id: string;
|
||||
code: string;
|
||||
imageType: string;
|
||||
animated: boolean;
|
||||
user: User;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
codeOriginal?: string | null;
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
providerId: string;
|
||||
}
|
52
types/scraping/emotes/ffz.ts
Normal file
52
types/scraping/emotes/ffz.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
export interface ffzData {
|
||||
room: Room;
|
||||
sets: Sets;
|
||||
}
|
||||
|
||||
export interface Room {
|
||||
_id: number;
|
||||
twitch_id: number;
|
||||
youtube_id?: null;
|
||||
id: string;
|
||||
is_group: boolean;
|
||||
display_name: string;
|
||||
set: number;
|
||||
}
|
||||
|
||||
export interface Sets {
|
||||
[k: number]: SetData;
|
||||
}
|
||||
|
||||
export interface SetData {
|
||||
id: number;
|
||||
_type: number;
|
||||
title: string;
|
||||
emoticons: EmoteData[];
|
||||
}
|
||||
|
||||
export interface EmoteData {
|
||||
id: number;
|
||||
name: string;
|
||||
height: number;
|
||||
width: number;
|
||||
public: boolean;
|
||||
hidden: boolean;
|
||||
modifier: boolean;
|
||||
modifier_flags: number;
|
||||
owner: Owner;
|
||||
urls: Urls;
|
||||
status: number;
|
||||
usage_count: number;
|
||||
created_at: string;
|
||||
last_updated: string;
|
||||
}
|
||||
|
||||
export interface Owner {
|
||||
_id: number;
|
||||
name: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export interface Urls {
|
||||
[k: number]: string
|
||||
}
|
2
types/scraping/emotes/index.ts
Normal file
2
types/scraping/emotes/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './bttv'
|
||||
export * from './ffz'
|
53
util/scraping/extractor/emoteHandler.ts
Normal file
53
util/scraping/extractor/emoteHandler.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { Urls, bttvData, ffzData } from "../../../types/scraping/emotes"
|
||||
|
||||
interface Emote {
|
||||
name: string
|
||||
urls: Urls
|
||||
}
|
||||
|
||||
export const ffzEmotesHandler = (emoteData: ffzData) => {
|
||||
const sets = emoteData.sets[emoteData.room.set]
|
||||
const emotes: Emote[] = []
|
||||
|
||||
for (let emote of sets.emoticons) {
|
||||
const data: Emote = {
|
||||
name: emote.name,
|
||||
urls: emote.urls
|
||||
}
|
||||
|
||||
emotes.push(data)
|
||||
}
|
||||
|
||||
return emotes
|
||||
}
|
||||
|
||||
const generateBttvEmoteUrls = (id: string) => {
|
||||
const bttvApi = 'https://cdn.betterttv.net/emote/'
|
||||
const urls: Urls = {}
|
||||
|
||||
// Creates urls like "https://cdn.betterttv.net/emote/6368b11b9013520589f5ac0c/3x" from 1x to 3x
|
||||
for (let i = 1; i < 4; i++) {
|
||||
urls[i] = `${bttvApi}${id}/${i}x`
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
export const bttvEmotesHandler = (emoteData: bttvData) => {
|
||||
const rawEmoteArray = emoteData
|
||||
console.log(emoteData.length)
|
||||
const emotes: Emote[] = []
|
||||
|
||||
for (let rawEmote of rawEmoteArray) {
|
||||
const formattedEmote: Emote = {
|
||||
name: rawEmote.code,
|
||||
urls: generateBttvEmoteUrls(rawEmote.id)
|
||||
}
|
||||
|
||||
emotes.push(formattedEmote)
|
||||
}
|
||||
|
||||
console.log(emotes.length)
|
||||
console.log(emotes)
|
||||
return emotes
|
||||
}
|
|
@ -3,6 +3,7 @@ import { StreamerData, StreamData, Social, Badge } from "../../../types/scraping
|
|||
import { Category, Tag } from "../../../types/scraping/Category"
|
||||
import { CategoryData, CategoryMinifiedStream } from "../../../types/scraping/CategoryData"
|
||||
import { SearchResult } from "../../../types/scraping/Search"
|
||||
import { bttvEmotesHandler, ffzEmotesHandler } from "./emoteHandler"
|
||||
|
||||
const base64 = (data: String) => {
|
||||
return Buffer.from(data).toString('base64url')
|
||||
|
@ -259,8 +260,6 @@ export class TwitchAPI {
|
|||
headers: this.headers
|
||||
})
|
||||
|
||||
console.log(res.status)
|
||||
|
||||
return await res.text()
|
||||
}
|
||||
|
||||
|
@ -589,4 +588,54 @@ export class TwitchAPI {
|
|||
|
||||
return finalData
|
||||
}
|
||||
|
||||
public getStreamerId = async (channelName: string) => {
|
||||
const payload = {
|
||||
"operationName": "ChannelRoot_AboutPanel",
|
||||
"variables": {
|
||||
"channelLogin": channelName,
|
||||
"skipSchedule": true
|
||||
},
|
||||
"extensions": {
|
||||
"persistedQuery": {
|
||||
"version": 1,
|
||||
"sha256Hash": "6089531acef6c09ece01b440c41978f4c8dc60cb4fa0124c9a9d3f896709b6c6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(this.twitchUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
headers: this.headers
|
||||
})
|
||||
const data = await res.json()
|
||||
|
||||
if (!data.data.user) {
|
||||
return Promise.reject(new Error(`Steamer ${channelName} is offline.`))
|
||||
}
|
||||
|
||||
return Number(data.data.user.id)
|
||||
}
|
||||
|
||||
public getEmotes = async (channelName: string) => {
|
||||
let id = await this.getStreamerId(channelName)
|
||||
const ffzUrl = 'https://api.frankerfacez.com/v1/room/id/' + id
|
||||
const bttvGlobalUrl = 'https://api.betterttv.net/3/cached/users/twitch/121059319'
|
||||
const bttvChannelUrl = 'https://api.betterttv.net/3/cached/users/twitch/' + id
|
||||
|
||||
let res = await fetch(ffzUrl)
|
||||
let data = await res.json()
|
||||
const ffzEmotes = ffzEmotesHandler(data)
|
||||
|
||||
res = await fetch(bttvGlobalUrl)
|
||||
data = await res.json()
|
||||
const bttvGlobalEmotes = bttvEmotesHandler([...data.channelEmotes, ...data.sharedEmotes])
|
||||
|
||||
res = await fetch(bttvChannelUrl)
|
||||
data = await res.json()
|
||||
const bttvChannelEmotes = bttvEmotesHandler(data.channelEmotes)
|
||||
|
||||
return [ ...ffzEmotes, ...bttvGlobalEmotes, ...bttvChannelEmotes]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue