@@ -4284,70 +4284,138 @@ function escapeHtml(unsafe) {
42844284 }
42854285}
42864286
4287- function sendToH2R ( data ) {
4288- if ( settings . h2r && settings . h2rserver && settings . h2rserver . textsetting ) {
4289- try {
4290- var postServer = "http://127.0.0.1:4001/data/" ;
4291- if ( settings . h2rserver . textsetting . startsWith ( "http" ) ) {
4292- // full URL provided
4293- postServer = settings . h2rserver . textsetting ;
4294- } else if ( settings . h2rserver . textsetting . startsWith ( "127.0.0.1" ) ) {
4295- // missing the HTTP, so assume what they mean
4296- postServer = "http://" + settings . h2rserver . textsetting ;
4297- } else {
4298- postServer += settings . h2rserver . textsetting ; // Just going to assume they gave the token
4299- }
4300- var msg = { } ;
4301- if ( "id" in data ) {
4302- msg . id = data . id ;
4303- }
4304- if ( data . timestamp ) {
4305- msg . timestamp = data . timestamp ;
4306- }
4307- if ( ! data . textonly ) {
4308- data . chatmessage = unescapeHtml ( data . chatmessage ) ;
4309- }
4310- msg . snippet = { } ;
4311- msg . snippet . displayMessage = sanitizeRelay ( data . chatmessage , data . textonly ) || "" ;
4312- if ( ! msg . snippet . displayMessage ) {
4313- return ;
4314- }
4315- msg . authorDetails = { } ;
4316- msg . authorDetails . displayName = data . chatname || "" ;
4317- if ( data . type && ( data . type == "twitch" ) && ! data . chatimg && data . chatname ) {
4318- msg . authorDetails . profileImageUrl = "https://api.socialstream.ninja/twitch/large?username=" + encodeURIComponent ( data . chatname ) ; // 150x150
4319- } else if ( data . type && ( ( data . type == "youtube" ) || ( data . type == "youtubeshorts" ) ) && data . chatimg ) {
4320- let chatimg = data . chatimg . replace ( "=s32-" , "=s256-" ) ;
4321- msg . authorDetails . profileImageUrl = chatimg . replace ( "=s64-" , "=s256-" ) ;
4322- } else {
4323- msg . authorDetails . profileImageUrl = data . chatimg || "https://socialstream.ninja/sources/images/unknown.png" ;
4324- }
4325- if ( data . type && data . sourceImg && data . type == "restream" ) {
4326- msg . platform = { } ;
4327- msg . platform . name = data . type || "" ;
4328- if ( data . sourceImg === "restream.png" ) {
4329- msg . platform . logoUrl = "https://socialstream.ninja/sources/images/" + data . sourceImg ;
4330- } else {
4331- msg . platform . logoUrl = data . sourceImg ;
4332- }
4333- } else if ( data . type ) {
4334- msg . platform = { } ;
4335- msg . platform . name = data . type || "" ;
4336- msg . platform . logoUrl = "https://socialstream.ninja/sources/images/" + data . type + ".png" ;
4337- }
4338- var h2r = { } ;
4339- h2r . messages = [ ] ;
4340- h2r . messages . push ( msg ) ;
4341- ajax ( h2r , postServer , "POST" ) ;
4342- } catch ( e ) {
4343- console . warn ( e ) ;
4344- }
4345- }
4346- }
4347- function sanitizeRelay ( text , textonly = false , alt = false ) {
4348- if ( ! text || ! text . trim ( ) ) {
4349- return alt || text ;
4350- }
4287+ function sendToH2R ( data ) {
4288+ if ( settings . h2r && settings . h2rserver && settings . h2rserver . textsetting ) {
4289+ try {
4290+ var postServer = "http://127.0.0.1:4001/data/" ;
4291+ if ( settings . h2rserver . textsetting . startsWith ( "http" ) ) {
4292+ // full URL provided
4293+ postServer = settings . h2rserver . textsetting ;
4294+ } else if ( settings . h2rserver . textsetting . startsWith ( "127.0.0.1" ) ) {
4295+ // missing the HTTP, so assume what they mean
4296+ postServer = "http://" + settings . h2rserver . textsetting ;
4297+ } else {
4298+ postServer += settings . h2rserver . textsetting ; // Just going to assume they gave the token
4299+ }
4300+ var msg = { } ;
4301+ if ( "id" in data ) {
4302+ msg . id = data . id ;
4303+ }
4304+ if ( data . timestamp ) {
4305+ msg . timestamp = data . timestamp ;
4306+ }
4307+ if ( ! data . textonly ) {
4308+ data . chatmessage = unescapeHtml ( data . chatmessage ) ;
4309+ }
4310+ msg . snippet = { } ;
4311+ msg . snippet . displayMessage = sanitizeRelay ( data . chatmessage , data . textonly ) || "" ;
4312+ if ( ! msg . snippet . displayMessage ) {
4313+ return ;
4314+ }
4315+ msg . authorDetails = { } ;
4316+ msg . authorDetails . displayName = data . chatname || "" ;
4317+ if ( data . type && ( data . type == "twitch" ) && ! data . chatimg && data . chatname ) {
4318+ msg . authorDetails . profileImageUrl = "https://api.socialstream.ninja/twitch/large?username=" + encodeURIComponent ( data . chatname ) ; // 150x150
4319+ } else if ( data . type && ( ( data . type == "youtube" ) || ( data . type == "youtubeshorts" ) ) && data . chatimg ) {
4320+ let chatimg = data . chatimg . replace ( "=s32-" , "=s256-" ) ;
4321+ msg . authorDetails . profileImageUrl = chatimg . replace ( "=s64-" , "=s256-" ) ;
4322+ } else {
4323+ msg . authorDetails . profileImageUrl = data . chatimg || "https://socialstream.ninja/sources/images/unknown.png" ;
4324+ }
4325+ if ( data . type && data . sourceImg && data . type == "restream" ) {
4326+ msg . platform = { } ;
4327+ msg . platform . name = data . type || "" ;
4328+ if ( data . sourceImg === "restream.png" ) {
4329+ msg . platform . logoUrl = "https://socialstream.ninja/sources/images/" + data . sourceImg ;
4330+ } else {
4331+ msg . platform . logoUrl = data . sourceImg ;
4332+ }
4333+ } else if ( data . type ) {
4334+ msg . platform = { } ;
4335+ msg . platform . name = data . type || "" ;
4336+ msg . platform . logoUrl = "https://socialstream.ninja/sources/images/" + data . type + ".png" ;
4337+ }
4338+ var h2r = { } ;
4339+ h2r . messages = [ ] ;
4340+ h2r . messages . push ( msg ) ;
4341+ ajax ( h2r , postServer , "POST" ) ;
4342+ } catch ( e ) {
4343+ console . warn ( e ) ;
4344+ }
4345+ }
4346+ }
4347+
4348+ const WEBHOOK_RELAY_SOURCES = new Set ( [ "stripe" , "kofi" , "bmac" , "fourthwall" ] ) ;
4349+
4350+ function normalizeWebhookRelayUrl ( rawUrl ) {
4351+ if ( ! rawUrl ) {
4352+ return null ;
4353+ }
4354+
4355+ let url = String ( rawUrl ) . trim ( ) ;
4356+ if ( ! url ) {
4357+ return null ;
4358+ }
4359+
4360+ if ( url . startsWith ( "http://" ) || url . startsWith ( "https://" ) ) {
4361+ return url ;
4362+ }
4363+
4364+ if ( url . startsWith ( "127.0.0.1" ) || url . startsWith ( "localhost" ) ) {
4365+ return "http://" + url ;
4366+ }
4367+
4368+ return "https://" + url ;
4369+ }
4370+
4371+ function relayIncomingWebhook ( source , payload ) {
4372+ if ( ! WEBHOOK_RELAY_SOURCES . has ( source ) ) {
4373+ return ;
4374+ }
4375+ if ( ! settings . webhookrelay || ! settings . webhookrelayurl || ! settings . webhookrelayurl . textsetting ) {
4376+ return ;
4377+ }
4378+ if ( ! payload ) {
4379+ return ;
4380+ }
4381+
4382+ const endpoint = normalizeWebhookRelayUrl ( settings . webhookrelayurl . textsetting ) ;
4383+ if ( ! endpoint ) {
4384+ return ;
4385+ }
4386+
4387+ let body ;
4388+ let contentType = "application/json" ;
4389+ if ( typeof payload === "string" ) {
4390+ body = payload ;
4391+ contentType = "text/plain" ;
4392+ } else {
4393+ try {
4394+ body = JSON . stringify ( payload ) ;
4395+ } catch ( e ) {
4396+ console . warn ( `[WebhookRelay] Failed to serialize payload for ${ source } :` , e ) ;
4397+ return ;
4398+ }
4399+ }
4400+
4401+ try {
4402+ fetch ( endpoint , {
4403+ method : "POST" ,
4404+ headers : {
4405+ "Content-Type" : contentType ,
4406+ "X-SSN-Webhook-Source" : source
4407+ } ,
4408+ body
4409+ } ) . catch ( err => console . warn ( `[WebhookRelay] Request failed for ${ source } :` , err ) ) ;
4410+ } catch ( err ) {
4411+ console . warn ( `[WebhookRelay] Unexpected error relaying ${ source } :` , err ) ;
4412+ }
4413+ }
4414+
4415+ function sanitizeRelay ( text , textonly = false , alt = false ) {
4416+ if ( ! text || ! text . trim ( ) ) {
4417+ return alt || text ;
4418+ }
43514419
43524420 // Extract all emojis from image alt attributes before stripping HTML
43534421 const emojiMap = new Map ( ) ;
@@ -5897,15 +5965,17 @@ socketserver.addEventListener("message", async function (event) {
58975965 } catch ( e ) {
58985966 console . error ( e ) ;
58995967 }
5900- } else if ( "stripe" in data ) {
5901- try {
5902- if ( data . stripe . type !== "checkout.session.completed" ) {
5903- return false ;
5904- }
5905-
5906- console . log ( data . stripe ) ;
5907-
5908- var message = { } ;
5968+ } else if ( "stripe" in data ) {
5969+ try {
5970+ if ( data . stripe . type !== "checkout.session.completed" ) {
5971+ return false ;
5972+ }
5973+
5974+ console . log ( data . stripe ) ;
5975+
5976+ relayIncomingWebhook ( "stripe" , data . stripe ) ;
5977+
5978+ var message = { } ;
59095979 message . chatname = "" ;
59105980 message . chatmessage = "" ;
59115981
@@ -6025,13 +6095,15 @@ socketserver.addEventListener("message", async function (event) {
60256095 return ;
60266096 }
60276097 } else if ( "kofi" in data ) {
6028- try {
6029-
6030- if ( ! data . kofi . data ) {
6031- return false ;
6032- }
6033- try {
6034- var kofi = JSON . parse ( decodeURIComponent ( data . kofi . data ) . replace ( / \+ / g, " " ) ) ;
6098+ try {
6099+
6100+ if ( ! data . kofi . data ) {
6101+ return false ;
6102+ }
6103+
6104+ relayIncomingWebhook ( "kofi" , data . kofi ) ;
6105+ try {
6106+ var kofi = JSON . parse ( decodeURIComponent ( data . kofi . data ) . replace ( / \+ / g, " " ) ) ;
60356107 } catch ( e ) {
60366108 console . error ( e ) ;
60376109 return ;
@@ -6097,15 +6169,16 @@ socketserver.addEventListener("message", async function (event) {
60976169 }
60986170
60996171
6100- } else if ( "bmac" in data ) { // Buy Me a Coffe New Membership and Donation detection
6101- try {
6102- if ( ! data . bmac ) {
6103- return false ;
6104- }
6105- else {
6106- var bmac = data . bmac ;
6107- var message = { } ;
6108- if ( bmac . type === "membership.started" ) {
6172+ } else if ( "bmac" in data ) { // Buy Me a Coffe New Membership and Donation detection
6173+ try {
6174+ if ( ! data . bmac ) {
6175+ return false ;
6176+ }
6177+ else {
6178+ var bmac = data . bmac ;
6179+ relayIncomingWebhook ( "bmac" , data . bmac ) ;
6180+ var message = { } ;
6181+ if ( bmac . type === "membership.started" ) {
61096182 message . chatname = bmac . data . supporter_name || "Anonymous" ;
61106183 message . chatmessage = bmac . data . support_note . trim ( ) ;
61116184 //We use the donation badge from Kofi to feature the membership level name
@@ -6159,15 +6232,17 @@ socketserver.addEventListener("message", async function (event) {
61596232 } catch ( e ) {
61606233 return ;
61616234 }
6162- } else if ( "fourthwall" in data ) { // Dorthwall
6163- try {
6164- if ( ! data . fourthwall . data || data . fourthwall . type !== "ORDER_PLACED" ) {
6165- return false ;
6166- }
6167-
6168- const fourthwallData = data . fourthwall . data ;
6169-
6170- var message = { } ;
6235+ } else if ( "fourthwall" in data ) { // Dorthwall
6236+ try {
6237+ if ( ! data . fourthwall . data || data . fourthwall . type !== "ORDER_PLACED" ) {
6238+ return false ;
6239+ }
6240+
6241+ relayIncomingWebhook ( "fourthwall" , data . fourthwall ) ;
6242+
6243+ const fourthwallData = data . fourthwall . data ;
6244+
6245+ var message = { } ;
61716246 message . chatname = fourthwallData . username ||
61726247 ( fourthwallData . billing ?. address ?. name || "Anonymous" ) ;
61736248 message . chatmessage = fourthwallData . message || "" ;
0 commit comments