tClient will now try to successfully reconnect and NOT crash the entire app
This commit is contained in:
+3
-3
@@ -1,3 +1,3 @@
|
|||||||
mongo_uri=
|
MONGO_URI=
|
||||||
mongo_db=
|
MONGO_DB=
|
||||||
prefix=
|
PREFIX=
|
||||||
@@ -1 +1,2 @@
|
|||||||
.env
|
.env
|
||||||
|
TODO.txt
|
||||||
+14
-14
@@ -2,15 +2,22 @@
|
|||||||
const { MongoClient } = require('mongodb');
|
const { MongoClient } = require('mongodb');
|
||||||
const { Client, GatewayIntentBits } = require('discord.js');
|
const { Client, GatewayIntentBits } = require('discord.js');
|
||||||
const tmi = require('tmi.js');
|
const tmi = require('tmi.js');
|
||||||
const channels = ['x__hel__x','desq_blocki']
|
const channels = ['x__hel__x', 'desq_blocki'];
|
||||||
|
|
||||||
|
let mClient, dClient, tClient; // Global references
|
||||||
|
|
||||||
const initializeClients = async () => {
|
const initializeClients = async () => {
|
||||||
|
// Disconnect existing clients if they exist
|
||||||
|
if (tClient) await tClient.disconnect().catch(() => {});
|
||||||
|
if (dClient) await dClient.destroy().catch(() => {});
|
||||||
|
if (mClient) await mClient.close().catch(() => {});
|
||||||
|
|
||||||
// Initialize MongoDB Client
|
// Initialize MongoDB Client
|
||||||
const mClient = new MongoClient(process.env.mongo_uri);
|
mClient = new MongoClient(process.env.MONGO_URI);
|
||||||
await mClient.connect();
|
await mClient.connect();
|
||||||
|
|
||||||
// Initialize Discord Client
|
// Initialize Discord Client
|
||||||
const dClient = new Client({
|
dClient = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
GatewayIntentBits.Guilds,
|
GatewayIntentBits.Guilds,
|
||||||
GatewayIntentBits.GuildMembers,
|
GatewayIntentBits.GuildMembers,
|
||||||
@@ -18,31 +25,24 @@ const initializeClients = async () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch Discord credentials from the database
|
const db = mClient.db(process.env.MONGO_DB);
|
||||||
const db = mClient.db(process.env.mongo_db);
|
|
||||||
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' });
|
||||||
|
|
||||||
await dClient.login(dCreds.token);
|
await dClient.login(dCreds.token);
|
||||||
|
|
||||||
// Initialize Twitch Client
|
// Initialize Twitch Client
|
||||||
const tClient = new tmi.Client({
|
let tClient = new tmi.Client({
|
||||||
options: { debug: false },
|
options: { debug: false },
|
||||||
connection: {
|
connection: { reconnect: true, secure: true },
|
||||||
reconnect: true,
|
|
||||||
secure: true,
|
|
||||||
},
|
|
||||||
identity: {
|
identity: {
|
||||||
username: tCreds.username,
|
username: tCreds.username,
|
||||||
password: `oauth:${tCreds.token.access_token}`,
|
password: `oauth:${tCreds.token.access_token}`,
|
||||||
},
|
},
|
||||||
channels: channels, // Replace with your channel
|
channels: channels,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tClient.connect();
|
|
||||||
|
|
||||||
return { mClient, dClient, tClient };
|
return { mClient, dClient, tClient };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+57
-44
@@ -3,9 +3,9 @@ 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 } = require('../../index')
|
const { dClient, mClient, makeTwitchRequest } = 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 updateChatMode(userID, setTo) {
|
||||||
let state
|
let state
|
||||||
if (setTo === 'on') {
|
if (setTo === 'on') {
|
||||||
state = true
|
state = true
|
||||||
@@ -14,19 +14,21 @@ async function updateChatMode(userID, setTo) {
|
|||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await Helix.chat.updateSettings(userID, process.env.mod_id, {
|
await Helix.chat.updateSettings(userID, process.env.MOD_ID, {
|
||||||
follower_mode: state,
|
follower_mode: state,
|
||||||
follower_mode_duration: 0,
|
follower_mode_duration: 0,
|
||||||
subscriber_mode: state,
|
subscriber_mode: state,
|
||||||
emote_mode: state
|
emote_mode: state
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
async function getIDByName(user) {
|
async function getIDByName(user) {
|
||||||
|
// Function to convert Names to ID
|
||||||
const result = await Helix.users.get(user);
|
const result = await Helix.users.get(user);
|
||||||
return result.data[0].id
|
return result.data[0].id
|
||||||
}
|
}
|
||||||
async function verifyUser(channel, username) {
|
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)
|
||||||
|
|
||||||
@@ -38,41 +40,9 @@ async function verifyUser(channel, username) {
|
|||||||
user: username
|
user: username
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (self) { return }
|
|
||||||
if (knownBots.has(userstate.username)) { return }
|
|
||||||
|
|
||||||
if (message.toLowerCase().includes('caw')) {
|
|
||||||
tClient.say(channel, 'CAW!')
|
|
||||||
} else if (message.toLowerCase().includes('kweh')) {
|
|
||||||
tClient.say(channel, 'KWEH!')
|
|
||||||
} else if (message.toLowerCase().includes('kaw')) {
|
|
||||||
tClient.say(channel, 'KAW!')
|
|
||||||
}
|
}
|
||||||
|
async function sendPositivity() {
|
||||||
|
// Function sends a random string of predetermined positivity, Donothon Top Donator Reward
|
||||||
if (message.startsWith(process.env.prefix)) {
|
|
||||||
const args = message.substring(1).split(" ")
|
|
||||||
const cmd = args[0]
|
|
||||||
switch (cmd) {
|
|
||||||
// case "test":
|
|
||||||
// tClient.say(channel, "/me This is a test message")
|
|
||||||
// break;
|
|
||||||
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
|
|
||||||
let regex = /^(on|off)$/
|
|
||||||
if (!args[1].match(regex)) { break }
|
|
||||||
updateChatMode(await getIDByName(channel.substring(1)), args[1])
|
|
||||||
break;
|
|
||||||
// case "donate":
|
|
||||||
// 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, `You can see goals and incentives here: ${links[0]}`)
|
|
||||||
// tClient.say(channel, `You can donate here: ${links[1]}`)
|
|
||||||
// tClient.say(channel, `Terms, conditions, rules and how rewards work are found here: ${links[2]}`)
|
|
||||||
// tClient.say(channel, `All donations and gifts are greatly appreciated, but please do so responsibly!`)
|
|
||||||
// break;
|
|
||||||
case "positivity":
|
|
||||||
let positive_Messages = ["You are so very worthy of all the love and support that may come your way.",
|
let positive_Messages = ["You are so very worthy of all the love and support that may come your way.",
|
||||||
"There are many things which draw us together. In this time and place, that lovely subject is you, Hel.",
|
"There are many things which draw us together. In this time and place, that lovely subject is you, Hel.",
|
||||||
"The happiness and joy you add to the world is worth more than most anything.",
|
"The happiness and joy you add to the world is worth more than most anything.",
|
||||||
@@ -88,16 +58,59 @@ async function verifyUser(channel, username) {
|
|||||||
"Watching you succeed and achieve your heart's desires gives me so much motivation and I am very grateful for it."]
|
"Watching you succeed and achieve your heart's desires gives me so much motivation and I am very grateful for it."]
|
||||||
|
|
||||||
let rdm = Math.floor(Math.random() * positive_Messages.length)
|
let rdm = Math.floor(Math.random() * positive_Messages.length)
|
||||||
tClient.say(channel, positive_Messages[rdm])
|
return tClient.say(channel, positive_Messages[rdm])
|
||||||
|
}
|
||||||
|
async function sendDonationAd() {
|
||||||
|
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, `You can see goals and incentives here: ${links[0]}`)
|
||||||
|
tClient.say(channel, `You can donate here: ${links[1]}`)
|
||||||
|
tClient.say(channel, `Terms, conditions, rules and how rewards work are found here: ${links[2]}`)
|
||||||
|
tClient.say(channel, `All donations and gifts are greatly appreciated, but please do so responsibly!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self) { return }
|
||||||
|
if (knownBots.has(userstate.username)) { return }
|
||||||
|
|
||||||
|
if (message.toLowerCase().includes('caw')) {
|
||||||
|
tClient.say(channel, 'CAW!')
|
||||||
|
} else if (message.toLowerCase().includes('kweh')) {
|
||||||
|
tClient.say(channel, 'KWEH!')
|
||||||
|
} else if (message.toLowerCase().includes('kaw')) {
|
||||||
|
tClient.say(channel, 'KAW!')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (message.startsWith(process.env.PREFIX)) {
|
||||||
|
const args = message.substring(1).split(" ")
|
||||||
|
const cmd = args[0]
|
||||||
|
switch (cmd) {
|
||||||
|
// case "test":
|
||||||
|
// tClient.say(channel, "/me This is a test message")
|
||||||
|
// break;
|
||||||
|
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
|
||||||
|
let regex = /^(on|off)$/
|
||||||
|
if (!args[1].match(regex)) { break }
|
||||||
|
updateChatMode(await getIDByName(channel.substring(1)), args[1])
|
||||||
|
break;
|
||||||
|
case "donate":
|
||||||
|
sendDonationAd()
|
||||||
|
break;
|
||||||
|
case "test":
|
||||||
|
console.log(await makeTwitchRequest('games/top'))
|
||||||
|
break;
|
||||||
|
case "positivity":
|
||||||
|
sendPositivity()
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------VIP Handler-------------------
|
// ---------------VIP Handler-------------------
|
||||||
if (!userstate.badges?.vip) { return }
|
// if (!userstate.badges?.vip) { return }
|
||||||
verifyUser(channel, userstate.username)
|
// verifyUser(channel, userstate.username)
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ async function tLogin() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tClient = new tmi.Client({
|
var tClient = new tmi.Client({
|
||||||
options: { debug: false },
|
options: { debug: false },
|
||||||
connection: {
|
connection: {
|
||||||
reconnect: true,
|
reconnect: true,
|
||||||
@@ -233,18 +233,48 @@ async function tLogin() {
|
|||||||
dClient.emit('Twitch/Subscribers', channel, enabled, tClient);
|
dClient.emit('Twitch/Subscribers', channel, enabled, tClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
tClient.on('disconnected', (reason) => {
|
tClient.on('disconnected', async (reason) => {
|
||||||
console.log('Twitch client disconnected:', reason);
|
console.log('Twitch disconnected:', reason);
|
||||||
if (reason.includes('Login authentication failed')) {
|
if (reason.includes('Login authentication failed')) {
|
||||||
refreshAccessToken()
|
try {
|
||||||
.then(() => tClient.connect())
|
await refreshAccessToken();
|
||||||
.catch((error) => {
|
console.log('Reinitializing Twitch client after token refresh...');
|
||||||
console.error('Failed to refresh token and reconnect:', error);
|
|
||||||
|
try {
|
||||||
|
await tClient.disconnect();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Twitch client already disconnected:', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = mClient.db(process.env.MONGO_DB);
|
||||||
|
const credentialCollection = db.collection('credentials');
|
||||||
|
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
|
||||||
|
|
||||||
|
// Recreate the client
|
||||||
|
tClient = new tmi.Client({
|
||||||
|
options: { debug: false },
|
||||||
|
connection: { reconnect: true, secure: true },
|
||||||
|
identity: {
|
||||||
|
username: tCreds.username,
|
||||||
|
password: `oauth:${tCreds.token.access_token}`,
|
||||||
|
},
|
||||||
|
channels: channels,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await tClient.connect();
|
||||||
|
console.log('Twitch client reconnected successfully.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to refresh token and reconnect:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tLogin();
|
tLogin();
|
||||||
|
|
||||||
module.exports = { mClient, dClient };
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
|
// Prevent app from crashing
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = { mClient, dClient, makeTwitchRequest };
|
||||||
Reference in New Issue
Block a user