Raw Dogged that custom shoutout event and made HelixRequest easier to handle universally
This commit is contained in:
@@ -1,3 +1,2 @@
|
|||||||
MONGO_URI=
|
MONGO_URI=
|
||||||
MONGO_DB=
|
|
||||||
PREFIX=
|
PREFIX=
|
||||||
+1
-1
@@ -25,7 +25,7 @@ const initializeClients = async () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("client");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const dCreds = await credentialCollection.findOne({ service: 'discord' });
|
const dCreds = await credentialCollection.findOne({ service: 'discord' });
|
||||||
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
|
|||||||
+12
-35
@@ -3,41 +3,19 @@ module.exports = {
|
|||||||
name: 'Twitch/Message',
|
name: 'Twitch/Message',
|
||||||
once: false,
|
once: false,
|
||||||
async execute(channel, userstate, message, self, tClient) {
|
async execute(channel, userstate, message, self, tClient) {
|
||||||
const { dClient, mClient, makeTwitchRequest } = require('../../index')
|
const { dClient, mClient, getIDByName, updateChatMode} = require('../../index')
|
||||||
const knownBots = new Set(['streamlabs', 'nightbot', 'moobot', 'soundalerts', 'streamelements', 'remasuri_bot', 'commanderroot', 'x__hel_bot__x'])
|
const knownBots = new Set(['streamlabs', 'nightbot', 'moobot', 'soundalerts', 'streamelements', 'remasuri_bot', 'commanderroot', 'x__hel_bot__x'])
|
||||||
async function updateChatMode(userID, setTo) {
|
async function emitShoutoutInfo(channel, username) {
|
||||||
let state
|
// Function sends information to the Shoutout Handler if they haven't been shouted out before, default target: VIPs
|
||||||
if (setTo === 'on') {
|
|
||||||
state = true
|
|
||||||
} else if (setTo === 'off') {
|
|
||||||
state = false
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await Helix.chat.updateSettings(userID, process.env.MOD_ID, {
|
|
||||||
follower_mode: state,
|
|
||||||
follower_mode_duration: 0,
|
|
||||||
subscriber_mode: state,
|
|
||||||
emote_mode: state
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
async function getIDByName(user) {
|
|
||||||
// Function to convert Names to ID
|
|
||||||
const result = await Helix.users.get(user);
|
|
||||||
return result.data[0].id
|
|
||||||
}
|
|
||||||
async function verifyUser(channel, username) {
|
|
||||||
// Function checks if user is a target to shout out, default target: VIPs
|
|
||||||
let db = mClient.db("shoutouts")
|
let db = mClient.db("shoutouts")
|
||||||
let col = db.collection(channel)
|
let col = db.collection(channel)
|
||||||
|
let status = await col.findOne({ user: username })
|
||||||
|
|
||||||
let query = { user: username }
|
|
||||||
let status = await col.findOne(query)
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
dClient.emit('TwitchShoutout', "VIP", channel, username)
|
dClient.emit('Twitch/Shoutout', "VIP", channel, username, tClient)
|
||||||
await col.insertOne({
|
await col.insertOne({
|
||||||
user: username
|
user: username,
|
||||||
|
created_at: Date()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +39,7 @@ module.exports = {
|
|||||||
return tClient.say(channel, positive_Messages[rdm])
|
return tClient.say(channel, positive_Messages[rdm])
|
||||||
}
|
}
|
||||||
async function sendDonationAd() {
|
async function sendDonationAd() {
|
||||||
|
// Function to send static donate ad
|
||||||
const links = ["https://bit.ly/heltwittergoals", "https://bit.ly/donatetohel", "http://bit.ly/donothon_goals"]
|
const links = ["https://bit.ly/heltwittergoals", "https://bit.ly/donatetohel", "http://bit.ly/donothon_goals"]
|
||||||
tClient.say(channel, "Hel is currently celebrating her birthday with a donathon!")
|
tClient.say(channel, "Hel is currently celebrating her birthday with a donathon!")
|
||||||
tClient.say(channel, `You can see goals and incentives here: ${links[0]}`)
|
tClient.say(channel, `You can see goals and incentives here: ${links[0]}`)
|
||||||
@@ -85,9 +64,6 @@ module.exports = {
|
|||||||
const args = message.substring(1).split(" ")
|
const args = message.substring(1).split(" ")
|
||||||
const cmd = args[0]
|
const cmd = args[0]
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
// case "test":
|
|
||||||
// tClient.say(channel, "/me This is a test message")
|
|
||||||
// break;
|
|
||||||
case "mode":
|
case "mode":
|
||||||
if (userstate.badges.moderator != 1 && userstate.badges.broadcaster != 1) { break } // return stops all further operations, so you should just break out of switch
|
if (userstate.badges.moderator != 1 && userstate.badges.broadcaster != 1) { break } // return stops all further operations, so you should just break out of switch
|
||||||
let regex = /^(on|off)$/
|
let regex = /^(on|off)$/
|
||||||
@@ -98,7 +74,8 @@ module.exports = {
|
|||||||
sendDonationAd()
|
sendDonationAd()
|
||||||
break;
|
break;
|
||||||
case "test":
|
case "test":
|
||||||
console.log(await makeTwitchRequest('games/top'))
|
await emitShoutoutInfo(channel, userstate.username)
|
||||||
|
// console.log(await getIDByName(userstate))
|
||||||
break;
|
break;
|
||||||
case "positivity":
|
case "positivity":
|
||||||
sendPositivity()
|
sendPositivity()
|
||||||
@@ -109,8 +86,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------VIP Handler-------------------
|
// ---------------VIP Handler-------------------
|
||||||
// if (!userstate.badges?.vip) { return }
|
if (!userstate.badges?.vip) { return }
|
||||||
// verifyUser(channel, userstate.username)
|
emitShoutoutInfo(channel, userstate.username)
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'Twitch/Raided',
|
name: 'Twitch/Raided',
|
||||||
once: false,
|
once: false,
|
||||||
execute(stream) {
|
execute(channel, username, raiders, tClient) {
|
||||||
console.log(`Connected to ${stream.address}`)
|
const { dClient } = require('../../index')
|
||||||
|
tClient.say(channel, `${username} entrusted us with ${raiders} people of their community, welcome xhelxBlankie`)
|
||||||
|
dClient.emit('Twitch/Shoutout', "RAID", channel, username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Event triggered by Custom Shoutout emitted in Messages.js
|
||||||
|
const soBuffer = new Map();
|
||||||
|
let isShoutoutInProgress = false; // Track shoutout status to avoid overlapping shoutouts
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'Twitch/Shoutout',
|
||||||
|
once: false,
|
||||||
|
async execute(reason, channel, username, tClient) {
|
||||||
|
const { getIDByName, getChannelInformation } = require('../../index');
|
||||||
|
|
||||||
|
// Function to perform shoutout (Nightbot or custom)
|
||||||
|
async function doShoutouts(channel, user) {
|
||||||
|
try {
|
||||||
|
// Use Nightbot command !so
|
||||||
|
// tClient.say(channel, `!so ${user}`); // Send the shoutout command
|
||||||
|
|
||||||
|
// Use custom shoutout
|
||||||
|
let channelID = await getIDByName(user)
|
||||||
|
let res = await getChannelInformation(channelID)
|
||||||
|
let channelInfo = res?.data[0]
|
||||||
|
let shoutoutMessage = `Check out ${channelInfo.broadcaster_name} over at https://twitch.tv/${channelInfo.broadcaster_login}! They were last playing ${channelInfo.game_name}`
|
||||||
|
tClient.say(channel, shoutoutMessage)
|
||||||
|
|
||||||
|
console.log(`Shouting out ${user} in channel ${channel} for reason: ${reason}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during shoutout:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle buffered shoutout queue
|
||||||
|
async function processShoutoutQueue() {
|
||||||
|
if (soBuffer.size === 0 || isShoutoutInProgress) {
|
||||||
|
return; // Exit if no users are in the buffer or a shoutout is in progress
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set shoutout in progress flag
|
||||||
|
isShoutoutInProgress = true;
|
||||||
|
|
||||||
|
// Get the next user in the buffer
|
||||||
|
const [user, channel] = soBuffer.entries().next().value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await doShoutouts(channel, user); // Perform the shoutout
|
||||||
|
soBuffer.delete(user); // Remove user after shoutout is done
|
||||||
|
// console.log(`Shoutout for ${user} completed in channel ${channel}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error while processing shoutout:', error);
|
||||||
|
} finally {
|
||||||
|
// Reset shoutout in progress flag and schedule next shoutout
|
||||||
|
isShoutoutInProgress = false;
|
||||||
|
setTimeout(processShoutoutQueue, 5000); // Schedule next shoutout after cooldown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add user to the buffer if not already present
|
||||||
|
if (!soBuffer.has(username)) {
|
||||||
|
soBuffer.set(username, channel);
|
||||||
|
console.log(`Added ${username} to the shoutout buffer. Reason: ${reason}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the shoutout queue processing
|
||||||
|
setTimeout(processShoutoutQueue, 5000); // Ensure 5-second cooldown between shoutouts
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,7 +2,17 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'Twitch/Subscribers',
|
name: 'Twitch/Subscribers',
|
||||||
once: false,
|
once: false,
|
||||||
execute(stream) {
|
async execute(channel, enabled) {
|
||||||
console.log(`Connected to ${stream.address}`)
|
const { mClient } = require('../..')
|
||||||
|
|
||||||
|
let db = mClient.db('shoutouts')
|
||||||
|
let col = db.collection(channel)
|
||||||
|
|
||||||
|
// Drop Shoutout Collection to reset
|
||||||
|
try {
|
||||||
|
await col.drop()
|
||||||
|
} catch (error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -12,10 +12,10 @@ module.exports = (dClient) => {
|
|||||||
const filePath = path.join(eventsPath, file)
|
const filePath = path.join(eventsPath, file)
|
||||||
const event = require(filePath)
|
const event = require(filePath)
|
||||||
if (event.once) {
|
if (event.once) {
|
||||||
dClient.once(event.name, (...args) => event.execute(...args, dClient))
|
dClient.once(event.name, (...args) => event.execute(...args))
|
||||||
// added client to commomerate global usage
|
// added client to commomerate global usage
|
||||||
} else {
|
} else {
|
||||||
dClient.on(event.name, (...args) => event.execute(...args, dClient))
|
dClient.on(event.name, (...args) => event.execute(...args))
|
||||||
// added client to commomerate global usage
|
// added client to commomerate global usage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const axios = require('axios');
|
|||||||
|
|
||||||
// Validate required environment variables
|
// Validate required environment variables
|
||||||
function validateEnvVariables() {
|
function validateEnvVariables() {
|
||||||
const requiredVars = ['MONGO_URI', 'MONGO_DB'];
|
const requiredVars = ['MONGO_URI'];
|
||||||
const missingVars = requiredVars.filter((varName) => !process.env[varName]);
|
const missingVars = requiredVars.filter((varName) => !process.env[varName]);
|
||||||
|
|
||||||
if (missingVars.length > 0) {
|
if (missingVars.length > 0) {
|
||||||
@@ -36,7 +36,7 @@ initializeMongoClient();
|
|||||||
|
|
||||||
// Function to save tokens to MongoDB
|
// Function to save tokens to MongoDB
|
||||||
async function saveToken(access_token, refresh_token, expiresIn, scope) {
|
async function saveToken(access_token, refresh_token, expiresIn, scope) {
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
|
|
||||||
const tokenData = {
|
const tokenData = {
|
||||||
@@ -57,7 +57,7 @@ async function saveToken(access_token, refresh_token, expiresIn, scope) {
|
|||||||
|
|
||||||
// Function to retrieve stored tokens from MongoDB
|
// Function to retrieve stored tokens from MongoDB
|
||||||
async function getStoredToken() {
|
async function getStoredToken() {
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
|
|
||||||
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
@@ -82,7 +82,7 @@ async function refreshAccessToken() {
|
|||||||
isRefreshingToken = true;
|
isRefreshingToken = true;
|
||||||
refreshPromise = (async () => {
|
refreshPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const credentials = await credentialCollection.findOne({ service: 'twitch' });
|
const credentials = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
const token = await getStoredToken();
|
const token = await getStoredToken();
|
||||||
@@ -121,8 +121,8 @@ async function refreshAccessToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to make requests to the Twitch API
|
// Function to make requests to the Twitch API
|
||||||
async function makeTwitchRequest(endpoint) {
|
async function makeHelixRequest(requestType, endpoint, params, data) {
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const credentials = await credentialCollection.findOne({ service: 'twitch' });
|
const credentials = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
|
|
||||||
@@ -133,27 +133,77 @@ async function makeTwitchRequest(endpoint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`https://api.twitch.tv/helix/${endpoint}`, {
|
var response
|
||||||
headers: {
|
var headers = {
|
||||||
'Client-Id': credentials.client_id,
|
'Client-Id': credentials.client_id,
|
||||||
'Authorization': `Bearer ${token.access_token}`,
|
'Authorization': `Bearer ${token.access_token}`,
|
||||||
},
|
}
|
||||||
});
|
switch (requestType) {
|
||||||
|
case 'GET':
|
||||||
|
response = await axios.get(`https://api.twitch.tv/helix/${endpoint}`, { headers, params });
|
||||||
|
return response.data;
|
||||||
|
break;
|
||||||
|
case 'PATCH':
|
||||||
|
response = await axios.patch(`https://api.twitch.tv/helix/${endpoint}`, data, { headers, params })
|
||||||
|
return response.data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
console.log('Access token expired. Refreshing...');
|
console.log('Access token expired. Refreshing...');
|
||||||
const newToken = await refreshAccessToken();
|
const newToken = await refreshAccessToken();
|
||||||
if (newToken) {
|
if (newToken) {
|
||||||
return makeTwitchRequest(endpoint); // Retry with new token
|
return makeHelixRequest(requestType, endpoint, params, data); // Retry with new token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('Twitch API request failed:', error.response?.data || error.message);
|
console.error('Twitch API request failed:', error.response?.data || error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function getIDByName(user) {
|
||||||
|
// Function to convert Names to ID
|
||||||
|
let endpoint = `users`
|
||||||
|
let params = {
|
||||||
|
login: user
|
||||||
|
}
|
||||||
|
let res = await makeHelixRequest('GET', endpoint, params)
|
||||||
|
return res?.data[0]?.id
|
||||||
|
}
|
||||||
|
async function updateChatMode(broadcaster_id, setTo) {
|
||||||
|
let state
|
||||||
|
if (setTo === 'on') {
|
||||||
|
state = true
|
||||||
|
} else if (setTo === 'off') {
|
||||||
|
state = false
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let db = mClient.db('clients')
|
||||||
|
let credentialCollection = db.collection('credentials')
|
||||||
|
let credentials = await credentialCollection.findOne({ service: 'twitch' })
|
||||||
|
|
||||||
|
let endpoint = `chat/settings`
|
||||||
|
let data = {
|
||||||
|
"follower_mode": state,
|
||||||
|
"follower_mode_duration": 0,
|
||||||
|
"subscriber_mode": state,
|
||||||
|
"emote_mode": state
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
broadcaster_id: broadcaster_id,
|
||||||
|
moderator_id: credentials.mod_id
|
||||||
|
}
|
||||||
|
makeHelixRequest('PATCH', endpoint, params, data)
|
||||||
|
|
||||||
|
}
|
||||||
|
async function getChannelInformation(broadcaster_id){
|
||||||
|
return response = await makeHelixRequest('GET', 'channels', { broadcaster_id: broadcaster_id })
|
||||||
|
}
|
||||||
// Initialize Discord Client
|
// Initialize Discord Client
|
||||||
const dClient = new Client({
|
const dClient = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
@@ -171,7 +221,7 @@ fs.readdirSync('./handlers').forEach((handler) => {
|
|||||||
// Function to log in to Discord
|
// Function to log in to Discord
|
||||||
async function dLogin() {
|
async function dLogin() {
|
||||||
try {
|
try {
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const dCreds = await credentialCollection.findOne({ service: 'discord' });
|
const dCreds = await credentialCollection.findOne({ service: 'discord' });
|
||||||
|
|
||||||
@@ -191,7 +241,7 @@ dLogin();
|
|||||||
// Function to log in to Twitch
|
// Function to log in to Twitch
|
||||||
async function tLogin() {
|
async function tLogin() {
|
||||||
const channels = ['desq_blocki'];
|
const channels = ['desq_blocki'];
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
|
|
||||||
@@ -246,7 +296,7 @@ async function tLogin() {
|
|||||||
console.warn('Twitch client already disconnected:', err.message);
|
console.warn('Twitch client already disconnected:', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = mClient.db(process.env.MONGO_DB);
|
const db = mClient.db("clients");
|
||||||
const credentialCollection = db.collection('credentials');
|
const credentialCollection = db.collection('credentials');
|
||||||
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
|
|
||||||
@@ -277,4 +327,4 @@ process.on('unhandledRejection', (reason, promise) => {
|
|||||||
// Prevent app from crashing
|
// Prevent app from crashing
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = { mClient, dClient, makeTwitchRequest };
|
module.exports = { mClient, dClient, makeHelixRequest, getIDByName, updateChatMode, getChannelInformation };
|
||||||
Reference in New Issue
Block a user