WIP
| @@ -1,26 +0,0 @@ | ||||
| import PIL.Image, PIL.ImageDraw, PIL.ImageFont | ||||
| import os | ||||
| import sys | ||||
|  | ||||
|  | ||||
| font = PIL.ImageFont.truetype(os.path.join(os.path.dirname(__file__), './customAlertsMedia/ibmplex.ttf'), 20) | ||||
| nFrames = 60 | ||||
| width = 500 | ||||
| height = 500 | ||||
| if __name__ == "__main__": | ||||
|   tpe = sys.argv[1] | ||||
|   username = sys.argv[2] | ||||
|   description = sys.argv[3] | ||||
|   target = sys.argv[4] | ||||
|  | ||||
|   animated_gif = Image.open("./customAlertsMedia/"+tpe+".gif") | ||||
|  | ||||
|   frames = [] | ||||
|   for frame in ImageSequence.Iterator(animated_gif): | ||||
|     frame = frame.convert('RGB') | ||||
|     d = PIL.ImageDraw.Draw(frame) | ||||
|     d.text((0, 0), description, font=font, fill=(255, 255, 255)) | ||||
|     d.text((0, 100), username, font=font, fill=(255, 255, 255)) | ||||
|     frames.append(frame) | ||||
|   frames[0].save(target+tpe+".gif", save_all=True, append_images=frames[1:], duration=100, loop=0, optimize=0, comment="") | ||||
|   os.popen('cp ./customAlertsMedia/'+tpe+'.mp3 '+target+tpe+'.mp3') | ||||
| @@ -1,19 +1,65 @@ | ||||
| (function() { | ||||
|     function alert(username, desc, tpe) { | ||||
|       var pb = new java.lang.ProcessBuilder("/usr/bin/python3", "./Main.py", tpe, username, desc, "/opt/PhantomBot/config/gif-alerts/"); | ||||
|       var proc = pb.start(); | ||||
|       proc.waitFor(); | ||||
|  | ||||
|       $.panelsocketserver.alertImage(tpe+".gif?noise=" + Math.floor(Math.random() * 1000 + 1)); | ||||
|   function sendData(tpe, d) { | ||||
|     $.panelsocketserver.sendJSONToAll(JSON.stringify({ | ||||
|         'eventFamily': 'calert', | ||||
|         'eventType': tpe, | ||||
|         'data': d | ||||
|     })); | ||||
|   } | ||||
|  | ||||
|   $.bind('twitchFollow', function(event) { | ||||
|       var follower = event.getFollower(); | ||||
|       alert(follower.toUpperCase(), "NEW FOLLOWER", "follow"); | ||||
|     sendData('follow',{ 'user': event.getFollower()}); | ||||
|   }); | ||||
|   $.bind('twitchSubscriber', function(event) { | ||||
|       var subscriber = event.getSubscriber(); | ||||
|       alert(subscriber.toUpperCase(), "NEW SUBSCRIBER", "subscribe"); | ||||
|     sendData('subscribe', { | ||||
|       'user': event.getSubscriber(), | ||||
|       'isReSub': false, | ||||
|       'months': 0, | ||||
|       'tier': event.getPlan() / 1000, | ||||
|       'message': event.getMessage() | ||||
|     }); | ||||
|   }); | ||||
|   $.bind('twitchReSubscriber', function(event) { | ||||
|     sendData('subscribe', { | ||||
|       'user': event.getReSubscriber(), | ||||
|       'isReSub': true, | ||||
|       'months': event.getMonths(), | ||||
|       'tier': event.getPlan() / 1000, | ||||
|       'message': event.getMessage() | ||||
|     }); | ||||
|   }); | ||||
|   $.bind('twitchSubscriptionGift', function(event) { | ||||
|     sendData('gift', { | ||||
|       'recipient': event.getRecipient(), | ||||
|       'user': event.getUsername(), | ||||
|       'months': event.getMonths(), | ||||
|       'isReSub': (parseInt(event.getMonths()) > 1), | ||||
|       'tier': event.getPlan() / 1000 | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   $.bind('twitchMassSubscriptionGifted', function(event) { | ||||
|     sendData('mgift', { | ||||
|       'user': event.getUsername(), | ||||
|       'amount': event.getAmount(), | ||||
|       'tier': event.getPlan() / 1000 | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   $.bind('twitchRaid', function(event) { | ||||
|     sendData('raid',{ | ||||
|       'user': event.getUsername(), | ||||
|       'viewers': event.getViewers(), | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   $.bind('twitchBits', function (event) { | ||||
|     sendData('bits', { | ||||
|         'user': event.getUsername(), | ||||
|         'amount': event.getBits(), | ||||
|         'message': event.getMessage() | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   $.bind('command', function(event) { | ||||
| @@ -35,7 +81,7 @@ | ||||
|             $.say($.whisperPrefix(sender) + $.lang.get('calert.help', ' Use "!calert [follow | subsribe | donation] user" to trigger alert.')); | ||||
|         } | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
|  | ||||
|   $.bind('initReady', function() { | ||||
|     $.registerChatCommand('./custom/custom/customAlerts.js', 'calert'); | ||||
| @@ -43,5 +89,4 @@ | ||||
|     $.registerChatSubcommand('calert', 'subscribe', 2); | ||||
|     $.registerChatSubcommand('calert', 'donation', 2); | ||||
|   }); | ||||
|   })(); | ||||
|    | ||||
| })(); | ||||
|   | ||||
| Before Width: | Height: | Size: 147 KiB | 
							
								
								
									
										62
									
								
								web/alert/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,62 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <title>CustomAlerts</title> | ||||
|     <!-- Load our styles --> | ||||
|     <style>body {margin:0;overflow:hidden;}</style> | ||||
|     <style> | ||||
|         @font-face { | ||||
|             font-family: IBM Plex Mono; | ||||
|             src: url(./media/ibmplex.ttf); | ||||
|         } | ||||
|         .main{ | ||||
|             position: absolute; | ||||
|             width:500px; | ||||
|             top: 50%; | ||||
|             left: 50%; | ||||
|             transform: translateX(-50%) translateY(-50%); | ||||
|             color: #5e81ac; | ||||
|             text-align:center; | ||||
|             font-size:32px; | ||||
|             font-family: IBM Plex Mono; | ||||
|             opacity: 0; | ||||
|             transition:1s; | ||||
|         } | ||||
|         .img{ | ||||
|             position: absolute; | ||||
|             top: calc(50% - 150px); | ||||
|             left: 50%; | ||||
|             width:auto; | ||||
|             height:200px; | ||||
|             transform: translateX(-50%) translateY(-50%); | ||||
|             opacity: 0; | ||||
|             transition:1s; | ||||
|         } | ||||
|         </style> | ||||
| </head> | ||||
|  | ||||
| <!-- Main body --> | ||||
| <body> | ||||
|     <img class="img" id="img"></img> | ||||
|     <div class="main" id="main"> | ||||
|         <!-- <canvas id="wall"></canvas> --> | ||||
|  | ||||
|     </div> | ||||
|     <!-- jQuery --> | ||||
|     <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | ||||
|     <!-- Load jQuery UI --> | ||||
|     <script src="https://code.jquery.com/ui/1.13.1/jquery-ui.min.js"></script> | ||||
|     <!-- Load Reconnecting socket --> | ||||
|     <script src="/common/reconnecting-websocket/reconnectingWS.min.js"></script> | ||||
|     <!-- Load Bot config file --> | ||||
|     <script src="/common/js/wsConfig.js"></script> | ||||
|  | ||||
|     <!-- Load functions copied out of panel dir, since can't directly read panel dir (http auth problem) --> | ||||
|     <script src="/custom/js/socketWrapper.js"></script> | ||||
|  | ||||
|     <!-- Load our script --> | ||||
|     <script src="index.js"></script> | ||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										157
									
								
								web/alert/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,157 @@ | ||||
| const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | ||||
|  | ||||
| const volume = parseInt((new URLSearchParams(window.location.search)).get('vol')) || 100 | ||||
|  | ||||
| $(async function () { | ||||
|     const webSocket = window.socket; | ||||
|     const announceQueue = []; | ||||
|  | ||||
|     const playAudio = (src) =>{ | ||||
|         return new Promise(resolve =>{ | ||||
|             var audio = new Audio(); | ||||
|             $(audio).on("loadedmetadata", function() { | ||||
|                 resolve(audio); | ||||
|             }); | ||||
|             audio.volume = volume/100.0; | ||||
|             audio.src = src; | ||||
|             audio.play().catch((err)=>{ | ||||
|                 if(err.toString().startsWith('NotAllowedError')){ | ||||
|                     $('body').append($('<button/>', { | ||||
|                         'html': 'Enable Audio Hook', | ||||
|                         'style': 'top:80%;left:50%; position: absolute; font-size: 30px; transform: translateX(-50%) translateY(-50%);' | ||||
|                     }).on('click', function(){$(this).remove()})); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|  | ||||
|     const renderEl = async (pic, music, message, detail) => { | ||||
|         let node = document.getElementById('main'); | ||||
|         let nodeIm = document.getElementById('img'); | ||||
|         node.innerHTML = message; | ||||
|         nodeIm.src = pic | ||||
|         let audio = await playAudio(music); | ||||
|         node.style.opacity = 1; | ||||
|         nodeIm.style.opacity = 1; | ||||
|         await sleep(audio.duration*1000 - 1000); | ||||
|         node.style.opacity = 0; | ||||
|         nodeIm.style.opacity = 0; | ||||
|     } | ||||
|      | ||||
|     const renderStart = async (an) => { | ||||
|         await renderEl('','./media/sonar.ogg', | ||||
|                        `The Stream Started`, `...`) | ||||
|     } | ||||
|  | ||||
|     const renderFollow = async (an) => { | ||||
|         await renderEl('./media/donate.gif','./media/sonar.ogg', | ||||
|                        `${an.user} joined the squad !`, `...`) | ||||
|     } | ||||
|  | ||||
|     const renderSubscribe = async (an) => { | ||||
|         // TTS: an.details.message | ||||
|         let v = an.details.isReSub ? 'resubscribed' : 'subscribed' | ||||
|         let detail = an.details.months == 0 ? '' : `for ${an.details.months} months ` | ||||
|         let message = `${an.user} ${v} ${detail}!` | ||||
|         await renderEl('./media/follow.gif','./media/follow.mp3', | ||||
|         message, | ||||
|         `...`) | ||||
|     } | ||||
|  | ||||
|     const renderGift = async (an) => { | ||||
|         await renderEl('./media/follow.gif','./media/follow.mp3', | ||||
|         `${an.user} gifted ${an.details.recipient} a sub !`, | ||||
|         `...`) | ||||
|     } | ||||
|  | ||||
|     const renderMassGift = async (an) => { | ||||
|         await renderEl('./media/follow.gif','./media/follow.mp3', | ||||
|         `${an.user} gifted ${an.details.amount} subs to the community !`, | ||||
|         `...`) | ||||
|     } | ||||
|  | ||||
|     const renderRaid = async (an) => { | ||||
|         await renderEl('./media/raid.gif','./media/subscribe.mp3', | ||||
|         `${an.user} is Raiding with a party of ${an.details.viewers} !`, | ||||
|         `...`) | ||||
|     } | ||||
|  | ||||
|     const renderBits = async (an) => { | ||||
|         // TTS: an.details.message | ||||
|         await renderEl('','', | ||||
|         `${an.user} cheered ${an.details.amount} bitties!`, | ||||
|         `...`) | ||||
|     } | ||||
|  | ||||
|     const renderAnnouncement = async (an) => { | ||||
|         if(an.tpe=='follow') await renderFollow(an) | ||||
|         else if(an.tpe=='subscribe') await renderSubscribe(an) | ||||
|         else if(an.tpe=='gift') await renderGift(an) | ||||
|         else if(an.tpe=='mgift') await renderMassGift(an) | ||||
|         else if(an.tpe=='raid') await renderRaid(an) | ||||
|         else if(an.tpe=='bits') await renderBits(an) | ||||
|         else if(an.tpe=='start') await renderStart(an) | ||||
|     } | ||||
|  | ||||
|     class Announcement { | ||||
|         constructor(data){ | ||||
|             this.user = data.user; | ||||
|             this.tpe = data.tpe; | ||||
|             this.details = data.details || {}; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     announceQueue.push(new Announcement({user:"-",tpe:"start"})); | ||||
|     const main = async () => { | ||||
|         while(true){ | ||||
|             if(announceQueue.length>0) | ||||
|                 await renderAnnouncement(announceQueue.shift()); | ||||
|             await sleep(1000); | ||||
|         } | ||||
|     } | ||||
|     main(); | ||||
|  | ||||
|     const handleSocketMessage = async (e)=>{ | ||||
|         try { | ||||
|             let rawMessage = e.data, | ||||
|                 message = JSON.parse(rawMessage); | ||||
|  | ||||
|             if(!message.hasOwnProperty('eventFamily') || message.eventFamily != 'calert' ||  | ||||
|                 !message.hasOwnProperty('eventType') || !message.hasOwnProperty('data')) | ||||
|                 return; | ||||
|             if(message.eventType == 'follow') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'follow'})) | ||||
|             }else if(message.eventType == 'subscribe') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'subscribe', details:message.data.details})) | ||||
|             }else if(message.eventType == 'gift') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'gift', details:message.data.details})) | ||||
|             }else if(message.eventType == 'mgift') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'mgift', details:message.data.details})) | ||||
|             }else if(message.eventType == 'raid') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'raid', details:message.data.details})) | ||||
|             }else if(message.eventType == 'bits') { | ||||
|                 announceQueue.push(new Announcement({user:message.data.user, tpe:'bits'})) | ||||
|             }else{ | ||||
|                 console.log("unhandled:", message.eventType) | ||||
|             } | ||||
|         } catch (ex) { | ||||
|             console.log(ex) | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     jQuery(async ()=>{ | ||||
|          | ||||
|         try{ | ||||
|             socket.addFamilyHandler("calert", handleSocketMessage); | ||||
|             if(socket){ | ||||
|                 while(socket.getReadyState() === 0){ | ||||
|                     await new Promise(r => setTimeout(r, 500)); | ||||
|                 } | ||||
|             } | ||||
|         }catch(e) {console.log(e)} | ||||
|     }) | ||||
|  | ||||
| }); | ||||
| Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 399 KiB | 
| Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB | 
| Before Width: | Height: | Size: 547 KiB After Width: | Height: | Size: 547 KiB | 
							
								
								
									
										
											BIN
										
									
								
								web/alert/media/sonar.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1752
									
								
								web/chatguessr/SRC
									
									
									
									
									
								
							
							
						
						| @@ -136,7 +136,7 @@ $(async function () { | ||||
|  | ||||
|     class Particle { | ||||
|         constructor(emote,data){ | ||||
|             this.size = 64; | ||||
|             this.size = 56; | ||||
|             this.data = data; | ||||
|             this.setPhysics(randRange(0,canvas.width), | ||||
|                             randRange(0,canvas.height), | ||||
| @@ -299,7 +299,7 @@ $(async function () { | ||||
|     const createParticule = (partP, emote, data, args) => { | ||||
|         let a =  new partP(emote,data,args) | ||||
|         particles.push(a); | ||||
|         setTimeout(()=>{particles.pop()}, drt*1000+2000); | ||||
|         setTimeout(()=>{particles.shift()}, drt*1000+2000); | ||||
|     } | ||||
|  | ||||
|     const genExplosion = (x,y,ang,em) =>{ | ||||
|   | ||||