twtitch emote support

This commit is contained in:
Khyretos 2023-12-29 06:17:18 +01:00
parent 525cb6116e
commit f7e3248b90
7 changed files with 218 additions and 166 deletions

3
.gitignore vendored
View file

@ -104,4 +104,5 @@ src/sounds/tts/*
loquendoBot_backend.spec
forge.config.js
backend/*
src/backend/loquendoBot_backend.exe
src/backend/loquendoBot_backend.exe
src/config/twitch-emotes.json

View file

@ -336,6 +336,17 @@
tip="Test Twitch credentials"
></i>
</div>
<div class="AdvancedMenuRow inputTwitch">
<div class="AdvancedMenuLabel">Get Twitch emotes</div>
<button type="text" class="AdvancedMenuButton" id="GetTwitchEmotes">
<img src="https://static-cdn.jtvnw.net/emoticons/v2/25/static/light/1.0" />
</button>
<i
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
id="Info_TWITCH_GET_EMOTES"
tip="Get Twitch emotes available to you"
></i>
</div>
</fieldset>
<fieldset id="AdvancedMenuServer" class="AdvancedMenu">

View file

@ -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 = `
<!DOCTYPE html>
<html>
@ -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 };

View file

@ -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 };

View file

@ -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;
});
}

View file

@ -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');

View file

@ -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 };