refactor for new host

This commit is contained in:
2025-12-19 08:52:44 -08:00
parent 8e371b92eb
commit 1b624ce8d4
20 changed files with 2365 additions and 1012 deletions
+2
View File
@@ -0,0 +1,2 @@
node_modules
static/*.js*
+1
View File
@@ -0,0 +1 @@
@sugoidogo:registry=https://gitea.sugoidogo.com/api/packages/sugoidogo/npm/
-317
View File
@@ -1,317 +0,0 @@
<title>SugoiTTS</title>
<h1>SugoiTTS</h1>
<div id="status">Loading...</div>
<br>
<div id="content" hidden>
<form name="new">
<label>
Redeem Name:
<input type="text" name="name" required>
<button>Create Redeem</button>
</label>
</form>
<form name="config">
<div id="redeems"></div>
<button>save changes</button>
</form>
</div>
<a id="prompt" href="https://www.twitch.tv/subs/sugoidogo">Subscribe to unlock SSML and Neural Voices</a>
<template id="redeem">
<form class="redeem">
Redeem Name:
<span id="title"></span>
<br>
<label>
Voice:
<input type="search" list="voices" name="voice" required>
</label>
<br>
<label>
<input type="radio" name="type" value="preset">
Preset Text:
</label>
<textarea name="presetText"></textarea>
<button type="button" onclick="testText(this.parentElement)">Test</button>
<br>
<label>
<input type="radio" name="type" value="dracula">
Dracula Flow
</label>
<label>
<input type="radio" name="type" value="input" checked>
Viewer input
</label>
<br>
<label>
<input type="checkbox" name="ssml" disabled>
Use SSML
</label>
<br>
<label>
<input type="checkbox" name="neural" disabled>
Use Neural Voice
</label>
<br>
<a target="_blank"></a>
<br>
<button type="button" id="delete" onclick="remove(this.parentElement)">delete</button>
<br>
</form>
</template>
<datalist id="voices"></datalist>
<datalist id="languages"></datalist>
<style>
.redeem {
border-left: 1px solid black;
padding-left: 10px;
}
textarea {
vertical-align: top;
height: 1.5em;
width: 20em;
}
</style>
<script>
import('https://cdn.jsdelivr.net/npm/@sentry/browser@8/+esm').then(sentry=>{
sentry.init({
dsn:'https://94c848565de04860a879df9a34ece8c2@app.glitchtip.com/8354',
environment:location.hostname,
release:"1.1.0",
integrations: [
sentry.feedbackIntegration({colorScheme:"light"})
]
})
}).catch(e=>console.warn('automatic error reporting failed to load',e))
try{
if(window.location.search.includes('remotejs')){
const channel=new URLSearchParams(window.location.search).get('remotejs')
const script=document.createElement('script')
script.src='https://remotejs.com/agent/agent.js'
script.setAttribute("data-consolejs-channel",channel)
document.head.appendChild(script)
}
}catch(e){
console.warn('remotejs load failed',e)
}
const client_id='1gsnqnvtrguxysilfqp5gkb1snswmf'
const scope='channel:manage:redemptions channel:read:redemptions'
const status_div=document.querySelector('div#status')
const new_form=document.querySelector('form[name=new]')
const redeem_template=document.querySelector('template#redeem').content.firstElementChild
const redeem_list=document.querySelector('div#redeems')
const voice_ids=[]
const ts_url='https://ts.sugoidogo.com/'
const tba_url='https://tba.sugoidogo.com/'
const twitch_polly_url='https://tts.sugoidogo.com/'
let tokens
function add_redeem(id,title,config){
const redeem_form=redeem_template.cloneNode(true)
redeem_form.name=id
redeem_form.querySelector('span#title').innerHTML=title
const a=redeem_form.querySelector('a')
let url=(location.origin+location.pathname).split('/')
url.pop()
url.push('overlay.html')
url=url.join('/')
url=new URL(url)
url.search=new URLSearchParams({
'refresh_token':tokens.refresh_token,
'redeem_id':id
})
a.href=url.href
a.innerHTML=url.href
for(const name in config){
if(name=='preset'){
redeem_form['type'].value='preset'
continue
}
redeem_form[name].value=config[name]
if(redeem_form[name].type=='checkbox'){
redeem_form[name].checked=name in config
}
}
redeem_list.appendChild(redeem_form)
}
function save(){
for(const form of redeem_list.children){
config=Object.fromEntries(new FormData(form))
id=form.name
fetch(ts_url+id,{
headers:tokens.auth_headers,
method:'POST',
body:JSON.stringify(config)
}).then(response=>{
if(!response.ok){
response.text().then(text=>{
status_div.innerHTML="Couldn't save settings<br>"+text
})
}
}).then(function(){
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards')
url.searchParams.append('broadcaster_id',tokens.user_id)
url.searchParams.append('id',id)
const body=JSON.stringify({'is_user_input_required':config.type=='preset'})
const headers={'Content-Type':'application/json'}
Object.assign(headers,tokens.auth_headers)
fetch(url,{headers:headers,method:'PATCH',body:body})
})
}
}
function remove(redeem_form){
const id=redeem_form.name
redeem_form.remove()
fetch(ts_url+id,{
headers:tokens.auth_headers,
method:'DELETE'
})
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards')
url.search=new URLSearchParams({
broadcaster_id:tokens.user_id,
id:id
})
fetch(url,{
headers:tokens.auth_headers,
method:'DELETE'
}).then(response=>{
if(!response.ok){
response.text().then(text=>{
status_div.innerHTML="Couldn't delete redeem<br>"+text
})
}
})
}
new_form.onsubmit=function(event){
event.preventDefault()
const redeem_name=event.target.name.value
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards')
url.searchParams.append('broadcaster_id',tokens.user_id)
const headers={'Content-Type':'application/json'}
Object.assign(headers,tokens.auth_headers)
body=JSON.stringify({
title:redeem_name,
cost:1,
is_enabled:false,
is_user_input_required:true
})
fetch(url,{
headers:headers,
method:'POST',
body:body
})
.then(response=>response.json())
.then(json=>{
const redeem=json['data'][0]
add_redeem(redeem.id,redeem.title,{voice:'Brian'})
event.target.name.value=''
save()
}).catch(error=>{
status_div.innerHTML='Something went wrong, check the console for more details'
console.log(error)
})
}
document.querySelector('form[name=config]').onsubmit=function(event){
event.preventDefault()
save()
}
import(tba_url+'tba.mjs')
.then(tba=>{
status_div.innerHTML='Signing in to Twitch...'
return tba.get_tokens(client_id,scope)
}).then(response=>{
tokens=response
//if(parseInt(tokens.tier)>=1000){
redeem_template.querySelector('[name=ssml]').disabled=false
//}
//if(parseInt(tokens.tier)>=2000){
redeem_template.querySelector('[name=neural]').disabled=false
document.querySelector('#prompt').hidden=true
//}
})
.then(()=>import(twitch_polly_url+'twitch-polly.mjs'))
.then(polly=>{
status_div.innerHTML='Loading voices...'
return polly.DescribeVoices(auth_headers=tokens.auth_headers)
}).then(voices=>{
const voice_list=document.querySelector('datalist#voices')
for(voice of voices){
const option=document.createElement('option')
option.value=voice.Id
option.innerHTML=voice.LanguageName+' '+voice.Gender+'\r\n'+voice.SupportedEngines
voice_list.appendChild(option)
voice_ids.push(voice.Id)
}
status_div.innerHTML='Loading redeems...'
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards')
url.search=new URLSearchParams({
broadcaster_id:tokens.user_id,
only_manageable_rewards:true
})
return fetch(url,{headers:auth_headers})
}).then(response=>response.json())
.then(json=>{
status_div.innerHTML='Loading settings...'
promises=[]
redeems=json['data']
for(const redeem of redeems){
const id=redeem.id
const title=redeem.title
const promise=fetch(ts_url+id,{headers:tokens.auth_headers})
.then(response=>response.json())
.then(config=>add_redeem(id,title,config))
.catch(error=>{
console.warn(error)
add_redeem(id,title,{voice:'Brian'})
})
promises.push(promise)
}
document.querySelector("div#content").hidden=false
return Promise.allSettled(promises)
}).then(()=>{
status_div.innerHTML='Ready!'
}).catch((error)=>{
status_div.innerHTML='Something went wrong during loading, check the console for more details'
console.error(error)
})
async function testText(form){
const polly=await import('https://tts.sugoidogo.com/twitch-polly.mjs')
const config=Object.fromEntries(new FormData(form))
console.log(config)
let TextType='text'
let Engine='standard'
let Voice=config.voice
if(config.ssml){
TextType='ssml'
}
if(config.neural){
Engine='neural'
}
polly.SynthesizeSpeech(tokens.auth_headers,config.presetText,Voice,TextType,Engine)
.then(async response=>{
if(response.ok){
return response.blob()
}else{
throw await response.text()
}
})
.then(blob=>{
const url=URL.createObjectURL(blob)
const audio=document.createElement('audio')
audio.src=url
audio.autoplay=true
document.body.appendChild(audio)
}).catch(error=>{
window.alert(error)
console.error(error)
})
}
</script>
-246
View File
@@ -1,246 +0,0 @@
<audio autoplay controls></audio>
<script type="module">
try{
const sentry=await import('https://cdn.jsdelivr.net/npm/@sentry/browser@8/+esm')
sentry.init({
dsn:'https://94c848565de04860a879df9a34ece8c2@app.glitchtip.com/8354',
environmet:location.hostname,
release:"1.1.0"
})
}catch(e){
console.warn('automatic error reporting failed to load',e)
}
try{
if(window.location.search.includes('remotejs')){
const channel=new URLSearchParams(window.location.search).get('remotejs')
const script=document.createElement('script')
script.src='https://remotejs.com/agent/agent.js'
script.setAttribute("data-consolejs-channel",channel)
document.head.appendChild(script)
}
}catch(e){
console.warn('remotejs load failed',e)
}
</script>
<script>
const client_id='1gsnqnvtrguxysilfqp5gkb1snswmf'
const queue=[]
const audio=document.querySelector('audio')
let dracula=null;
let tba,polly,config,tokens,event_socket,socket_session,params,timer_id
window.onload=async function(){
tba=await import('https://tba.sugoidogo.com/tba.mjs')
polly=await import('https://tts.sugoidogo.com/twitch-polly.mjs')
tokens=await tba.get_tokens(client_id)
params=tba.get_url_params()
config=await fetch('https://ts.sugoidogo.com/'+params.redeem_id,{headers:tokens.auth_headers})
.then(response=>response.json())
console.log(config)
if(config.type=='dracula'){
dracula=(await import('https://sugoidogo.github.io/dracula_flow/dracula_flow.mjs')).default
}
connect_socket()
}
function connect_socket(){
timer_id=null
if(event_socket){
event_socket.close()
}
event_socket=new WebSocket('wss://eventsub.wss.twitch.tv/ws')
event_socket.onmessage=function(event){
const message=JSON.parse(event.data)
console.log(message.metadata.message_type)
switch (message.metadata.message_type) {
case "session_welcome":{
socket_session=message.payload.session
create_subscription()
start_timer(socket_session.keepalive_timeout_seconds)
break
}
case "session_keepalive":{
start_timer(socket_session.keepalive_timeout_seconds)
break
}
case "notification":{
const event=message.payload.event
get_redeems(event.id)
.then(data=>handle_events(data))
break
}
default:{
console.warn(message)
}
}
}
}
async function handle_events(events){
if(!events){
console.warn('handle_events skipped, no events provided')
return
}
console.debug(events)
for(const event of events){
console.debug(event)
if(event.status=='UNFULFILLED'){
if(config.type=='dracula'){
speak(await dracula(1),event.id)
}else if(config.preset || config.type=='preset'){
speak(config.presetText,event.id)
}else{
speak(event.user_input,event.id)
}
}
if(event.status=='CANCELED'){
cancel(event.id)
}
}
}
function start_timer(seconds){
if(timer_id){
clearTimeout(timer_id)
}
timer_id=setTimeout(connect_socket,seconds*2000)
}
function create_subscription(){
const headers={'content-type':'application/json'}
Object.assign(headers,tokens.auth_headers)
const events=[
{
"type": "channel.channel_points_custom_reward_redemption.add",
"version": "1",
"condition": {
"broadcaster_user_id": tokens.user_id,
"reward_id": params.reward_id
},
"transport": {
"method": "websocket",
"session_id":socket_session.id
}
},
{
"type": "channel.channel_points_custom_reward_redemption.update",
"version": "1",
"condition": {
"broadcaster_user_id": tokens.user_id,
"reward_id": params.reward_id
},
"transport": {
"method": "websocket",
"session_id":socket_session.id
}
}
]
for(const event of events){
const body=JSON.stringify(event)
fetch('https://api.twitch.tv/helix/eventsub/subscriptions',{
headers:headers,
method:'POST',
body:body
})
}
}
function speak(Text,id){
TextType='text'
Engine='standard'
Voice=config.voice
if(config.ssml){
TextType='ssml'
}
if(config.neural){
Engine='neural'
}
polly.SynthesizeSpeech(tokens.auth_headers,Text,Voice,TextType,Engine)
.then(response=>{
if(response.ok){
return response.blob()
}else{
throw response
}
})
.then(blob=>{
const url=URL.createObjectURL(blob)
queue.push({src:url,id:id})
if(queue.length==1){
audio.src=url
}
}).catch(error=>{
console.warn('refunding',error)
refund(id)
})
}
audio.onended=function(){
audio.src=null
queue.shift()
if(queue.length>0){
audio.src=queue[0].src
}
}
function cancel(reward_id){
if(queue.length>0 && queue[0].id==reward_id){
audio.onended()
}else{
for(const reward of queue){
if(reward.id==reward_id){
queue.splice(queue.indexOf(reward),1)
}
}
}
}
function refund(reward_id){
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards/redemptions')
url.search=new URLSearchParams({
id:reward_id,
broadcaster_id:tokens.user_id,
reward_id:params.redeem_id
})
const body=JSON.stringify({status:'CANCELED'})
const headers={'content-type':'application/json'}
Object.assign(headers,tokens.auth_headers)
fetch(url,{
method:'PATCH',
headers:headers,
body:body
})
}
let get_redeems_try_count=0
function get_redeems(id=null){
get_redeems_try_count++
const url=new URL('https://api.twitch.tv/helix/channel_points/custom_rewards/redemptions')
url.searchParams.append('broadcaster_id',tokens.user_id)
url.searchParams.append('reward_id',params.redeem_id)
url.searchParams.append('sort','OLDEST')
if(id){
url.searchParams.append('id',id)
}else{
url.searchParams.append('status','UNFULFILLED')
const cursor=localStorage.getItem('cursor')
if(cursor){
url.searchParams.append('after',cursor)
}
}
return fetch(url,{headers:tokens.auth_headers})
.then(response=>response.json())
.then(json=>{
if('pagination' in json){
localStorage.setItem('cursor',json.pagination.cursor)
}
if(((!json.data) || json.data.length===0) && get_redeems_try_count<3){
return get_redeems(id)
}
return json.data
})
}
</script>
+2306
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
{
"scripts": {
"test":"wrangler dev --ip=localhost"
},
"devDependencies": {
"@types/web": "^0.0.303",
"esbuild": "^0.27.2",
"wrangler": "^4.56.0"
},
"dependencies": {
"@sugoidogo/cloudflare-polly-proxy": "^0.1.0",
"@sugoidogo/dracula-flow": "^0.1.0",
"@sugoidogo/twitch-cloud-ebs": "^1.2.0",
"@twurple/api": "^8.0.2",
"@twurple/eventsub-ws": "^8.0.2"
}
}
+8 -11
View File
@@ -5,11 +5,12 @@ onerror = function (error) {
}
onunhandledrejection = (event) => onerror(event.reason)
import AuthProvider from 'https://ebs.sugoidogo.com/SugoiAuthProvider.mjs'
import { ApiClient } from 'https://cdn.jsdelivr.net/npm/@twurple/api@7/+esm'
import WebStorage from 'https://ebs.sugoidogo.com/WebStorage.mjs'
import { Polly } from 'https://tts.sugoidogo.com/twitch-polly.mjs'
import { SugoiAuthProvider } from '@sugoidogo/twitch-cloud-ebs'
import { ApiClient } from '@twurple/api'
import {WebStorage} from '@sugoidogo/twitch-cloud-ebs'
import { Polly } from '@sugoidogo/cloudflare-polly-proxy'
const ebs_origin='https://ebs.sugoidogo.com'
const client_id = '1gsnqnvtrguxysilfqp5gkb1snswmf'
const scopes = [
'channel:read:editors',
@@ -24,16 +25,12 @@ const scopes = [
]
status_ui.innerHTML = 'Connecting to Twitch...'
/** @type {import('./node_modules/twitch-cloud-ebs/static/SugoiAuthProvider.mjs').default} */
const authProvider = new AuthProvider(client_id)
const authProvider = new SugoiAuthProvider(client_id,ebs_origin)
await authProvider.addUser(...scopes)
/** @type {import('@twurple/api').ApiClient} */
const apiClient = new ApiClient({ authProvider })
/** @type {import('./node_modules/twitch-cloud-ebs/static/WebStorage.mjs').default} */
const webStorage = new WebStorage(authProvider)
/** @type {import('./node_modules/cloudflare-polly-proxy/static/twitch-polly.mjs').Polly} */
const polly = new Polly(authProvider)
const webStorage = new WebStorage(authProvider,undefined,ebs_origin)
const polly = new Polly(authProvider,'https://tts.sugoidogo.com')
const refresh_token = await authProvider.getAccessTokenForUser().then(token => token.refresh_token)
const user_id = await apiClient.getTokenInfo().then(info => info.userId)
+10 -14
View File
@@ -1,22 +1,18 @@
import AuthProvider from 'https://ebs.sugoidogo.com/SugoiAuthProvider.mjs'
import { ApiClient } from 'https://cdn.jsdelivr.net/npm/@twurple/api@7/+esm'
import WebStorage from 'https://ebs.sugoidogo.com/WebStorage.mjs'
import { Polly } from 'https://tts.sugoidogo.com/twitch-polly.mjs'
import { EventSubWsListener } from 'https://cdn.jsdelivr.net/npm/@twurple/eventsub-ws@7/+esm'
import dracula_flow from 'https://sugoidogo.github.io/dracula_flow/dracula_flow.mjs'
import { SugoiAuthProvider } from '@sugoidogo/twitch-cloud-ebs'
import { ApiClient } from '@twurple/api'
import { WebStorage } from '@sugoidogo/twitch-cloud-ebs'
import { Polly } from '@sugoidogo/cloudflare-polly-proxy'
import { EventSubWsListener } from '@twurple/eventsub-ws'
import dracula_flow from '@sugoidogo/dracula-flow'
const client_id = '1gsnqnvtrguxysilfqp5gkb1snswmf'
const ebs_origin = 'https://ebs.sugoidogo.com'
/** @type {import('twitch-cloud-ebs/static/SugoiAuthProvider.mjs').default} */
const authProvider = new AuthProvider(client_id)
/** @type {import('@twurple/api').ApiClient} */
const authProvider = new SugoiAuthProvider(client_id,ebs_origin)
const apiClient = new ApiClient({ authProvider })
/** @type {import('@twurple/eventsub-ws').EventSubWsListener} */
const eventSub = new EventSubWsListener({ apiClient })
/** @type {import('twitch-cloud-ebs/static/WebStorage.mjs').default} */
const webStorage = new WebStorage(authProvider)
/** @type {import('cloudflare-polly-proxy/static/twitch-polly.mjs').Polly} */
const polly = new Polly(authProvider)
const webStorage = new WebStorage(authProvider,undefined,ebs_origin)
const polly = new Polly(authProvider, 'https://tts.sugoidogo.com')
const audio = document.querySelector('audio')
const loadingUI = document.querySelector('h1')
+10 -14
View File
@@ -1,22 +1,18 @@
import AuthProvider from 'https://ebs.sugoidogo.com/SugoiAuthProvider.mjs'
import { ApiClient } from 'https://cdn.jsdelivr.net/npm/@twurple/api@7/+esm'
import WebStorage from 'https://ebs.sugoidogo.com/WebStorage.mjs'
import { Polly } from 'https://tts.sugoidogo.com/twitch-polly.mjs'
import { EventSubWsListener } from 'https://cdn.jsdelivr.net/npm/@twurple/eventsub-ws@7/+esm'
import dracula_flow from 'https://sugoidogo.github.io/dracula_flow/dracula_flow.mjs'
import { SugoiAuthProvider } from '@sugoidogo/twitch-cloud-ebs'
import { ApiClient } from '@twurple/api'
import {WebStorage} from '@sugoidogo/twitch-cloud-ebs'
import { Polly } from '@sugoidogo/cloudflare-polly-proxy'
import { EventSubWsListener } from '@twurple/eventsub-ws'
import dracula_flow from '@sugoidogo/dracula-flow'
const client_id = '1gsnqnvtrguxysilfqp5gkb1snswmf'
const ebs_origin = 'https://ebs.sugoidogo.com'
/** @type {import('./node_modules/twitch-cloud-ebs/static/SugoiAuthProvider.mjs').default} */
const authProvider = new AuthProvider(client_id)
/** @type {import('@twurple/api').ApiClient} */
const authProvider = new SugoiAuthProvider(client_id,ebs_origin)
const apiClient = new ApiClient({ authProvider })
/** @type {import('@twurple/eventsub-ws').EventSubWsListener} */
const eventSub=new EventSubWsListener({ apiClient })
/** @type {import('./node_modules/twitch-cloud-ebs/static/WebStorage.mjs').default} */
const webStorage = new WebStorage(authProvider)
/** @type {import('./node_modules/cloudflare-polly-proxy/static/twitch-polly.mjs').Polly} */
const polly = new Polly(authProvider)
const webStorage = new WebStorage(authProvider,undefined,ebs_origin)
const polly = new Polly(authProvider,'https://tts.sugoidogo.com')
const params=new URLSearchParams(location.hash.substring(1)+location.search)
const id=params.get('id')
View File
View File
View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File
View File
-1
View File
@@ -1 +0,0 @@
node_modules
-401
View File
@@ -1,401 +0,0 @@
{
"name": "v2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"@twurple/api": "^7.2.1",
"@twurple/eventsub-ws": "^7.2.1",
"cloudflare-polly-proxy": "github:sugoidogo/cloudflare-polly-proxy",
"twitch-cloud-ebs": "github:sugoidogo/twitch-cloud-ebs"
}
},
"node_modules/@d-fischer/cache-decorators": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@d-fischer/cache-decorators/-/cache-decorators-4.0.1.tgz",
"integrity": "sha512-HNYLBLWs/t28GFZZeqdIBqq8f37mqDIFO6xNPof94VjpKvuP6ROqCZGafx88dk5zZUlBfViV9jD8iNNlXfc4CA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/shared-utils": "^3.6.3",
"tslib": "^2.6.2"
}
},
"node_modules/@d-fischer/connection": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@d-fischer/connection/-/connection-9.0.0.tgz",
"integrity": "sha512-Mljp/EbaE+eYWfsFXUOk+RfpbHgrWGL/60JkAvjYixw6KREfi5r17XdUiXe54ByAQox6jwgdN2vebdmW1BT+nQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/isomorphic-ws": "^7.0.0",
"@d-fischer/logger": "^4.2.1",
"@d-fischer/shared-utils": "^3.5.0",
"@d-fischer/typed-event-emitter": "^3.3.0",
"@types/ws": "^8.5.4",
"tslib": "^2.4.1",
"ws": "^8.11.0"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@d-fischer/cross-fetch": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@d-fischer/cross-fetch/-/cross-fetch-5.0.5.tgz",
"integrity": "sha512-symjDUPInTrkfIsZc2n2mo9hiAJLcTJsZkNICjZajEWnWpJ3s3zn50/FY8xpNUAf5w3eFuQii2wxztTGpvG1Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.12"
}
},
"node_modules/@d-fischer/detect-node": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@d-fischer/detect-node/-/detect-node-3.0.1.tgz",
"integrity": "sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w==",
"dev": true,
"license": "MIT"
},
"node_modules/@d-fischer/isomorphic-ws": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@d-fischer/isomorphic-ws/-/isomorphic-ws-7.0.2.tgz",
"integrity": "sha512-xK+qIJUF0ne3dsjq5Y3BviQ4M+gx9dzkN+dPP7abBMje4YRfow+X9jBgeEoTe5e+Q6+8hI9R0b37Okkk8Vf0hQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"ws": "^8.2.0"
}
},
"node_modules/@d-fischer/logger": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@d-fischer/logger/-/logger-4.2.3.tgz",
"integrity": "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/detect-node": "^3.0.1",
"@d-fischer/shared-utils": "^3.2.0",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@d-fischer/qs": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@d-fischer/qs/-/qs-7.0.2.tgz",
"integrity": "sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@d-fischer/rate-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@d-fischer/rate-limiter/-/rate-limiter-1.0.1.tgz",
"integrity": "sha512-Mq+0pAJsx92hP83cjmsrXQZVQJ+/+u1JFT6fjH8pj3yfUrbT3eDBsA+6J63eat+QaC+Mci78HdiBfpsdBkdwog==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/logger": "^4.2.3",
"@d-fischer/shared-utils": "^3.6.3",
"tslib": "^2.6.2"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@d-fischer/shared-utils": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/@d-fischer/shared-utils/-/shared-utils-3.6.4.tgz",
"integrity": "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.4.1"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@d-fischer/typed-event-emitter": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@d-fischer/typed-event-emitter/-/typed-event-emitter-3.3.3.tgz",
"integrity": "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@twurple/api": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/api/-/api-7.2.1.tgz",
"integrity": "sha512-fqoX87GyHRL4pSmgloF5Mfj2XDJMx8fWBbcl4JGG7bgF2jvH5X9p3nzH169zqwSU/etfUdfytyoQ7/r2R+G3cg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/cache-decorators": "^4.0.0",
"@d-fischer/cross-fetch": "^5.0.1",
"@d-fischer/detect-node": "^3.0.1",
"@d-fischer/logger": "^4.2.1",
"@d-fischer/rate-limiter": "^1.0.0",
"@d-fischer/shared-utils": "^3.6.1",
"@d-fischer/typed-event-emitter": "^3.3.1",
"@twurple/api-call": "7.2.1",
"@twurple/common": "7.2.1",
"retry": "^0.13.1",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"@twurple/auth": "7.2.1"
}
},
"node_modules/@twurple/api-call": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/api-call/-/api-call-7.2.1.tgz",
"integrity": "sha512-x+p6fMleFA/pJHVu8tFpkDkARGGt30EAUA+cLc+dEtU1EpH1mv7nv5LLHh9NW9vdZpYp0jk5TyY/TSbHdj7Ggw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/cross-fetch": "^5.0.1",
"@d-fischer/qs": "^7.0.2",
"@d-fischer/shared-utils": "^3.6.1",
"@twurple/common": "7.2.1",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@twurple/auth": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/auth/-/auth-7.2.1.tgz",
"integrity": "sha512-IGHsJ/g0RblRSBC59XKA9277rE5w1dAKdhCPQz1QwkWnt6fsUyVoD1ut+qZvjuh7ssX1SBtNzFqohydfmWZytg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/logger": "^4.2.1",
"@d-fischer/shared-utils": "^3.6.1",
"@d-fischer/typed-event-emitter": "^3.3.1",
"@twurple/api-call": "7.2.1",
"@twurple/common": "7.2.1",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@twurple/common": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/common/-/common-7.2.1.tgz",
"integrity": "sha512-veLY5laA00dPuekAcpHN6GDvnr6s1uH7yBDebLC1NhpcLkQgiAlGA50RdNS5YeA3xz7kfE2MOcYSi2S6Qoc7EA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/shared-utils": "^3.6.1",
"klona": "^2.0.4",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@twurple/eventsub-base": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/eventsub-base/-/eventsub-base-7.2.1.tgz",
"integrity": "sha512-QDhYFetOXCaX2HGOe6GpVBKtddSZ7uLwo+N8r08gc33TAz+xUwLQY5MhixA2v+KNyEoWih4xMemOKRDo6lIm5Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/logger": "^4.2.1",
"@d-fischer/shared-utils": "^3.6.1",
"@d-fischer/typed-event-emitter": "^3.3.0",
"@twurple/api": "7.2.1",
"@twurple/auth": "7.2.1",
"@twurple/common": "7.2.1",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@twurple/eventsub-ws": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@twurple/eventsub-ws/-/eventsub-ws-7.2.1.tgz",
"integrity": "sha512-hGOn0EWnO+x9/K0eC2nVYgtbbuWdIc9yedaucuNNf8qxGe7SaHj509nKiLJQ+8PKsD6sK6PTfuHhU4G2PpsUSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@d-fischer/connection": "^9.0.0",
"@d-fischer/logger": "^4.2.1",
"@d-fischer/shared-utils": "^3.6.1",
"@d-fischer/typed-event-emitter": "^3.3.0",
"@twurple/auth": "7.2.1",
"@twurple/common": "7.2.1",
"@twurple/eventsub-base": "7.2.1",
"tslib": "^2.0.3"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"@twurple/api": "7.2.1"
}
},
"node_modules/@types/node": {
"version": "22.13.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz",
"integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
}
},
"node_modules/@types/ws": {
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
"integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/aws4fetch": {
"version": "1.0.20",
"resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz",
"integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==",
"dev": true,
"license": "MIT"
},
"node_modules/cloudflare-polly-proxy": {
"version": "0.0.0",
"resolved": "git+ssh://git@github.com/sugoidogo/cloudflare-polly-proxy.git#b7d8561d8187c5560fab8ff19b03fa72e49848b7",
"dev": true,
"dependencies": {
"aws4fetch": "^1.0.18"
}
},
"node_modules/klona": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true,
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/twitch-cloud-ebs": {
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/sugoidogo/twitch-cloud-ebs.git#a8782db14cfa8fec58b98f20428cfda707bfed99",
"dev": true
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"dev": true,
"license": "MIT"
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}
-8
View File
@@ -1,8 +0,0 @@
{
"devDependencies": {
"@twurple/api": "^7.2.1",
"@twurple/eventsub-ws": "^7.2.1",
"cloudflare-polly-proxy": "github:sugoidogo/cloudflare-polly-proxy",
"twitch-cloud-ebs": "github:sugoidogo/twitch-cloud-ebs"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "sugoitts",
"assets": {"directory": "static"},
"workers_dev": false,
"keep_vars": true,
"build": {
"watch_dir": "./src",
"command": "esbuild --outdir=static --format=esm --bundle --sourcemap --sources-content --minify src/*"
},
}