From 19006f06eab302f0e3f9ee9146fc651d6ed59baa Mon Sep 17 00:00:00 2001 From: Rodrigo Emanuel Date: Fri, 23 May 2025 19:28:51 -0300 Subject: [PATCH] Add files via upload Added a Chat Input Field and Moderation Tools. --- chat.html | 43 ++++++- css/app.css | 221 +++++++++++++++++++++++++++++++++++- css/settings.css | 5 + index.html | 28 ++++- js/app-mockup.js | 2 +- js/app.js | 171 +++++++++++++++++++++++++++- js/kick/module.js | 1 - js/twitch/module.js | 4 + streamerbot-import.vortisrd | 2 +- 9 files changed, 460 insertions(+), 17 deletions(-) diff --git a/chat.html b/chat.html index 6f1b0d7..04d682e 100644 --- a/chat.html +++ b/chat.html @@ -50,10 +50,49 @@
+
+
+ +
+ + + + + +
- @@ -80,4 +119,4 @@ - + \ No newline at end of file diff --git a/css/app.css b/css/app.css index 1223a4e..657db03 100644 --- a/css/app.css +++ b/css/app.css @@ -59,12 +59,19 @@ body { #chat .message { + position: relative; color: #FFF; font-size: 18px; line-height: 150%; text-shadow: 2px 2px 2px rgba(0,0,0,0.75); transition: all ease-in-out 300ms; margin: 5px 0; + padding-bottom: 4px; + border-radius: 5px; +} + +#chat .message:not(.event):hover { + background: #1a1a1a; } #chat .message img { @@ -458,14 +465,12 @@ body { +.wrapper.horizontal { + align-items: flex-end; +} - -#chat.horizontal { - position: absolute; - right: 0; - bottom: 0; - +.wrapper.horizontal #chat { flex-direction: row-reverse; align-items: flex-end; gap: 10px; @@ -474,6 +479,210 @@ body { + + +#chat .message.twitch:hover .chatmoderation.twitch { + display: block; +} + +#chat .message .chatmoderation { + display: none; + position: absolute; + top: 0; + right: 0; + padding: 2px 3px; + background: #262626; + border-radius: 5px; + transition: all ease-in-out 300ms; +} + +#chat .message .chatmoderation button { + background: none; + border: none; + color: #FFF; + cursor: pointer; + font-size: 18px; + padding: 5px; + transition: all ease-in-out 300ms; + margin: 0 3px; +} + +#chat .message .chatmoderation { + color: #ffcc00; +} + +#chat .message.twitch:hover .chatmoderation.twitch { + display: block; +} + +#chat .message.youtube:hover .chatmoderation.youtube { + display: block; +} + + +#chat .message.streamer .chatmoderation button:nth-child(2), +#chat .message.streamer .chatmoderation button:nth-child(3), +#chat .message.owner .chatmoderation { + display: none; +} + + + +#chat-input { + padding: 15px 10px; + position: relative; + display: none; +} + +#chat-input.enabled { + display: block; + width: 100%; +} + +#chat-input input[type=text] { + font-family: "Inter", sans-serif; + border: none; + background: #222; + color: #FFF; + padding: 10px 90px 10px 15px; + border-radius: 10px; + outline: none; + font-size: 16px; + width: 100%; + transition: all ease-in-out 300ms; +} + +#chat-input button { + background: none; + border: none; + color: #FFF; + cursor: pointer; + font-size: 18px; + padding: 5px; + + position: absolute; + top: 50%; + transform: translateY(-50%); + transition: all ease-in-out 300ms; +} + +#chat-input button.active, +#chat-input button:hover { + color: #ffcc00; +} + + +#chat-input #chat-input-config { + right: 20px; +} + +#chat-input #chat-input-send { + right: 60px; +} + +#chat-input .settings { + display: inline-block; + background: #0c0c0c; + padding: 10px; + border-radius: 10px; + + position: absolute; + top: -55px; + right: 0px; + z-index: 11; +} + +#chat-input .settings::after { + content: ""; + position: absolute; + bottom: -10px; /* posiciona fora do balão */ + right: 25px; /* onde a pontinha aparece horizontalmente */ + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid #0c0c0c; /* mesma cor do balão */ +} + +#chat-input .settings img { + width: 28px; +} + + +#chat-input .chat-enabler { + display: inline-flex; + padding: 5px; + gap: 5px; + color: #FFF; + font-size: 14px; + align-items: center; + color: #333; +} + +#chat-input .settings .switch { + position: relative; + display: inline-block; + width: 50px; + height: 27px; +} + +/* Hide default HTML checkbox */ +#chat-input .settings .switch input[type=checkbox] { + opacity: 1; + width: 0; + height: 0; +} + +/* The slider */ +#chat-input .settings .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #CCC; + transition: .4s; + border-radius: 30px; +} + +#chat-input .settings .slider:before { + position: absolute; + content: ""; + height: 1.4em; + width: 1.3em; + border-radius: 16px; + left: 5px; + top: 3px; + bottom: 0; + background-color: white; + transition: .4s; +} + +#chat-input .settings input[type=checkbox]:checked + .slider:before { + transform: translateX(1.5em); +} + +#chat-input #twitch input[type=checkbox]:checked + .slider { + background-color: #a970ff; +} +#chat-input #youtube input[type=checkbox]:checked + .slider { + background-color: #FF0000; +} + +#chat-input #kick input[type=checkbox]:checked + .slider { + background-color: #48d415; +} + + +#chat-input .settings input[type=checkbox]:checked + .slider { + background-color: #03c4de; +} +#chat-input .setting input[type=checkbox]:disabled + .slider { + background-color: #000 !important; +} + + #statistics { position: fixed; z-index: 11; diff --git a/css/settings.css b/css/settings.css index 8556a84..8dcf400 100644 --- a/css/settings.css +++ b/css/settings.css @@ -17,6 +17,11 @@ body { a { color: #ffcc00; } +hr { + border: 1px solid #222; + margin: 20px 0; +} + #chat-divided { display: flex; diff --git a/index.html b/index.html index e156270..2632bdb 100644 --- a/index.html +++ b/index.html @@ -63,7 +63,31 @@ -
+
+ +
+ +
+ +
The Chat Field is a BETA feature!
+ +
+ +
+ +
The Chat Field also accepts /commands. Commands Supported.
+ +
+ +
Moderation is a BETA feature!
+ +
+ +
+ +
Moderation Actions follows the Commands suported. Read More.
+ +
@@ -168,7 +192,7 @@ Kick -
This is a BETA feature! Read more.
+
Kick is a BETA feature! Read more.
diff --git a/js/app-mockup.js b/js/app-mockup.js index 0238b2c..da620b6 100644 --- a/js/app-mockup.js +++ b/js/app-mockup.js @@ -1009,4 +1009,4 @@ function randomString(length) { function randomColor() { const randomColor = "hsl(" + Math.random() * 360 + ", 100%, 75%)"; return randomColor; -} +} \ No newline at end of file diff --git a/js/app.js b/js/app.js index 72442b4..6ec1187 100644 --- a/js/app.js +++ b/js/app.js @@ -16,6 +16,8 @@ const chatFontSize = getURLParam("chatFontSize", 1); const chatBackground = getURLParam("chatBackground", "#121212"); const chatBackgroundOpacity = getURLParam("chatBackgroundOpacity", 1); const chatScrollBar = getURLParam("chatScrollBar", false); +const chatField = getURLParam("chatField", false); +const chatModeration = getURLParam("chatModeration", false); const currentLang = lang[getURLParam("language", 'ptbr')]; const eventsMockup = getURLParam("eventsMockup", true); @@ -36,6 +38,7 @@ const ignoreUserList = ignoreChatters.split(',').map(item => item.trim().toLower chatContainer.style.zoom = chatFontSize; if (chatScrollBar == false) { chatContainer.classList.add('noscrollbar'); } +if (chatField == true) { document.getElementById("chat-input").classList.add('enabled'); } /* ----------------------- */ /* START */ @@ -44,7 +47,7 @@ if (chatScrollBar == false) { chatContainer.classList.add('noscrollbar'); } document.body.style.backgroundColor = hexToRGBA(chatBackground,chatBackgroundOpacity); if (showPlatformStatistics == false) { document.querySelector('#statistics').style.display = 'none'; } -if (chatHorizontal == true) { chatContainer.classList.add('horizontal'); } +if (chatHorizontal == true) { chatContainer.parentElement.classList.add('horizontal'); } /* ----------------------- */ /* STREAMER.BOT CONNECTION */ @@ -77,6 +80,8 @@ const streamerBotClient = new StreamerbotClient({ }); + + /* ----------------------- */ /* UTILITIES */ /* ----------------------- */ @@ -87,7 +92,7 @@ async function addMessageToChat(userID, messageID, platform, data) { if (ttsSpeakerBotChat == true) { ttsSpeakerBotSays(data.userName, currentLang.ttschat, data.message); } - const html = DOMPurify.sanitize(` + let html = DOMPurify.sanitize(`
@@ -109,9 +114,28 @@ async function addMessageToChat(userID, messageID, platform, data) { ${data.message}
+ + ${chatModeration == true && platform == 'twitch' ? `[CHATMODERATIONSNIPPETTWITCH]` : ''} + ${chatModeration == true && platform == 'youtube' ? `[CHATMODERATIONSNIPPETYOUTUBE]` : ''} +
`); + + let chatmodtwitch = ` + + + + `; + + let chatmodyoutube = ` + + + `; + + html = html.replace('[CHATMODERATIONSNIPPETTWITCH]', chatmodtwitch); + html = html.replace('[CHATMODERATIONSNIPPETYOUTUBE]', chatmodyoutube); + chatContainer.insertAdjacentHTML('afterbegin', html); const messageElement = document.getElementById(messageID); @@ -142,7 +166,6 @@ async function addEventToChat(userID, messageID, platform, data) { ${showPlatform == true ? '' : '' } - ${data.userName} ${data.message} @@ -167,6 +190,7 @@ async function addEventToChat(userID, messageID, platform, data) { } + const whatTimeIsIt = () => { const now = new Date(); const hours24 = now.getHours(); @@ -373,4 +397,143 @@ function hexToRGBA(hexadecimal,opacity) { const b = parseInt(hex.substr(5, 2), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; -} \ No newline at end of file +} + + + +const chatInputConfig = document.getElementById("chat-input-config"); +const chatInputSend = document.getElementById("chat-input-send"); +const chatInputForm = document.querySelector("#chat-input form"); +const settings = document.getElementById("chat-input-settings"); + +chatInputForm.addEventListener("submit", function(event) { + event.preventDefault(); + + var chatSendPlatforms = []; + + const settingsContainer = document.getElementById("chat-input-settings"); + const checkboxes = settingsContainer.querySelectorAll('input[type="checkbox"]'); + + checkboxes.forEach(checkbox => { + const checked = checkbox.checked; + const platform = checkbox.getAttribute('data-platform'); + if (checked == true) { chatSendPlatforms.push(platform); } + }); + + chatSendPlatforms = chatSendPlatforms.join(',') + + const chatInput = chatInputForm.querySelector("input[type=text]") + const chatInputText = chatInput.value; + + // Sends Message to Twitch and YouTube + + streamerBotClient.doAction( + { name : "ChatRD Messages and Commands" }, + { + "type": "chat", + "platforms": chatSendPlatforms, + "message": chatInputText, + } + ).then( (sendchatstuff) => { + console.debug('Sending Chat to Streamer.Bot', sendchatstuff); + }); + + // Sends Message to Kick that are not commands + if (chatSendPlatforms.includes('kick')) { + if (!chatInputText.startsWith('/')) { + streamerBotClient.doAction( + { name : "ChatRD Kick Messages" }, + { + "message": chatInputText, + } + ).then( (sendchatstuff) => { + console.debug('Sending Kick Chat to Streamer.Bot', sendchatstuff); + }); + } + } + + chatInput.value = ''; +}); + +chatInputSend.addEventListener("click", function () { + chatInputForm.requestSubmit(); +}); + +chatInputConfig.addEventListener("click", function () { + const isHidden = settings.style.display === "none" || settings.classList.contains("animate__fadeOutDown"); + + if (isHidden) { + // Remover animação de saída (caso ainda esteja presente) + settings.classList.remove("animate__fadeOutDown"); + + // Mostrar com animação de entrada + settings.style.display = "block"; + settings.classList.add("animate__animated", "animate__fadeInUp"); + + // Limpa as classes após a animação + settings.addEventListener("animationend", function handler() { + settings.classList.remove("animate__animated", "animate__fadeInUp"); + settings.removeEventListener("animationend", handler); + }); + + chatInputConfig.classList.add("active"); + } + + else { + // Começar animação de saída + settings.classList.remove("animate__fadeInUp"); + settings.classList.add("animate__animated", "animate__fadeOutDown"); + + // Após animação, esconder elemento + settings.addEventListener("animationend", function handler() { + settings.style.display = "none"; + settings.classList.remove("animate__animated", "animate__fadeOutDown"); + settings.removeEventListener("animationend", handler); + }); + + chatInputConfig.classList.remove("active"); + } +}); + + +async function executeModCommand(event, command) { + event.preventDefault(); + + if (streamerBotConnected == true) { + const chatInput = chatInputForm.querySelector("input[type=text]") + chatInput.value = command; + + chatInputForm.requestSubmit(); + } + else { + + notifyError({ + title: currentLang.streamerbotdisconnected, + text: `` + }); + } +} + + +document.addEventListener("DOMContentLoaded", () => { + const settingsContainer = document.getElementById("chat-input-settings"); + + // Seleciona apenas checkboxes DENTRO do container + const checkboxes = settingsContainer.querySelectorAll('input[type="checkbox"]'); + + checkboxes.forEach(checkbox => { + const name = checkbox.name; + if (!name) return; // Ignora se o checkbox não tem 'name' + + // Restaurar estado salvo + const saved = localStorage.getItem(name); + if (saved !== null) { + checkbox.checked = saved === "true"; + } + + // Salvar alterações + checkbox.addEventListener("change", () => { + localStorage.setItem(name, checkbox.checked); + }); + }); +}); diff --git a/js/kick/module.js b/js/kick/module.js index c7f8237..5bc30d3 100644 --- a/js/kick/module.js +++ b/js/kick/module.js @@ -38,7 +38,6 @@ if (kickUserName) { kick7TVEmojis.set(emote.name, emote.url); }); } - })(); kickWebSocket.send( diff --git a/js/twitch/module.js b/js/twitch/module.js index 09a5e72..bcfdc42 100644 --- a/js/twitch/module.js +++ b/js/twitch/module.js @@ -116,6 +116,10 @@ async function twitchChatMessage(data) { const classes = firstMessage ? ['first-message'] : []; + if (data.user.role == 4) { + classes.push('streamer'); + } + const replyHTML = isReply ? `
${replyData.userName}: ${replyData.msgBody}
` : ''; diff --git a/streamerbot-import.vortisrd b/streamerbot-import.vortisrd index 7211fef..dbb70f8 100644 --- a/streamerbot-import.vortisrd +++ b/streamerbot-import.vortisrd @@ -1 +1 @@ -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 \ No newline at end of file +U0JBRR+LCAAAAAAABADtXetzokra/75V+z+k5q2td9/aZQ5XhVO1H5SIQRMzooKycz7Q3YhEEI94iW6d//19GhTBSxLnZM4k2TNVjpFu+vJcfs+lu+E/f/3L1dWn0J07n36++g/9AT8nTujCz0/qyJkb11ef/rm97izmo2hGS8xoNvdj4zorWrqz2I8mtIz9zH6WswLixnjmT+fbwnxbkbGYVPC2ZLIIgl1Z6E/8cBGaWZu0kJb9ltT4RJzCcJ2kjRiu/Du9crUrSop9Qjsmw+GwJOAy44hYZkQklhnEEZnBQ0UQuaHrDmVlN7jktl8X7sItDiy57k4cFLi0zfls4RZKHnGwIK42i8IbP55HszVUGjpBfK7WF3dC/Il3qtaOBV1/3I3GV7WlO5nHhfF5s2gx3XOpUOYEK2cdA3lPNT1zJiQKM8IfleNoghezGXR4Yo7zme95wJc8sQ8InjYS+NCAnpBeUFzXcTjMlByOo6QfMgqvEAaVxZKCSZlDJSc//BzXJMFR3LIjMrLsANcECfjnohLjymXMl4dlASHu6Nb5ekppJ7PSYclZ3u05E+9E6Zd86W/7H78UCH0seqeosePm0VDPaMeeV+7QBU5g96iLpFj9+etXywd2ruKvX+98PIviaDj/3Kp1v37VZtDpKpqNS+LXr0sRlFJgBU75+jWMcTQLfPSZBMGnYpO/HPaP1nNXjUgyeNJvTVGIvZ4QbEjdnN+v2ObuWjc0BVJXFphXQqJKTfhe3I4fp2hSK1+3o5Y6qXKD8HE6WFcfUF3b4HX1ulcbNRBcQ2EPyuOW6lc8Xa2uiNWIHevOG4TKEqlVza2bD6RvBE11vKtD24TvSvq5abE4DBb2ujq3+63NwCKBfs16g35jgrnYR5a5wbw2sTsNchsYy55grB1LmkB7xXZq0y+DcBoMhLbnTFpL5FeU3LVFr66tMd+L9u0d3F9hm9nfapUf9HUPh1A3oUvP+9KpLuw+9jpjxXdCmBPUV9sBbaMB30ifVONB3wiR0JjrAQv1K75ZD2ZmXZnp/qqleuOGasa+XdfYAcyFzjEZp5e0o1B+qO1xA/pP6ra5qn4bkGAAdQb8PCC1xhrx2njAaxtSm24QL0a58S2gny65aVB+Rs10blt6px+oy5J+Y6HfGGti9bK5D9tsc9j+17+OhHc6c3EUTv3APQEzW/EPnHVn7szm52rEztI13HgRzLuR6cx8qr9P1S3UOtanFFWUsuyU5RLLEFwGVBmKLqAKERhJFEtD4gzFkige3bpyfW9Exwn27QziKPTfYdkOpw/sSFL2NB6BYruPtMOXI1GddpbCVxHSg8CZxi7Jle+Kf8sqHhvNIUJYHPKYAYwWGVEsAxC7rsKgISF4WB5iAZXfoNEcRIvuArlX6gIaDK9qYTR3f4TxPCrNW89vsSNgLub+xNl2fiSGy63ot7Z0wDC5GVnPcUIH94gMyU1T6mXF87NSGEeLGXZPdxcsXt5PKlFcmbjlIdhyxAtlRiSsC74AWPVSWXBkGeHy0FW+RfU4nv9Iiue4nENEgTCygwhD3SQgmMwxEuKBUAj0UhbfoOJ1u53UVf0IyuYEvkMrfepMXWfszqrR/EgyQzeOHS+Z+9/m83j7829H9ZBDrGhGND+Yu7MnreFZ4wZm9OQ8k9ItXMuKzGMIb5QSO2REVnAYEJ8hIyuO45SFMlfGx97yC5SrxJ5VrmMT+zLV4gqq9c+n+HAh6O3Z8D2g7onWUx4ghNiSLECIwrECRCyOwChOiWN4pJREluMdxSn/CXCfSkTgRAmBHRAIZkRZ4RhZBKHlEBoC9imEFYU3CHBNH7/DYHw3+n/T4f9ypQHPotWx4tJp7bRoDDXP1NsysOxIrotYBsQdgnpMIJ7nJYFxZFESwcqzosiei8w5mT1GlG+PzZ9Ej4O5dxZoH3E/SwGofWb6ZSyILMtiRsEYPGOZAz9GEgVGEBSwzzLYbpF/m9O/qvvD+dXfH7n/e37+tOoZAsgy5iQX0K2EiQsE4MHWCC5oMce6PBJKBCnSGyZAfPX3EIJKfxq4L6TDOY+2pCi4xBMJ7C39j2MVRqHiwGPAe8cBi0yOAf/HE8JwYPjPTlyfQAQPmHiydjp/Hpx4XpCGTInlwOAhojAO4UTGlRWFUFvoKMdK9H3m/6QB+zNJ92OTdPYI+9W13W+twE5s7E5F0etSQNbVZpdv/GpbLbZYPy4kn5KPFvu4/jgidXuJQ9bv0zbUxq1jtdIE2Yn6+WRZlnhTpToOlfmu32ZdW9vC3bTYP3uUAPtjE2ps8/0k1MocuLhcmWdIuQzOgOtKjMIqEkOGjoAdEfGEP7aG/40JNV7ASOSQxHCcghjR4THjSEOgljiUCM8OCae8xbh+uxB4l0Y+8RU4rVdqFIbw/SHyah8W+q/b0z1U848jLNwlMNaxJLgmBVCewQ6+MX1UDx70emOJ+JVn9EfBQDBZu+Nl0Oiujtc49ushFNIplLaQHnIBFlojm+8BTEs9xO3WaLzp/Yk29rDObej6TH79Jl+mB2wRVrtFM0FCba3f7NdRMPs4xUKbrr2swKSxdqisUb8F9+uLnlCNnX47aqxXk8N2CqagHoS6OsrWlm6DFgtjYzFHpqTuRbov+wDvB7SpNIprQ8F2ntqa3NzR8eTbg7EFYIYSc/jgQB2LYz13XZnoKvaGnZXXOlyrqgCPUxoAX5U5srSFrQL9b6ojPDE21qZK7g/GgwU63lYEc3lAPDcfWNK42Tkw05XxwbjH6bpTe7xs+pXd37/qKju/7Zz4tNM1KLUDbWiG4PSNB0etXu/6w6t9+cn74ZOuh40bTV/e9VfgjdoOHgb9VqCr+hJZPf/eK85T7ceNXRtqr9XuqFLXhr67mZzmeKlWw4H1uNmu5SX3UNNbaM9qrG1Lmx3SUz2q1xrRdT7dV0YolJbEkh7sjl46pK+7OpzPGHiTrknq9Z3emF/A5UrkNpG1tXQHc5iAvHlf/Mqjfi1vZamF7kzWu/erO3lYGBa3Ijf7+aj94/VR84YsqY5t5XHRrZsLWzAi/Vr07vzKT3s5oq5T5O3Gdzs25/jGkHZrjcCLTA4z+poGdYOCs+2v8+23nmk/k7/teuZWpmraAoUKh8JW0Mxo1ov1uj1CN3CN6nNyX6Ac0n8waQQDK35O7hvUrQNM82/5Vgz8X+sP0aEc7mnczugBLrO3APomutbmHwPg3zXoNJvRp38X2aEWg6xMs3n22cO2faDhyNnEh7JyVhfsuhnb4ILv8fibZa9h5LAEQoQAhYR11Mo/9Oua92W9lUkufgQXvLRff5c01K+ybkb78aEe9ggfsAN+VLOtxwBsVJEuOb3s9NrneJm0dzEPhVaMBHwKK7Kx7ulgcDjUWMdSFjtbdFKOO3k51hI51h/uVvrDiflb4G2p1TG4+2DLgnRvwXWlnCsHG+ONoH8WZHztmrRdKtvFsQBeLUm/7dk35hps3BRCjinF8PyY6XjvNpUyhDDP8aGL6orQtZQx0J7FE5PiYW6Mr4qJG9oX4u3wST06P77vgdMb8I02A6ExHYTmGq/P6Msp/NFaHISBDyD3vt1vAFY/Ss00jHtVncaC6WO+BXLZCEAelnY4eFKGnx3jZfj4zDgv/b2fV+qzgAzzwQipp+h+bF/2Mi9R/+0C21jxWw8V/5RNofp2pDvXca584DVVbYombcD0hrTbA3QwFsAqhSvq93iq38R5PaX7eXhoOwnvn7RtWoND4xb4NoDHYWs6sFZRXt+/Sb4OZeZQJ7IxndUVoA8XIIgHntTfva/BYd5co1CbJ3HBK9iSi/H5hB+jPwxWd6p+arwUOzYpn07K1aYgV1wqV91Q9sDuhjSWstWsjTciay+yAb0dX03QTeyPcnxb5duM9RvAhX7rte0ChybQvmVSu/Yy+3xjbgDPFgMY9yW2+WIfUzMXg1BbHNPlZfN/Rp/8gSX+oPnuMZXiBU0DW2D/0Pqcnup5PV2/WE938hKA7drG4N9Dr4pxQWOEAkovPSKUb6E0ghhxL7vBFK7JMx36aAqF8b2q3YbY7wfy9/vI86E+P+d779o8H1dxS/uFtj/Hy9eJic/Fa5YxTvzNBFuVdTPHm1eNKQVzgaCfH2VLz9iCjm1xSxKaLcQbAbVTSCjo0evqSBis3tj8q3bdsJx+5fvN2ZJ4aP+N4UIjAFnkbdOeYvX348JLYy+H+jGJL/O2MKDHA321VkR9IvppT4I7xJMpegXMfCFt1gMreGvY0CWUrlpj5FiG6vYSPryynjypN8/T5GwOTlsMeDPuhdrUVr0/Nn56a7878q965ZIcfMWzLIUzb0zf7rxCHv/w+4X8d/ttmt9McjWAadPEFzyd9y/gyKncd5eXXt2PRvvct/DqfnQty8v2HPBjz+v5Xb79zYV6bgPW9YjVCAD/WKNI68MzTy+OfdLcZoZrElEVFmxO/Oe6yGuviwRLYhrcIDS7dt/opTTe59Nf6LsW5CLLtWc+a8DeCk/G6k/pX2KnHOtsHPitvNvnQQK45l+ge6q+ubt+E3nBzTO5mkw3zXowtwGPiZb4hmCHg/E2RgHbVsjXvMzOFfC4iLc0Vjxlb49tzKGPBnb6d+Jrsf2zsq6CLTKpXIGP1rC3vivI2OU2/qDeAbYVyuxkHeJo3Xx5y7eiQb9dbDdZ1xlV8aQxcjtSA4VG4NaUsHm8RyHW1Qab+lqe3+xU/gFysWpuMTTzhy9ez070PaXfZWORqO9LZU73x96XB5B/dcuHLFZJ/azUX8540sqwtK7cQbuA2wbEk9KUxtO21c7ZEpDXg3XQIt2ryuEaxMc9k8pj2eWVEseUeU5hRMlxGIfFDiMjzBPOxQLmjnfg/jduoSOc4pZcxDPgSSBG5Bz4S2YRo7iEK7FDLMuS83a30CUHSHb76D7C1rkLT2e95GjWaUV6/mwW3bD+9OEsTsKk5A5ZRimJLiMSQhhZKUsMLxEZkaGAys5bPX365N5+99HFi3QnYPrIjRInyqwzxIxbVhxGRCWRcRz4y5WxICuu7GD5eKKhOx9Fye0dkOe7M5ScLSb3k57fHc1c5zy3vsuxfYxLCluSGbpRkhEdZcjIWCYMW4LLjuQMJV74VoiUX59/xSOOfyRESg5yyogTGEJcAtxHhHFKrsMIjiyIvOMIgojeIERSbDw4ZLtHRVr4IzDxKbXbgRut+J/fTgutNBzy3JBHjDAs0x3fSGIQ55QYpYzL5aHIYwmfPSdXfnuHYw7ZkBQe7JJObFxnDvhAjw5d+ZO5682ckyfuvvv+6X++TpudNdim8EyLn79+hQJojU78RXVQNH/Ngz27a7d010j9MRgISQQKETCBCJhGRHfgGU8hOkoi5q4L0bNt0Z3UI5qxGdPo62R7QZKxGNn1JIPUg+hwhmnkNDE3aWZqPh3wcb7t5DDO7bixJNdH9RbJyhpEJ7djrd1JMhJsc7/rvAVRSGuj1yDCqQULRPsELz6JfDryUq+lmX/EDzxSHwVQP4Lob2FbK/gt013QC5Qc7qmyKLuH7r60IaJtLIj1GOs+lK2rI9I3lsiikX1rBPSSoK0lCqXkyT9JW32TdSBqhYj9gUYwtppmLxoC3UU/ort7A0SzqP3swBTQi7Z356X0grHcjL19Bq+lt3vSvdFbJU8BomNJI57dOEdLsk7GBpE8HcNoitd0Z3xjOrAasd2p0GxZ43Yt73fp39Bd6AbtO+Eh8Dt2O1nGxGuzo2p3LGldNTkk1XJ7O3o93icrSukuRfpZNpMnCI1HeqfaMXq21hub923W7Oqa8UWvaTVDrYz0JBINGnp9NCIh8CGUPcJrEkReS72ujdNVerpTP+DtJMMDsrOuQlSZPAUptPuNgO7WxeuVh3nZcy2F27WV0NwyxtBWROVym/1Io8xeKjt2/THeRrSpjIUgY6o0RvXkyUi/3uboebuu1rqsODHV6nWnpt0bbM8D+ttmbdTojoE2Y/Ouy2EP6LmkbdIPBrqRUGNh7CoSjGugcYAmba/NP05tmg26Zj00MWN0HXsn6mskBHmp0ejZSHROz/+9vzel+cEhOhIqU6AfROQBe+oAndqDudZaUBf6UCUadVM5ot+5OhpH9YLuphnwWnJqI8uk3GTZjXN9V0EnIjpPqhOk3ovyJwNU0xhh4EFvYi6adW3jWhLoK8ytbk6ofuQO6zXUvrF203t3mbIoR0N66I8Dnk1zGa7tvLIDgXn6R7nM5dT2R7vri/ahjiYZMd370t3SWt1njQZ9MnL6e14C/pyklbrdidss8u5ozLnVyWRMVMa6IFMn59cO7pwEb6UgyU7WNWFgBay+52e6spb1CbjHGcseb8a2nxtXG+Z90wp+b7vNPRYVs2RJZof1KD0GvBcZ/dGD3a+m2c96b5rwOJf16daViVE3fWJhGFBDc+ug2+lqAc3clu0+62e822fXGsNtZmqY20Fy0cmjSnAsq/vTO42nMkLp/TudyLDbR7wS61oLaGRk2SvQw3yfLJ5kqxON/fVdpjbI5DLJhmUrNr0oOcUEmNetJzZ1ATK6aO9siJpl6dZ2n2abRQ/wm7Nz89mPO5HzB0cdFWhtd06PK6PDdsfv/X4uyoU06YAcgG3xpnk9h3FKGT70c/q/zVhmuhbk2xiNcEg2t2o1J7sSPQVAbXe8l5dGQG7MNQJ7TXelnxq7mvC+FTVrpjjgzVWaXa8GzT299viVo+8247gvy2SzmrV9Vi63Jyz2mddvpMk4wU7W7n0QOamRYLsiATbMXuL1M3Tx8rgv3cCckix+x5LAX7h7/7TIHwyv7+dVsKnUThZltYAjBk93njSW4BfRVRl2T9v3rUN0h50ZBqvn8LWoL+Bf1jL/Ndmh9a5lpLDj6iJd2e3Qy/id0DJ9gEGc9zkhFtzFR5mdP2cb8/b5nP0/iWt7GUtXnU7R9wnfc/vwCLrr7otxGWbQezIadFn8MeShp9xcpBfB7r5MN4CO7x8fCjtwvx0nCrtYP4Z8cHQXFF3hX2L/9+FGgcbvGz+yXQCXycr+pMD7lo3djhzjchoEB/e+bzkwUbjn6SW+lpmc1pA+Ag22u3SrS1RfXUSDnmDQXTltxD/G75wGCcYluyWTXXAX4eTBve/cliYYRoI2zXFZZAlYcJFMnLr/XWNlLX2SAx0vuczvBpqYIzxOdni9cxpwI4c3KY8vjMFy9711fPDOzZ3G5sDTfnVz/kF1OZ1QT5Sn14pxOtAI37wob3HYzjkZKfIyrbuVlcpZWqXjP/HgvJuzdCv28zLatXrXhtkbg33J7jt4ClNOl86OqVbI/yRy1TUrrGk27l4iX0U6nt8JeGAzks9W5ryzWHye72dxudhPfufiSfvEGtQfr5ubbrJud7GNOrj//dspkj6tIn1KxUW4RDHZ6NIneXwYWtQaS/Dh1ghkP13LFy+226faeN92K+FznfqnSCD0aSjfKivHbbx1e/a8v8v2+MclYWl+oHe5rGj06UgkyZe8c1q0HUt68RpeMRauTtH4g6zHaOYC14PFi9epivbFRJMPRItkbQrmA2PNr8teuhbTA3qCLuRPXbx1O3NOR6i/tcPAKn3aG11Xek1/OHnCRPKUTGPzwX25Nt2L1Au1hV1X5m2rxTp9O0ifsHH2qal5HDpRnn7yuDTg5x166hbxXJXachKaL85VvWPaJnHBTp/afPKkILDZd68ad5xq/0PEcRP2mdNPpzYQFw4/nXxFzvfY2P9NpxV+3NmnC95dlJ1Nub5v/W/3Srs36rXuVff+qnJ9fVW5MujmylpLrdFL+Z3BV5VWelYo+aG3rro3Nbj79vbe0lv1K/X+unZEEBwF6ct5/0fTWPh3htgsKknCkHCMK4giIw4llpE5nmOEsjR0yryLZeGbXhLFsexZWn/rW6K+8ymz9I9d/fQURKEJuH37kPP8xZWL4giP3XnHnS0Pjk7tC9XkDbjPvJX40lfj5t8LrPkTf74ulLoTMo385FDDp1X8808/cXz5M30fM/czDzwWfirUngfxl1k0j4BG8cHBquT1zGo0mbj45AkJiiNJ4f3k2o9xVvHwmMjMnc/W+mQOhHICKBcKnWwPa7W3p0+SXs4xZ+6HO1LTK9s3Qe9fO81x6RX3cRrN5i6hZ04Scf/M795Gffxe6aRUZJA7dz6XPv31L7/9P7G9BYgPewAA \ No newline at end of file