diff --git a/.gitignore b/.gitignore index cd88a68..763e324 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,5 @@ src/sounds/tts/* loquendoBot_backend.spec forge.config.js backend/* -src/backend/loquendoBot_backend.exe \ No newline at end of file +src/backend/loquendoBot_backend.exe +src/config/twitch-emotes.json diff --git a/src/index.html b/src/index.html index 89111e2..cbf6544 100644 --- a/src/index.html +++ b/src/index.html @@ -336,6 +336,17 @@ tip="Test Twitch credentials" > +
+
Get Twitch emotes
+ + +
diff --git a/src/js/auth.js b/src/js/auth.js index 926ea4b..b303bc4 100644 --- a/src/js/auth.js +++ b/src/js/auth.js @@ -4,7 +4,7 @@ const twitchAuthentication = () => new Promise(resolve => { const http = require('http'); const redirectUri = 'http://localhost:1989/auth'; - const scopes = ['chat:edit', 'chat:read']; + const scopes = ['chat:edit', 'chat:read', 'user:read:follows', 'user:read:subscriptions']; const express = require('express'); const tempAuthServer = express(); @@ -24,10 +24,6 @@ const twitchAuthentication = () => next(); }); - // function stopServer() { - // tempAuthServer.close(); - // } - const htmlString = ` @@ -99,119 +95,11 @@ function getTwitchUserId() { }); } -function getTwitchSubscriptions(channelId) { - // Get user Logo with access token - options = { - method: 'GET', - url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`, - headers: { - 'Client-ID': settings.TWITCH.CLIENT_ID, - Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` - } - }; - - axios - .request(options) - .then(responseLogoUrl => { - console.log(responseLogoUrl); - }) - .catch(error => { - console.error(error); - }); -} - -function getTwitchChannelEmotes(channelId) { - // Get user Logo with access token - options = { - method: 'GET', - url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`, - headers: { - 'Client-ID': settings.TWITCH.CLIENT_ID, - Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` - } - }; - - axios - .request(options) - .then(responseLogoUrl => { - console.log(responseLogoUrl); - }) - .catch(error => { - console.error(error); - }); -} - -function getTwitchChannelFollows(channelId) { - // Get user Logo with access token - options = { - method: 'GET', - url: 'https://api.twitch.tv/helix/streams/followed', - headers: { - 'Client-ID': settings.TWITCH.CLIENT_ID, - Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` - } - }; - - axios - .request(options) - .then(responseLogoUrl => { - console.log(responseLogoUrl); - }) - .catch(error => { - console.error(error); - }); -} - -function getTwitchChannelSubscriptions(channelId) { - // Get user Logo with access token - options = { - method: 'GET', - url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`, - headers: { - 'Client-ID': settings.TWITCH.CLIENT_ID, - Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` - } - }; - - axios - .request(options) - .then(responseLogoUrl => { - console.log(responseLogoUrl); - }) - .catch(error => { - console.error(error); - }); -} - -function getTwitchChannelId() { - // Get user Logo with access token - options = { - method: 'GET', - url: 'https://api.twitch.tv/helix/users?login=MelvyCoral', - headers: { - 'Client-ID': settings.TWITCH.CLIENT_ID, - Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` - } - }; - - axios - .request(options) - .then(responseLogoUrl => { - // console.log(responseLogoUrl); - getTwitchChannelEmotes(responseLogoUrl.data.data[0].id); - }) - .catch(error => { - console.error(error); - }); -} - function getTwitchOauthToken() { - // getTwitchChannelId(); - getTwitchGLobalEmotes(); - // return twitchAuthentication().then(res => { - // getTwitchUserId(); - // return res; - // }); + return twitchAuthentication().then(res => { + getTwitchUserId(); + return res; + }); } module.exports = { getTwitchOauthToken }; diff --git a/src/js/customEmojis.js b/src/js/customEmojis.js deleted file mode 100644 index a1a951a..0000000 --- a/src/js/customEmojis.js +++ /dev/null @@ -1,9 +0,0 @@ -const customEmojis = [ - { - name: 'sakuraestaKleefeliz', - shortcodes: ['sakuraestaKleefeliz'], - url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0' - } -]; - -module.exports = { customEmojis }; diff --git a/src/js/renderer.js b/src/js/renderer.js index 16ba76b..a3d33a3 100644 --- a/src/js/renderer.js +++ b/src/js/renderer.js @@ -25,6 +25,8 @@ const settings = main.settings; const googleVoices = fs.readFileSync(path.join(__dirname, './config/googleVoices.txt')).toString().split('\r\n'); // TODO: remove amazon voices txt and use api instead (sakura project has it) const amazonVoices = fs.readFileSync(path.join(__dirname, './config/amazonVoices.txt')).toString().split('\r\n'); +const emoteListSavePath = + main.isPackaged === true ? path.join(resourcesPath, './twitch-emotes.json') : path.join(resourcesPath, './config/twitch-emotes.json'); // html elements const root = document.documentElement; @@ -273,25 +275,13 @@ function setZoomLevel(currentZoom, zoomIn) { document.body.querySelector('#ZOOMLEVEL').value = (settings.GENERAL.ZOOMLEVEL * 100).toFixed(0); } -// const customEmojix = [ -// { -// name: 'sakuraestaKleefeliz', -// shortcodes: ['sakuraestaKleefeliz'], -// url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0', -// category: 'Sakura' -// } -// ]; - -// const customEmojiy = [ -// { -// name: 'sakuraestaKleefeliz', -// shortcodes: ['sakuraestaKleefeliz'], -// url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0', -// category: 'Sakurax' -// } -// ]; - -// emojiPicker.customEmoji = customEmojix; -// emojiPicker.customEmoji = customEmojiy; - -// console.log(emojiPicker.database.getEmojiBySearchQuery('Kappa')); +if (fs.existsSync(emoteListSavePath)) { + fs.readFile(emoteListSavePath, 'utf8', (error, data) => { + if (error) { + console.log(error); + return; + } + const emotes = JSON.parse(data); + emojiPicker.customEmoji = emotes; + }); +} diff --git a/src/js/settings.js b/src/js/settings.js index ee3ddd0..39a9f59 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -137,19 +137,12 @@ document.body.querySelector('#notificationSoundAudioDevice').addEventListener('c document.body.querySelector('#TWITCH_CHANNEL_NAME').addEventListener('change', () => { settings.TWITCH.CHANNEL_NAME = document.body.querySelector('#TWITCH_CHANNEL_NAME').value; fs.writeFileSync(settingsPath, ini.stringify(settings)); - - const button = document.body.querySelector('#TestTwitchCredentials'); - button.className = 'AdvancedMenuButton'; createNotification('Saved Channel name, please restart the application to reset twitch service', 'warning'); }); document.body.querySelector('#TWITCH_OAUTH_TOKEN').addEventListener('change', () => { settings.TWITCH.OAUTH_TOKEN = document.body.querySelector('#TWITCH_OAUTH_TOKEN').value; fs.writeFileSync(settingsPath, ini.stringify(settings)); - createNotification('Saved OAuth token!', 'success'); - - const button = document.body.querySelector('#TestTwitchCredentials'); - button.className = 'AdvancedMenuButton'; createNotification('Saved OAuth token, please restart the application to reset twitch service', 'warning'); }); @@ -340,6 +333,11 @@ document.body.querySelector('#TestTwitchCredentials').addEventListener('click', // resetTwitch(; }); +document.body.querySelector('#GetTwitchEmotes').addEventListener('click', () => { + twitch.getUserAvailableTwitchEmotes('#GetTwitchEmotes'); + // resetTwitch(; +}); + function toggleTwitch() { const toggle = settings.TWITCH.USE_TWITCH; const inputs = document.getElementsByClassName('inputTwitch'); diff --git a/src/js/twitch.js b/src/js/twitch.js index 781a85a..115e2b2 100644 --- a/src/js/twitch.js +++ b/src/js/twitch.js @@ -1,10 +1,11 @@ -/* global client, customEmojis, emojiPicker, settings, options, sound, showChatMessage, messageTemplates, getPostTime */ +/* global client, fs, main, path, resourcesPath, customEmojis, emojiPicker, settings, options, sound, showChatMessage, messageTemplates, getPostTime */ const tmi = require('tmi.js'); const axios = require('axios'); let client = null; let logoUrl = null; +const twitchChannels = []; function sendMessage(message) { client.say(settings.TWITCH.CHANNEL_NAME, message).catch(console.error); @@ -144,17 +145,156 @@ client.on('message', (channel, tags, message, self) => { getProfileImage(tags['user-id'], tags['display-name'], messageObject, filteredMessage); }); -function formatTwitchEmojis(emojis, name) { - emojis.forEach(emoji => { +function saveTwitchEmotesToFile(TwitchEmotes) { + const data = JSON.stringify(TwitchEmotes); + const savePath = + main.isPackaged === true ? path.join(resourcesPath, './twitch-emotes.json') : path.join(resourcesPath, './config/twitch-emotes.json'); + // console.log(savePath); + fs.writeFile(savePath, data, error => { + // throwing the error + // in case of a writing problem + if (error) { + // logging the error + console.error(error); + + throw error; + } + + // console.log('twitch-emotes.json written correctly'); + }); +} + +async function formatGlobalTwitchEmotes(emotes, name) { + for (const emote of emotes) { const emojiToBeAdded = { - name: emoji.name, - shortcodes: [emoji.name], - url: emoji.images.url_1x, + name: emote.name, + shortcodes: [emote.name], + url: emote.images.url_1x, category: name }; + await customEmojis.push(emojiToBeAdded); + } + emojiPicker.customEmoji = customEmojis; + saveTwitchEmotesToFile(customEmojis); +} + +function formatFollowedChannelsTwitchEmotes(channel) { + if (channel.emotes.length === 0) { + return; + } + + channel.emotes.forEach(emote => { + if (emote.emote_type === 'bitstier') { + return; + } + if (emote.emote_type === 'subscriptions' && parseInt(channel.tier) < parseInt(emote.tier)) { + return; + } + if (emote.emote_type === 'follower ' && parseInt(channel.tier) === '0') { + return; + } + const emojiToBeAdded = { + name: emote.name, + shortcodes: [emote.name], + url: emote.images.url_1x, + category: channel.broadcaster_name + }; customEmojis.push(emojiToBeAdded); }); emojiPicker.customEmoji = customEmojis; + saveTwitchEmotesToFile(customEmojis); +} + +function getTwitchChannelFollows(paginationToken) { + let url = ''; + if (!paginationToken) { + url = `https://api.twitch.tv/helix/channels/followed?user_id=${settings.TWITCH.USER_ID}&first=100`; + } else { + url = `https://api.twitch.tv/helix/channels/followed?user_id=${settings.TWITCH.USER_ID}&after=${paginationToken}`; + } + options = { + method: 'GET', + url: url, + headers: { + 'Client-ID': settings.TWITCH.CLIENT_ID, + Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` + } + }; + + axios + .request(options) + .then(responseLogoUrl => { + // console.log(responseLogoUrl); + + responseLogoUrl.data.data.forEach(channel => { + twitchChannels.push({ broadcaster_id: channel.broadcaster_id, broadcaster_name: channel.broadcaster_name, tier: '0' }); + }); + + if (Object.keys(responseLogoUrl.data.pagination).length !== 0) { + getTwitchChannelFollows(responseLogoUrl.data.pagination.cursor); + } else { + getTwitchChannelSubscriptions(twitchChannels); + } + }) + .catch(error => { + console.error(error); + }); +} + +function getTwitchChannelSubscriptions(channels) { + channels.forEach(channel => { + options = { + method: 'GET', + url: `https://api.twitch.tv/helix/subscriptions/user?broadcaster_id=${channel.broadcaster_id}&user_id=${settings.TWITCH.USER_ID}`, + headers: { + 'Client-ID': settings.TWITCH.CLIENT_ID, + Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` + } + }; + + axios + .request(options) + .then(responseLogoUrl => { + // console.log(responseLogoUrl); + const objIndex = twitchChannels.findIndex(obj => obj.broadcaster_id === channel.broadcaster_id); + twitchChannels[objIndex].tier = responseLogoUrl.data.data[0].tier; + getTwitchChannelEmotes(channel); + }) + .catch(error => { + if (error.response.status !== 404) { + console.error(error); + } + }); + }); +} + +function getTwitchChannelEmotes(channel) { + // Get user Logo with access token + options = { + method: 'GET', + url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channel.broadcaster_id}`, + headers: { + 'Client-ID': settings.TWITCH.CLIENT_ID, + Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` + } + }; + + axios + .request(options) + .then(responseLogoUrl => { + // console.log(channel.broadcaster_id); + if (channel.broadcaster_id === '98553286') { + // console.log(responseLogoUrl); + // console.log(channel); + } + if (responseLogoUrl.data.data.length !== 0) { + channel.emotes = responseLogoUrl.data.data; + formatFollowedChannelsTwitchEmotes(channel); + } + }) + .catch(error => { + console.error(error); + }); } function getTwitchGLobalEmotes() { @@ -171,7 +311,7 @@ function getTwitchGLobalEmotes() { axios .request(options) .then(responseLogoUrl => { - formatTwitchEmojis(responseLogoUrl.data.data, 'Twitch Global'); + formatGlobalTwitchEmotes(responseLogoUrl.data.data, 'Twitch Global'); // console.log(responseLogoUrl); }) .catch(error => { @@ -179,8 +319,41 @@ function getTwitchGLobalEmotes() { }); } -if (settings.TWITCH.OAUTH_TOKEN) { - getTwitchGLobalEmotes(); +function getCurrentTwitchChannelId(channelId) { + let channel = {}; + options = { + method: 'GET', + url: `https://api.twitch.tv/helix/users?${channelId !== undefined ? 'id=' + channelId : 'login=' + settings.TWITCH.CHANNEL_NAME}`, + headers: { + 'Client-ID': settings.TWITCH.CLIENT_ID, + Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` + } + }; + + axios + .request(options) + .then(responseData => { + // console.log(responseData); + channel = { broadcaster_id: responseData.data.data[0].id, broadcaster_name: responseData.data.data[0].display_name, tier: '0' }; + if (responseData.data.data[0].id !== settings.TWITCH.USER_ID) { + getCurrentTwitchChannelId(settings.TWITCH.USER_ID); + channel.tier = '0'; + } else { + channel.tier = '3000'; + } + getTwitchChannelEmotes(channel); + }) + .catch(error => { + console.error(error); + }); } -module.exports = { sendMessage, ping, client }; +async function getUserAvailableTwitchEmotes() { + if (settings.TWITCH.OAUTH_TOKEN) { + await getTwitchGLobalEmotes(); + await getTwitchChannelFollows(); + await getCurrentTwitchChannelId(); + } +} + +module.exports = { sendMessage, ping, client, getUserAvailableTwitchEmotes };