diff --git a/package.json b/package.json index 94f2494..eb8957b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loquendo-bot", - "version": "2.3.0", + "version": "2.4.0", "description": "Bot assistant for streamers over different platforms", "main": "src/main.js", "scripts": { @@ -21,6 +21,7 @@ "electron-squirrel-startup": "^1.0.0", "express": "^4.18.2", "ini": "^2.0.0", + "kill-process-by-name": "^1.0.5", "node-google-tts-api": "^1.1.1", "querystring": "^0.2.1", "socket.io": "^4.7.1", diff --git a/src/backend/loquendoBot_backend.py b/src/backend/loquendoBot_backend.py index 79417ce..f539204 100644 --- a/src/backend/loquendoBot_backend.py +++ b/src/backend/loquendoBot_backend.py @@ -32,17 +32,15 @@ SetLogLevel(-1) settings = configparser.ConfigParser() app = Flask(__name__) +if len(sys.argv) > 1: + settingsPath = os.path.normpath(sys.argv[1]) + environment = sys.argv[2] -settingsPath = os.path.normpath(sys.argv[1]) -environment = sys.argv[2] q = queue.Queue() - # gobal functions # classes - - class LanguageDetection: def __init__(self): if environment == "dev": @@ -79,7 +77,7 @@ class STT: def __init__(self): settings.read(settingsPath) device_info = sd.query_devices(int(settings["STT"]["MICROPHONE"]), "input") - self.samplerate = int(device_info["default_samplerate"]) + self.samplerate = int(device_info["default_samplerate"]) if environment == "dev": settings_folder = os.path.dirname(settingsPath) @@ -182,7 +180,6 @@ text_to_speech_service = TTS() # endpoints - @app.route("/stream", methods=["GET"]) def stream_recognition(): def generate(): @@ -277,3 +274,4 @@ if __name__ == "__main__": stream_recognition() app.run(host="127.0.0.1", port=port) + app.terminate() diff --git a/src/css/chat.css b/src/css/chat.css index d01cd88..e65ea21 100644 --- a/src/css/chat.css +++ b/src/css/chat.css @@ -15,8 +15,9 @@ h1 { flex-direction: column; background-color: var(--mid-section); margin-left: 50px; - margin-right: 50px; font-family: 'FRAMDCN'; + position: relative; + z-index: 1; } .input-box { @@ -172,16 +173,25 @@ h1 { } .msg-container { + direction: ltr; position: static; display: inline-block; width: 100%; - margin: 0px 0px 0px 0px; padding: 0px 0px 10px 0px; } +.msg-container-user { + direction: rtl; + position: static; + display: inline-block; + width: 100%; + margin-top: 10px; +} + .msg-box { background: var(--chat-bubble); color: white; + min-width: 150px; border-radius: 5px; padding: 20px 5px 5px 25px; margin: 20px 0px 0px 25px; @@ -191,15 +201,14 @@ h1 { .msg-box-user { background: var(--chat-bubble); - padding: 5px 5px 5px 5px; - border-radius: 6px 6px 6px 6px; - margin-right: -20px; - margin-top: 10px; - max-width: 80%; - width: auto; - float: right; - word-wrap: break-word; + color: white; + text-align: -webkit-left; + min-width: 150px; + border-radius: 5px; + padding: 20px 15px 10px 5px; + margin: 0px 35px 0px 25px; box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24); + width: fit-content; } .msg-box-user-temp { @@ -211,14 +220,16 @@ h1 { border-radius: 50%; height: 50px; width: 50px; + z-index: 5; } .user-img-user { display: inline-block; border-radius: 50%; - height: 40px; - width: 40px; - margin: 0 0px 10px 10px; + height: 50px; + width: 50px; + position: absolute; + z-index: 5; } .messages { @@ -256,15 +267,26 @@ h1 { .username { float: left; - color: var(--chat-bubble-header); background-color: var(--main-color4); margin-left: 25px; color: white; position: relative; - z-index: 2; padding: 5px 5px 5px 30px; border-radius: 5px; top: 10px; + z-index: 1; +} + +.username-user { + background-color: var(--main-color4); + margin-right: 25px; + color: white; + padding: 5px 40px 5px 15px; + border-radius: 5px; + margin: 0px 30px 5px 5px; + top: 15px; + position: relative; + z-index: 1; } .username-temp { @@ -272,13 +294,35 @@ h1 { } .post-time { - float: right; font-size: 8pt; - padding: 10px 0px 0px 5px; + padding: 3px 5px 0px 15px; + color: white; display: inline-block; + background-color: var(--main-color4); + right: 15px; + top: -19px; + position: relative; + z-index: 2; + border-radius: 5px; + text-align: center; } -.msg-self .msg-box { +.post-time-user { + font-size: 8pt; + padding: 3px 15px 0px 5px; + margin: 5px -15px 0px -10px; + color: white; + display: inline-block; + background-color: var(--main-color4); + right: 60px; + top: -19px; + position: relative; + z-index: 2; + border-radius: 5px; + text-align: center; +} + +/* .msg-self .msg-box { border-radius: 6px 6px 6px 6px; background: var(--main-color1); float: right; @@ -291,30 +335,26 @@ h1 { .msg-self .msg { text-align: justify; text-justify: inter-word; -} +} */ .mmg { display: flex; } .icon-container { - width: 50px; height: 50px; position: absolute; - float: left; + left: 0; display: flex; align-items: center; - z-index: 3; } .icon-container-user { - width: 50px; + direction: ltr; height: 50px; - position: relative; - float: right; + position: absolute; display: flex; align-items: center; - z-index: 3; } .img { @@ -328,6 +368,7 @@ h1 { height: 20px; border-radius: 50%; margin-left: -15px; + z-index: 6; margin-top: -30px; } @@ -335,13 +376,8 @@ h1 { width: 20px; height: 20px; border-radius: 50%; - bottom: 0; - right: 0; - margin-left: -50px; - margin-top: 10px; -} - -select { + z-index: 6; + margin-top: -30px; } .menu-select { diff --git a/src/css/tabs.css b/src/css/tabs.css index e634850..e0eaaff 100644 --- a/src/css/tabs.css +++ b/src/css/tabs.css @@ -388,6 +388,7 @@ input[type='lol'] { transition: opacity 0.3s, visibility 0s; color: var(--main-color2); font-family: 'xxii_avenmedium'; + z-index: 999; } /* .tooltip .tooltiptext { diff --git a/src/index.html b/src/index.html index 64b444b..4a0caef 100644 --- a/src/index.html +++ b/src/index.html @@ -152,6 +152,11 @@
TTS Output Device
+
TTS Volume
diff --git a/src/js/auth.js b/src/js/auth.js index 7aed6e0..ec2d6a8 100644 --- a/src/js/auth.js +++ b/src/js/auth.js @@ -72,8 +72,30 @@ const twitchAuthentication = () => } }); +function getTwitchUserId() { + // Get user Logo with access token + options = { + method: 'GET', + url: `https://api.twitch.tv/helix/users`, + headers: { 'Client-ID': settings.TWITCH.CLIENT_ID, Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` }, + }; + + axios + .request(options) + .then((responseLogoUrl) => { + console.log(responseLogoUrl.data.data[0]); + settings.TWITCH.USERNAME = responseLogoUrl.data.data[0].display_name; + settings.TWITCH.USER_LOGO_URL = responseLogoUrl.data.data[0].profile_image_url; + settings.TWITCH.USER_ID = responseLogoUrl.data.data[0].id; + fs.writeFileSync(settingsPath, ini.stringify(settings)); + }) + .catch((error) => { + console.error(error); + }); +} function getTwitchOauthToken() { return twitchAuthentication().then((res) => { + getTwitchUserId(); return res; }); } diff --git a/src/js/backend.js b/src/js/backend.js index e271b5f..e576585 100644 --- a/src/js/backend.js +++ b/src/js/backend.js @@ -1,4 +1,5 @@ const spawn = require('child_process').spawn; +var kill = require('kill-process-by-name'); let python; async function getInstalledVoices() { @@ -145,17 +146,21 @@ async function initiateBackend() { initiateBackend(); +//TODO: convert to restartServer function ipcRenderer.on('quit-event', async () => { try { const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/terminate`, { method: 'GET' }); if (response.ok) { const responseData = await response.json(); console.log('Response:', responseData); + kill('loquendoBot_backend'); } else { console.error('Failed to send termination signal to Flask server.'); + kill('loquendoBot_backend'); } } catch (error) { console.error('Error sending termination signal:', error); + kill('loquendoBot_backend'); } }); diff --git a/src/js/chat.js b/src/js/chat.js index 21b3967..6ce1af7 100644 --- a/src/js/chat.js +++ b/src/js/chat.js @@ -8,22 +8,31 @@ function getResponse() { // Create chat message from received data const article = document.createElement('article'); - article.className = 'msg-container msg-self'; + article.className = 'msg-container-user'; article.innerHTML = messageTemplates.userTemplate; - const postTime = article.querySelector('.post-time'); + const userImg = article.querySelector('.icon-container-user > .user-img-user'); + if (userImg) { + userImg.src = settings.TWITCH.USER_LOGO_URL; + } + + const postTime = article.querySelector('.post-time-user'); + + const iconContainer = article.querySelector('.icon-container-user'); + iconContainer.appendChild(postTime); + if (postTime) { postTime.innerText = getPostTime(); } - const msg = article.querySelector('.msg'); + const msg = article.querySelector('.msg-box-user'); if (msg) { msg.innerText = userText; } // Appends the message to the main chat box (shows the message) - showChatMessage(article); + showChatMessage(article, true); twitch.sendMessage(userText); diff --git a/src/js/messageTemplates.js b/src/js/messageTemplates.js index d699652..10e4eb0 100644 --- a/src/js/messageTemplates.js +++ b/src/js/messageTemplates.js @@ -9,21 +9,14 @@ const twitchTemplate = ` `.trim(); const userTemplate = ` -
- +
+ You -
-
-
-
- - You - - -

-
-
-
+ +
+ You +
+
`.trim(); const messageTemplate = ` diff --git a/src/js/renderer.js b/src/js/renderer.js index 012c350..45f7da7 100644 --- a/src/js/renderer.js +++ b/src/js/renderer.js @@ -1,6 +1,7 @@ const fs = require('fs'); const ini = require('ini'); const path = require('path'); // get directory path +const axios = require('axios'); const { ipcRenderer, shell } = require('electron'); // necessary electron libraries to send data to the app const io = require('socket.io-client'); @@ -195,16 +196,23 @@ Array.from(document.body.querySelectorAll('[tip]')).forEach((el) => { }; }); -function showChatMessage(article) { +function showChatMessage(article, isUser) { document.querySelector('#chatBox').appendChild(article); + let usernameHtml; + let msg; + let messages = Array.from(document.body.querySelectorAll('.msg-container')); + + if (isUser) { + usernameHtml = article.querySelector('.username-user'); + msg = article.querySelector('.msg-box-user'); + } else { + usernameHtml = article.querySelector('.username'); + msg = article.querySelector('.msg-box'); + } - const usernameHtml = article.querySelector('.username'); var style = getComputedStyle(usernameHtml); var style2 = getComputedStyle(usernameHtml); - const msg = article.querySelector('.msg-box'); - - const messages = Array.from(document.body.querySelectorAll('.msg-container')); const lastMessage = messages[messages.length - 1]; lastMessage.scrollIntoView({ behavior: 'smooth' }); } diff --git a/src/js/settings.js b/src/js/settings.js index 06a70b3..3e57ed7 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -29,7 +29,7 @@ function getGeneralSettings() { document.body.querySelector('#USE_VTUBER').checked = settings.MODULES.USE_VTUBER; document.body.querySelector('#VTUBER_URL').value = `http://localhost:${settings.GENERAL.PORT}/vtuber/`; showMenuButton('#btnBrowsersourceVtuber', settings.MODULES.USE_VTUBER); - document.body.querySelector('#USE_CHATBUBBLE').checked = settings.GENERAL.USE_CHATBUBBLE; + document.body.querySelector('#USE_CHATBUBBLE').checked = settings.MODULES.USE_CHATBUBBLE; document.body.querySelector('#CHATBUBBLE_URL').value = `http://localhost:${settings.GENERAL.PORT}/chat/`; showMenuButton('#btnBrowsersourceChat', settings.GENERAL.USE_CHATBUBBLE); diff --git a/src/js/twitch.js b/src/js/twitch.js index f316931..ef4f299 100644 --- a/src/js/twitch.js +++ b/src/js/twitch.js @@ -61,6 +61,9 @@ function displayTwitchMessage(logoUrl, username, messageObject, fileteredMessage postTime.innerText = getPostTime(); } + const iconContainer = article.querySelector('.icon-container'); + iconContainer.appendChild(postTime); + const msg = article.querySelector('.msg-box'); if (msg) { messageObject.forEach((entry) => { @@ -70,11 +73,11 @@ function displayTwitchMessage(logoUrl, username, messageObject, fileteredMessage msg.innerHTML += entry.html; } }); - msg.appendChild(postTime); + // msg.appendChild(postTime); } // Appends the message to the main chat box (shows the message) - showChatMessage(article); + showChatMessage(article, false); if (fileteredMessage) { sound.playVoice(fileteredMessage, logoUrl, username, msg); diff --git a/src/main.js b/src/main.js index 1c7fec6..0bd5fab 100644 --- a/src/main.js +++ b/src/main.js @@ -2,6 +2,7 @@ const { app, BrowserWindow, ipcMain } = require('electron'); const { writeIniFile } = require('write-ini-file'); const path = require('path'); const http = require('http'); +const kill = require('kill-process-by-name'); const ini = require('ini'); const fs = require('fs'); @@ -109,6 +110,7 @@ ipcMain.on('maximize-window', (event) => { ipcMain.on('close-window', (event) => { const browserWindow = BrowserWindow.fromWebContents(event.sender); + kill('loquendoBot_backend'); browserWindow.close(); app.quit(); }); @@ -176,7 +178,9 @@ async function createIniFile() { TWITCH: { USE_TWITCH: false, CHANNEL_NAME: '', - USERNAME: 'loquendo', + USERNAME: '', + USER_ID: '', + USER_LOGO_URL: '', OAUTH_TOKEN: '', CLIENT_ID: '2t206sj7rvtr1rutob3p627d13jch9', },