From 1eae3a8bbf81e51d19e7cf45e003493d81ed1580 Mon Sep 17 00:00:00 2001 From: Arindy Date: Wed, 12 Feb 2025 15:48:24 +0100 Subject: [PATCH] creates chatoverlay --- pom.xml | 2 +- .../arindy/dicetower/ChatOverlayResource.kt | 40 +++++ .../kotlin/de/arindy/dicetower/Templates.kt | 20 +++ src/main/resources/META-INF/resources/app.js | 3 + .../META-INF/resources/overlay/style.css | 10 ++ .../resources/vendor/comfy.js/comfy.min.js | 1 + src/main/resources/templates/chatoverlay.html | 70 +++++++++ .../templates/chatoverlayconfig.html | 142 ++++++++++++++++++ src/main/resources/templates/index.html | 9 +- 9 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt create mode 100644 src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js create mode 100644 src/main/resources/templates/chatoverlay.html create mode 100644 src/main/resources/templates/chatoverlayconfig.html diff --git a/pom.xml b/pom.xml index 77ee31e..165bed2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.arindy dice-tower - 1.0.8-SNAPSHOT + 1.1.0-SNAPSHOT 3.13.0 diff --git a/src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt b/src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt new file mode 100644 index 0000000..2dd5d2d --- /dev/null +++ b/src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt @@ -0,0 +1,40 @@ +package de.arindy.dicetower + +import io.quarkus.qute.TemplateInstance +import jakarta.ws.rs.GET +import jakarta.ws.rs.Path +import jakarta.ws.rs.PathParam +import jakarta.ws.rs.Produces +import jakarta.ws.rs.QueryParam +import jakarta.ws.rs.core.MediaType + +@Path("chatoverlay") +class ChatOverlayResource { + + @GET + @Path("/{channel}") + @Produces(MediaType.TEXT_HTML) + fun get( + @PathParam("channel") channel: String, + @QueryParam("scale") scale: Int? = 7, + @QueryParam("maxDice") maxDice: Int? = 20, + @QueryParam("modsAllowed") modsAllowed: Boolean = false, + @QueryParam("vipAllowed") vipAllowed: Boolean = false, + @QueryParam("subsAllowed") subsAllowed: Boolean = false, + @QueryParam("cmd") cmd: String? = "roll", + @QueryParam("theme") theme: String? = "default", + @QueryParam("themeColor") themeColor: String? = "default", + @QueryParam("clearAfter") clearAfter: Long? = -1, + @QueryParam("timeout") timeout: Long? = -1 + ): TemplateInstance { + return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, cmd ?: "roll", theme ?: "default", themeColor ?: "ff0202", clearAfter ?: 10, timeout ?: 60) + } + + @GET + @Produces(MediaType.TEXT_HTML) + fun config( + ): TemplateInstance { + return Templates.chatoverlayconfig() + } + +} diff --git a/src/main/kotlin/de/arindy/dicetower/Templates.kt b/src/main/kotlin/de/arindy/dicetower/Templates.kt index d21223f..1bdb5cf 100644 --- a/src/main/kotlin/de/arindy/dicetower/Templates.kt +++ b/src/main/kotlin/de/arindy/dicetower/Templates.kt @@ -7,8 +7,28 @@ import io.quarkus.qute.TemplateInstance object Templates { @JvmStatic external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance + @JvmStatic external fun results(room: String, name: String?, user: String?): TemplateInstance + @JvmStatic external fun index(version: String): TemplateInstance + + @JvmStatic + external fun chatoverlayconfig(): TemplateInstance + + @JvmStatic + external fun chatoverlay( + channel: String, + scale: Int?, + maxDice: Int?, + modsAllowed: Boolean, + vipAllowed: Boolean, + subsAllowed: Boolean, + cmd: String?, + theme: String?, + themeColor: String?, + clearAfter: Long?, + timeout: Long? + ): TemplateInstance } diff --git a/src/main/resources/META-INF/resources/app.js b/src/main/resources/META-INF/resources/app.js index b9b0de0..61bf66d 100644 --- a/src/main/resources/META-INF/resources/app.js +++ b/src/main/resources/META-INF/resources/app.js @@ -27,6 +27,7 @@ function start(event = undefined) { document.getElementById('name').hidden = true; document.getElementById('room').hidden = true; document.getElementById('how-to').hidden = true; + document.getElementById('chatOverlay').hidden = true; document.getElementById('results').hidden = false; document.getElementById('all-results').hidden = !document.getElementById('gm').checked; document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none'; @@ -178,6 +179,8 @@ document.addEventListener("DOMContentLoaded", async () => { } }) + document.getElementById('chatOverlayLink').href = url() + '/chatoverlay' + document.addEventListener("DOMContentLoaded", async () => { if (!localStorage.getItem("userId")) { localStorage.setItem("userId", self.crypto.randomUUID()); diff --git a/src/main/resources/META-INF/resources/overlay/style.css b/src/main/resources/META-INF/resources/overlay/style.css index 250f390..b001e56 100644 --- a/src/main/resources/META-INF/resources/overlay/style.css +++ b/src/main/resources/META-INF/resources/overlay/style.css @@ -24,3 +24,13 @@ body { width: 100%; height: 100%; } + +.tooltip { + position: fixed; + border-radius: 0.5rem; + padding: 15px; + max-width: 80%; + font-size: 400%; + color: #fff; !important; + background-color: #333333dd; !important +} diff --git a/src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js b/src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js new file mode 100644 index 0000000..14fdfd2 --- /dev/null +++ b/src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js @@ -0,0 +1 @@ +!function e(t,n,s){function o(r,a){if(!n[r]){if(!t[r]){var c="function"==typeof require&&require;if(!a&&c)return c(r,!0);if(i)return i(r,!0);var l=new Error("Cannot find module '"+r+"'");throw l.code="MODULE_NOT_FOUND",l}var u=n[r]={exports:{}};t[r][0].call(u.exports,(function(e){return o(t[r][1][e]||e)}),u,u.exports,e,t,n,s)}return n[r].exports}for(var i="function"==typeof require&&require,r=0;re.json()).catch(e=>(t=e,null));return t||null===n?(console.error("Error fetching validation: ",t),null):n.client_id?n:(console.error("Invalid Password"),null)}(t);if(!n){if(null===h)return;n=h.client_id}if(!s&&null===(s=await async function(e,t,n){let s,i=await o("https://api.twitch.tv/helix/users?login="+e,{headers:{"Client-ID":t,Authorization:"Bearer "+n}}).then(e=>e.json()).catch(e=>(s=e,null));return s||null===i?(console.error("Error fetching user info: ",s),null):i.data[0].id}(e,n,t)))return;r||(r="wss://eventsub.wss.twitch.tv/ws");const d="undefined"!=typeof window?new WebSocket(r):new i(r);return(c=c||{}).onDisconnect=(o=!0)=>{clearTimeout(null),d.close(),o?l(e,t,n,s,r,a,c):console.log("Disconnected from EventSub")},c.messages=c.messages||{},d.onerror=function(e){console.error(e),c.onDisconnect(!1)},d.onopen=function(e){b&&b.isDebug&&console.log("Connected to EventSub")},d.onmessage=function(e){try{const i=JSON.parse(e.data);if("PING"===i.type)return void d.send(JSON.stringify({type:"PONG"}));switch(i.metadata.message_type){case"session_welcome":{a=i.payload.session.id;let e=h.scopes.map(e=>u[e]).filter(e=>!!e);e=e.filter((t,n)=>e.indexOf(t)===n);const r=e.map(e=>m[e]).flat();Promise.all(r.map(([e,i])=>async function(e,t,n,s,i,r,a){try{await o("https://api.twitch.tv/helix/eventsub/subscriptions",{method:"POST",headers:{"Client-ID":n,Authorization:"Bearer "+i,"Content-Type":"application/json"},body:JSON.stringify({type:e,version:t,condition:{moderator_user_id:s,user_id:s,broadcaster_user_id:r},transport:{method:"websocket",session_id:a}})}).then(e=>e.text());return!0}catch(e){console.error(e)}return!1}(e,i,n,h.user_id,t,s,a)));break}case"session_keepalive":console.debug("Keepalive received");break;case"session_reconnect":r=i.payload.session.reconnect_url,clearTimeout(null),c.onDisconnect();break;case"revocation":if(!subscribtions.map(([e,t])=>e).includes(i.payload.type))break;c.onDisconnect(!1);break;case"notification":{const e=i.metadata.message_id;if(c.messages[e])break;switch(c.messages[e]=!0,setTimeout(()=>delete c.messages[e],3e4),i.payload.subscription.type){case"channel.channel_points_custom_reward_redemption.add":case"channel.channel_points_automatic_reward_redemption.add":{const e=i.payload.event,t=e.reward,n={id:t.id,channelId:s,title:"title"in t?t.title:null,prompt:"prompt"in t?t.prompt:null,message:e.user_input||"",cost:t.cost},o={channelId:e.broadcaster_user_id,channelName:e.broadcaster_user_login,channelDisplayName:e.broadcaster_user_name,reward:n,rewardFulfilled:"channel.channel_points_automatic_reward_redemption.add"===i.payload.subscription.type||"fulfilled"===e.status.toLowerCase(),userId:e.user_id,username:e.user_login,displayName:e.user_name,customRewardId:e.id,redeemed_at:e.redeemed_at};b.onReward(o.displayName||o.username,n.title,n.cost,n.message,o)}break;case"channel.hype_train.begin":case"channel.hype_train.progress":case"channel.hype_train.end":{const e=i.payload.event,t={id:e.id,channelId:e.broadcaster_user_id,channelName:e.broadcaster_user_login,channelDisplayName:e.broadcaster_user_name,level:e.level,progressToNextLevel:e.progress,goalToNextLevel:e.goal,totalHype:e.total,isGoldenKappaTrain:e.is_golden_kappa_train,hypeEvent:e};switch(i.payload.subscription.type){case"channel.hype_train.begin":b.onHypeTrain("begin",e.level,e.progress||0,e.goal,e.total,t);break;case"channel.hype_train.progress":b.onHypeTrain("progress",e.level,e.progress||0,e.goal,e.total,t);break;case"channel.hype_train.end":b.onHypeTrain("end",e.level,e.progress||0,e.goal,e.total,t)}}break;case"channel.shoutout":{const e=i.payload.event;console.log("shoutout",e);const t={channelId:e.broadcaster_user_id,channelName:e.broadcaster_user_login,channelDisplayName:e.broadcaster_user_name,shoutoutUser:e.shoutout_user_login,shoutoutDisplayName:e.shoutout_user_name,shoutoutChannelId:e.shoutout_user_id,shoutoutChannelName:e.shoutout_user_login,shoutoutChannelDisplayName:e.shoutout_user_name};b.onShoutout(t)}}break}}}catch(e){console.error(e)}},()=>c.onDisconnect(!1)}async function u(e,t){let n;t=t.replace("oauth:","");let s=await o("https://id.twitch.tv/oauth2/validate",{headers:{Authorization:"OAuth "+t}}).then(e=>e.json());if(!s.client_id||!s.scopes.includes("channel:read:redemptions")||!s.scopes.includes("user:read:email"))return void console.error("Invalid Password or Permission Scopes (channel:read:redemptions, user:read:email)");let r,a=(await o("https://api.twitch.tv/helix/users?login="+e,{headers:{"Client-ID":s.client_id,Authorization:"Bearer "+t}}).then(e=>e.json())).data[0].id;r="undefined"!=typeof window?new WebSocket("wss://pubsub-edge.twitch.tv"):new i("wss://pubsub-edge.twitch.tv"),r.onopen=function(e){r.send(JSON.stringify({type:"PING"})),n=setInterval(()=>{r.send(JSON.stringify({type:"PING"}))},6e4);let s={type:"LISTEN",nonce:c(15),data:{topics:["channel-points-channel-v1."+a],auth_token:t}};r.send(JSON.stringify(s))},r.onerror=function(e){console.error(e)},r.onmessage=function(n){switch(message=JSON.parse(n.data),message.type){case"RESPONSE":"ERR_BADAUTH"===message.error&&console.error("PubSub Authentication Failure");break;case"RECONNECT":setTimeout(()=>{u(e,t)},3e3);break;case"MESSAGE":if(message.data.topic.startsWith("channel-points-channel")){let e=JSON.parse(message.data.message);if("reward-redeemed"===e.type){let t=e.data.redemption;var s=t.reward,o={id:s.id,channelId:s.channel_id,title:s.title,prompt:s.prompt,cost:s.cost,userInputRequired:s.is_user_input_required,subOnly:s.is_sub_only,image:{url1x:s.image?s.image.url_1x:null,url2x:s.image?s.image.url_2x:null,url4x:s.image?s.image.url_4x:null},defaultImage:{url1x:s.default_image.url_1x,url2x:s.default_image.url_2x,url4x:s.default_image.url_4x},backgroundColor:s.background_color,enabled:s.is_enabled,paused:s.is_paused,inStock:s.is_in_stock,maxPerStream:{enabled:s.max_per_stream.is_enabled,maxPerStream:s.max_per_stream.max_per_stream},shouldRedemptionsSkipRequestQueue:s.should_redemptions_skip_request_queue,templateId:s.template_id,updatedForIndicatorAt:s.updated_for_indicator_at,maxPerUserPerStream:{enabled:s.max_per_user_per_stream.is_enabled,maxPerUserPerStream:s.max_per_user_per_stream.max_per_user_per_stream},globalCooldown:{enabled:s.global_cooldown.is_enabled,globalCooldownSeconds:s.global_cooldown.global_cooldown_seconds},redemptionsRedeemedCurrentStream:s.redemptions_redeemed_current_stream,cooldownExpiresAt:s.cooldown_expires_at},i={channelId:t.channel_id,reward:o,rewardFulfilled:"FULFILLED"===t.status,userId:t.user.id,username:t.user.login,displayName:t.user.display_name,customRewardId:t.id,timestamp:t.redeemed_at};b.onReward(t.user.display_name||t.user.login,t.reward.title,t.reward.cost,t.user_input||"",i)}}}},r.onclose=function(){clearInterval(n),setTimeout(()=>{u(e,t)},3e3)}}var m="",h="",d=null,f=null,p=!0,g=0,_=null,b={isDebug:!1,useEventSub:!0,chatModes:{},version:function(){return"1.1.24"},onError:function(e){console.error("Error:",e)},onCommand:function(e,t,n,s,o){b.isDebug&&console.log("onCommand default handler")},onChat:function(e,t,n,s,o){b.isDebug&&console.log("onChat default handler")},onWhisper:function(e,t,n,s,o){b.isDebug&&console.log("onWhisper default handler")},onMessageDeleted:function(e,t){b.isDebug&&console.log("onMessageDeleted default handler")},onBan:function(e,t){b.isDebug&&console.log("onBan default handler")},onTimeout:function(e,t,n){b.isDebug&&console.log("onTimeout default handler")},onJoin:function(e,t,n){b.isDebug&&console.log("onJoin default handler")},onPart:function(e,t,n){b.isDebug&&console.log("onPart default handler")},onHosted:function(e,t,n,s){b.isDebug&&console.log("onHosted default handler")},onRaid:function(e,t,n){b.isDebug&&console.log("onRaid default handler")},onSub:function(e,t,n,s){b.isDebug&&console.log("onSub default handler")},onResub:function(e,t,n,s,o,i){b.isDebug&&console.log("onResub default handler")},onSubGift:function(e,t,n,s,o,i){b.isDebug&&console.log("onSubGift default handler")},onSubMysteryGift:function(e,t,n,s,o){b.isDebug&&console.log("onSubMysteryGift default handler")},onGiftSubContinue:function(e,t,n){b.isDebug&&console.log("onGiftSubContinue default handler")},onCheer:function(e,t,n,s,o){b.isDebug&&console.log("onCheer default handler")},onChatMode:function(e,t){b.isDebug&&console.log("onChatMode default handler")},onReward:function(e,t,n,s,o){b.isDebug&&console.log("onReward default handler")},onConnected:function(e,t,n){},onReconnect:function(e){},Say:function(e,t){return!!f&&(t||(t=m),f.say(t,e).catch(b.onError),!0)},Reply:function(e,t,n){if(f){n||(n=m);const s=`@reply-parent-msg-id=${e} PRIVMSG #${n} :${t}`;return f.ws.send(s),!0}return!1},Whisper:function(e,t){return!!f&&(f.whisper(t,e).catch(b.onError),!0)},Announce:function(e,t,n=null){return!!f&&(t||(t=m),f.say(t,"/announce "+e).catch(b.onError),!0)},DeleteMessage:function(e,t){return!!f&&(t||(t=m),f.deletemessage(t,e).catch(b.onError),!0)},GetClient:function(){return f},Init:function(e,t,n,o){if(("string"==typeof(n=n||[e])||n instanceof String)&&(n=[n]),!Array.isArray(n))throw new Error("Channels is not an array");b.isDebug=o,_=null,m=n[0];var i={options:{debug:o,skipUpdatingEmotesets:!0},connection:{reconnect:!0,secure:!0},channels:n};t&&(i.identity={username:e,password:t},h=t),(f=new s.client(i)).on("roomstate",(function(e,t){try{var n=e.replace("#","");b.chatModes[n]=b.chatModes[n]||{},"emote-only"in t&&(b.chatModes[n].emoteOnly=t["emote-only"]),"followers-only"in t&&(b.chatModes[n].followerOnly=t["followers-only"]>=0),"subs-only"in t&&(b.chatModes[n].subOnly=t["subs-only"]),"r9k"in t&&(b.chatModes[n].r9kMode=t.r9k),"slow"in t&&(b.chatModes[n].slowMode=t.slow),b.onChatMode(b.chatModes[n],n)}catch(e){b.onError(e)}})),f.on("message",(function(t,n,s,o){try{var i=n["display-name"]||n.username||e,r="#"+n.username===t,c=n.mod,l=n.badges&&"0"===n.badges.founder,u=l||n.badges&&void 0!==n.badges.subscriber||n.subscriber,m=n.badges&&"1"===n.badges.vip||!1,h="highlighted-message"===n["msg-id"],d=n["user-id"],f=n.id,p=n["room-id"],g=n.badges,_=n.color,y=n.emotes,v=n.flags,w=n["tmi-sent-ts"],C=n["emote-only"]||!1,k=n["message-type"],E=n["custom-reward-id"]||null,T={broadcaster:r,mod:c,founder:l,subscriber:u||l,vip:m,highlighted:h,customReward:!!E},S={id:f,channel:t.replace("#",""),roomId:p,messageType:k,messageEmotes:y,isEmoteOnly:C,userId:d,username:n.username,displayName:n["display-name"],userColor:_,userBadges:g,userState:n,customRewardId:E,flags:v,timestamp:w};if(o||"!"!==s[0])if(!o&&s.split(" ").length>0&&"@"===s[0]&&"!"===s.split(" ")[1][0]){O=(x=s.split(" "))[1].slice(1).toLowerCase(),I=x.slice(2).join(" ");S.sinceLastCommand=a(O,d),b.onCommand(i,O,I,T,S)}else"action"===k||"chat"===k?b.onChat(i,s,T,o,S):"whisper"===k&&b.onWhisper(i,s,T,o,S);else{var x,O=(x=s.split(/ (.*)/))[0].slice(1).toLowerCase(),I=x[1]||"";S.sinceLastCommand=a(O,d),b.onCommand(i,O,I,T,S)}}catch(e){b.onError(e)}})),f.on("messagedeleted",(function(e,t,n,s){try{var o=s["target-msg-id"],i={id:o,roomId:s["room-id"],username:t,message:n};b.onMessageDeleted(o,i)}catch(e){b.onError(e)}})),f.on("ban",(function(e,t,n,s){try{var o=t,i={roomId:s["room-id"],username:t,bannedUserId:s["target-user-id"]};b.onBan(o,i)}catch(e){b.onError(e)}})),f.on("timeout",(function(e,t,n,s,o){try{var i=t,r=s,a={roomId:o["room-id"],username:t,timedOutUserId:o["target-user-id"]};b.onTimeout(i,r,a)}catch(e){b.onError(e)}})),f.on("join",(function(e,t,n){var s={channel:e.replace("#","")};b.onJoin(t,n,s)})),f.on("part",(function(e,t,n){var s={channel:e.replace("#","")};b.onPart(t,n,s)})),f.on("hosted",(function(e,t,n,s){var o={channel:e.replace("#","")};b.onHosted(t,n,s,o)})),f.on("raided",(function(e,t,n){var s={channel:e.replace("#","")};b.onRaid(t,n,s)})),f.on("cheer",(function(e,t,n){var s=~~t.bits,o=t["room-id"],i=t["display-name"]||t.username||t.login,r=t["user-id"],a="#"+t.username===e,c=t.mod,l=t.badges&&"0"===t.badges.founder,u={broadcaster:a,mod:c,founder:l,subscriber:l||t.badges&&void 0!==t.badges.subscriber||t.subscriber,vip:t.badges&&"1"===t.badges.vip||!1},m={id:t.id,channel:e.replace("#",""),roomId:o,userId:r,username:t.username,userColor:t.color,userBadges:t.badges,userState:t,displayName:t["display-name"],messageEmotes:t.emotes,subscriber:t.subscriber};b.onCheer(i,n,s,u,m)})),f.on("subscription",(function(e,t,n,s,o){var i={id:o.id,roomId:o["room-id"],messageType:o["message-type"],messageEmotes:o.emotes,userId:o["user-id"],username:o.login,displayName:o["display-name"],userColor:o.color,userBadges:o.badges,userState:o,channel:e.replace("#","")};b.onSub(t,s,n,i)})),f.on("resub",(function(e,t,n,s,o,i){var r=~~o["msg-param-cumulative-months"],a={id:o.id,roomId:o["room-id"],messageType:o["message-type"],messageEmotes:o.emotes,userId:o["user-id"],username:o.login,displayName:o["display-name"],userColor:o.color,userBadges:o.badges,channel:e.replace("#","")};b.onResub(t,s,n,r,i,a)})),f.on("subgift",(function(e,t,n,s,o,i){var r=~~i["msg-param-sender-count"],a={id:i.id,roomId:i["room-id"],messageType:i["message-type"],messageEmotes:i.emotes,userId:i["user-id"],username:i.login,displayName:i["display-name"],userColor:i.color,userBadges:i.badges,userState:i,recipientDisplayName:i["msg-param-recipient-display-name"],recipientUsername:i["msg-param-recipient-user-name"],recipientId:i["msg-param-recipient-id"],channel:e.replace("#","")};b.onSubGift(t,n,s,r,o,a)})),f.on("submysterygift",(function(e,t,n,s,o){var i=~~o["msg-param-sender-count"],r={id:o.id,roomId:o["room-id"],messageType:o["message-type"],messageEmotes:o.emotes,userId:o["user-id"],username:o.login,displayName:o["display-name"],userColor:o.color,userBadges:o.badges,userState:o,recipientDisplayName:o["msg-param-recipient-display-name"],recipientUsername:o["msg-param-recipient-user-name"],recipientId:o["msg-param-recipient-id"],userMassGiftCount:~~o["msg-param-mass-gift-count"],channel:e.replace("#","")};b.onSubMysteryGift(t,n,i,s,r)})),f.on("giftpaidupgrade",(function(e,t,n,s){var o={id:s.id,roomId:s["room-id"],messageType:s["message-type"],messageEmotes:s.emotes,userId:s["user-id"],username:s.login,displayName:s["display-name"],userColor:s.color,userBadges:s.badges,userState:s,gifterUsername:s["msg-param-sender-login"],gifterDisplayName:s["msg-param-sender-name"],channel:e.replace("#","")};b.onGiftSubContinue(t,n,o)})),f.on("connected",(function(e,t){console.log("Connected:"+e+":"+t),b.onConnected(e,t,p),p=!1})),f.on("reconnect",(function(){console.log("Reconnecting"),g++,b.onReconnect(g)})),f.connect().catch(b.onError),t&&(b.useEventSub?l(m,t).then(e=>{if("boolean"==typeof _)e(),_=null;else{if("function"!=typeof e)throw new Error("EventSub connection failed");_=e}}).catch(e=>{u(m,t)}):u(m,t))},Disconnect:function(){"function"==typeof _?(_(),_=null):_=void 0!==_||null,f.disconnect().catch(b.onError)},GetChannelRewards:async function(e,t=!1){if(h){if(!d){let t=await o("https://api.twitch.tv/helix/users?login="+m,{headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.json());d=t.data[0]}return(await o(`https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=${d.id}&only_manageable_rewards=${t}`,{headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.json())).data||[]}return[]},CreateChannelReward:async function(e,t){if(h){if(!d){let t=await o("https://api.twitch.tv/helix/users?login="+m,{headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.json());d=t.data[0]}return(await o("https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id="+d.id,{method:"POST",headers:{"Client-ID":e,Authorization:"Bearer "+h,"Content-Type":"application/json"},body:JSON.stringify(t)}).then(e=>e.json())).data[0]}throw new Error("Missing Channel Password")},UpdateChannelReward:async function(e,t,n){if(h){if(!d){let t=await o("https://api.twitch.tv/helix/users?login="+m,{headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.json());d=t.data[0]}return(await o(`https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=${d.id}&id=${t}`,{method:"PATCH",headers:{"Client-ID":e,Authorization:"Bearer "+h,"Content-Type":"application/json"},body:JSON.stringify(n)}).then(e=>e.json())).data[0]}throw new Error("Missing Channel Password")},DeleteChannelReward:async function(e,t){if(h){if(!d){let t=await o("https://api.twitch.tv/helix/users?login="+m,{headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.json());d=t.data[0]}return await o(`https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=${d.id}&id=${t}`,{method:"DELETE",headers:{"Client-ID":e,Authorization:"Bearer "+h}}).then(e=>e.text())}throw new Error("Missing Channel Password")}};void 0!==t&&t.exports&&(t.exports=b),"undefined"!=typeof window&&(window.ComfyJS=b,s=window.tmi)},{"node-fetch":2,"tmi.js":4,ws:3}],2:[function(e,t,n){(function(e){(function(){"use strict";var s=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==e)return e;throw new Error("unable to locate global object")}();t.exports=n=s.fetch,s.fetch&&(n.default=s.fetch.bind(s)),n.Headers=s.Headers,n.Request=s.Request,n.Response=s.Response}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,t,n){"use strict";t.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}},{}],4:[function(e,t,n){(function(t){(function(){!function t(n,s,o){function i(a,c){if(!s[a]){if(!n[a]){var l="function"==typeof e&&e;if(!c&&l)return l(a,!0);if(r)return r(a,!0);throw(l=new Error("Cannot find module '"+a+"'")).code="MODULE_NOT_FOUND",l}l=s[a]={exports:{}},n[a][0].call(l.exports,(function(e){return i(n[a][1][e]||e)}),l,l.exports,t,n,s,o)}return s[a].exports}for(var r="function"==typeof e&&e,a=0;ae.length)&&(t=e.length);for(var n=0,s=new Array(t);n: ").concat(d)),b.hasOwn(e.tags,"username")||(e.tags.username=z),e.tags["message-type"]="whisper",z=b.channel(e.tags.username),this.emits(["whisper","message"],[[z,e.tags,d,!1]]);break;case"PRIVMSG":e.tags.username=e.prefix.split("!")[0],"jtv"===e.tags.username?(l=b.username(d.split(" ")[0]),u=d.includes("auto"),d.includes("hosting you for")?(c=b.extractNumber(d),this.emit("hosted",h,l,c,u)):d.includes("hosting you")&&this.emit("hosted",h,l,0,u)):(c=b.get(this.opts.options.messagesLogLevel,"info"),l=b.actionMessage(d),e.tags["message-type"]=l?"action":"chat",d=l?l[1]:d,b.hasOwn(e.tags,"bits")?this.emit("cheer",h,e.tags,d):(b.hasOwn(e.tags,"msg-id")?"highlighted-message"===e.tags["msg-id"]?(u=e.tags["msg-id"],this.emit("redeem",h,e.tags.username,u,e.tags,d)):"skip-subs-mode-message"===e.tags["msg-id"]&&(m=e.tags["msg-id"],this.emit("redeem",h,e.tags.username,m,e.tags,d)):b.hasOwn(e.tags,"custom-reward-id")&&(m=e.tags["custom-reward-id"],this.emit("redeem",h,e.tags.username,m,e.tags,d)),l?(this.log[c]("[".concat(h,"] *<").concat(e.tags.username,">: ").concat(d)),this.emits(["action","message"],[[h,e.tags,d,!1]])):(this.log[c]("[".concat(h,"] <").concat(e.tags.username,">: ").concat(d)),this.emits(["chat","message"],[[h,e.tags,d,!1]]))));break;default:this.log.warn("Could not parse message:\n".concat(JSON.stringify(e,null,4)))}}},a.prototype.connect=function(){var e=this;return new Promise((function(t,n){e.server=b.get(e.opts.connection.server,"irc-ws.chat.twitch.tv"),e.port=b.get(e.opts.connection.port,80),e.secure&&(e.port=443),443===e.port&&(e.secure=!0),e.reconnectTimer=e.reconnectTimer*e.reconnectDecay,e.reconnectTimer>=e.maxReconnectInterval&&(e.reconnectTimer=e.maxReconnectInterval),e._openConnection(),e.once("_promiseConnect",(function(s){s?n(s):t([e.server,~~e.port])}))}))},a.prototype._openConnection=function(){var e="".concat(this.secure?"wss":"ws","://").concat(this.server,":").concat(this.port,"/"),t={};"agent"in this.opts.connection&&(t.agent=this.opts.connection.agent),this.ws=new u(e,"irc",t),this.ws.onmessage=this._onMessage.bind(this),this.ws.onerror=this._onError.bind(this),this.ws.onclose=this._onClose.bind(this),this.ws.onopen=this._onOpen.bind(this)},a.prototype._onOpen=function(){var e=this;this._isConnected()&&(this.log.info("Connecting to ".concat(this.server," on port ").concat(this.port,"..")),this.emit("connecting",this.server,~~this.port),this.username=b.get(this.opts.identity.username,b.justinfan()),this._getToken().then((function(t){var n=b.password(t);e.log.info("Sending authentication to server.."),e.emit("logon"),t="twitch.tv/tags twitch.tv/commands",e._skipMembership||(t+=" twitch.tv/membership"),e.ws.send("CAP REQ :"+t),n?e.ws.send("PASS ".concat(n)):b.isJustinfan(e.username)&&e.ws.send("PASS SCHMOOPIIE"),e.ws.send("NICK ".concat(e.username))})).catch((function(t){e.emits(["_promiseConnect","disconnected"],[[t],["Could not get a token."]])})))},a.prototype._getToken=function(){var e,t=this.opts.identity.password;return"function"==typeof t?(e=t())instanceof Promise?e:Promise.resolve(e):Promise.resolve(t)},a.prototype._onMessage=function(e){var t=this;e.data.trim().split("\r\n").forEach((function(e){(e=g.msg(e))&&t.handleMessage(e)}))},a.prototype._onError=function(){var e=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),clearTimeout(this._updateEmotesetsTimer),this.reason=null===this.ws?"Connection closed.":"Unable to connect.",this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout((function(){e.reconnecting=!1,e.connect().catch((function(t){return e.log.error(t)}))}),this.reconnectTimer)),this.ws=null},a.prototype._onClose=function(){var e=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),clearTimeout(this._updateEmotesetsTimer),this.wasCloseCalled?(this.wasCloseCalled=!1,this.reason="Connection closed.",this.log.info(this.reason),this.emits(["_promiseConnect","_promiseDisconnect","disconnected"],[[this.reason],[null],[this.reason]])):(this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Could not connect to server. Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout((function(){e.reconnecting=!1,e.connect().catch((function(t){return e.log.error(t)}))}),this.reconnectTimer))),this.ws=null},a.prototype._getPromiseDelay=function(){return this.currentLatency<=600?600:this.currentLatency+100},a.prototype._sendCommand=function(e,t,n,s){var o=this;return new Promise((function(i,r){if(!o._isConnected())return r("Not connected to server.");var a;null!==e&&"number"!=typeof e||(null===e&&(e=o._getPromiseDelay()),b.promiseDelay(e).then((function(){return r("No response from Twitch.")}))),null!==t?(a=b.channel(t),o.log.info("[".concat(a,"] Executing command: ").concat(n)),o.ws.send("PRIVMSG ".concat(a," :").concat(n))):(o.log.info("Executing command: ".concat(n)),o.ws.send(n)),"function"==typeof s?s(i,r):i()}))},a.prototype._sendMessage=function(e,t,n,s){var o=this;return new Promise((function(i,r){if(!o._isConnected())return r("Not connected to server.");if(b.isJustinfan(o.getUsername()))return r("Cannot send anonymous messages.");var a,c=b.channel(t);o.userstate[c]||(o.userstate[c]={}),500<=n.length&&(a=b.splitLine(n,500),n=a[0],setTimeout((function(){o._sendMessage(e,t,a[1],(function(){}))}),350)),o.ws.send("PRIVMSG ".concat(c," :").concat(n));var l={};Object.keys(o.emotesets).forEach((function(e){return o.emotesets[e].forEach((function(e){return(b.isRegex(e.code)?g.emoteRegex:g.emoteString)(n,e.code,e.id,l)}))}));var u=Object.assign(o.userstate[c],g.emotes({emotes:g.transformEmotes(l)||null})),m=b.get(o.opts.options.messagesLogLevel,"info"),h=b.actionMessage(n);h?(u["message-type"]="action",o.log[m]("[".concat(c,"] *<").concat(o.getUsername(),">: ").concat(h[1])),o.emits(["action","message"],[[c,u,h[1],!0]])):(u["message-type"]="chat",o.log[m]("[".concat(c,"] <").concat(o.getUsername(),">: ").concat(n)),o.emits(["chat","message"],[[c,u,n,!0]])),"function"==typeof s?s(i,r):i()}))},a.prototype._updateEmoteset=function(e){var t,n=this,s=void 0!==e;s&&(e===this.emotes?s=!1:this.emotes=e),this._skipUpdatingEmotesets?s&&this.emit("emotesets",e,{}):(t=function(){0n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},o.prototype.once=function(e,t){if(!i(t))throw TypeError("listener must be a function");var n=!1;if(this._events.hasOwnProperty(e)&&"_"===e.charAt(0)){var s,o=1,r=e;for(s in this._events)this._events.hasOwnProperty(s)&&s.startsWith(r)&&o++;e+=o}function a(){"_"!==e.charAt(0)||isNaN(e.substr(e.length-1))||(e=e.substring(0,e.length-1)),this.removeListener(e,a),n||(n=!0,t.apply(this,arguments))}return a.listener=t,this.on(e,a),this},o.prototype.removeListener=function(e,t){var n,s,o,a;if(!i(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(o=(n=this._events[e]).length,s=-1,n===t||i(n.listener)&&n.listener===t){if(delete this._events[e],this._events.hasOwnProperty(e+"2")&&"_"===e.charAt(0)){var c,l=e;for(c in this._events)this._events.hasOwnProperty(c)&&c.startsWith(l)&&(isNaN(parseInt(c.substr(c.length-1)))||(this._events[e+parseInt(c.substr(c.length-1)-1)]=this._events[c],delete this._events[c]));this._events[e]=this._events[e+"1"],delete this._events[e+"1"]}this._events.removeListener&&this.emit("removeListener",e,t)}else if(r(n)){for(a=o;0n?(t.command=e.slice(n),t):null;for(t.command=e.slice(n,s),n=s+1;32===e.charCodeAt(n);)n++;for(;n").replace(/\\"\\;/g,'"').replace(/\\'\\;/g,"'")},unescapeIRC:function(e){return e&&"string"==typeof e&&e.includes("\\")?e.replace(r,(function(e,t){return t in c?c[t]:t})):e},escapeIRC:function(e){return e&&"string"==typeof e?e.replace(a,(function(e,t){return t in l?"\\".concat(l[t]):t})):e},addWord:function(e,t){return e.length?e+" "+t:e+t},splitLine:function(e,t){var n=e.substring(0,t).lastIndexOf(" ");return[e.substring(0,n=-1===n?t-1:n),e.substring(n+1)]},extractNumber:function(e){for(var t=e.split(" "),n=0;n + + + + Dice-Tower - Overlay + + + + + +
+
+
+ + + diff --git a/src/main/resources/templates/chatoverlayconfig.html b/src/main/resources/templates/chatoverlayconfig.html new file mode 100644 index 0000000..3d56752 --- /dev/null +++ b/src/main/resources/templates/chatoverlayconfig.html @@ -0,0 +1,142 @@ + + + + + Dice-Tower - Chatoverlay + + + + + + + + + +
+

Dice-Tower - Chatoverlay

+
+

Allows Chat to roll a given amount of one dice

+

Example: "!roll 5d20"

+
+
+ + + +
+ + + + +
+
+ + + +
+
+ + + + +
+
+ + + + +
+
+ + +
+ +
+
+
+
+
.. they see them rolling
+
+ + + + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 1e3d7d6..c6dab73 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -150,8 +150,8 @@

How is your character called?

-
-
+
+

How-To

  • @@ -174,6 +174,11 @@
+
+
+ If you are looking for a way to let your twitch chat roll click here +
+
.. they see them rolling
-- 2.47.2