Restore Core Functionality

This commit is contained in:
DeSqBlocki
2025-02-08 02:03:18 +01:00
parent 43958830cf
commit 21115a66fb
12 changed files with 427 additions and 73 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
mongo_uri=
mongo_db=
test_id=
prefix=
+49
View File
@@ -0,0 +1,49 @@
// clients.js
const { MongoClient } = require('mongodb');
const { Client, GatewayIntentBits } = require('discord.js');
const tmi = require('tmi.js');
const channels = ['x__hel__x','desq_blocki']
const initializeClients = async () => {
// Initialize MongoDB Client
const mClient = new MongoClient(process.env.mongo_uri);
await mClient.connect();
// Initialize Discord Client
const dClient = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.MessageContent,
],
});
// Fetch Discord credentials from the database
const db = mClient.db(process.env.mongo_db);
const credentialCollection = db.collection('credentials');
const dCreds = await credentialCollection.findOne({ service: 'discord' });
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
await dClient.login(dCreds.token);
// Initialize Twitch Client
const tClient = new tmi.Client({
options: { debug: false },
connection: {
reconnect: true,
secure: true,
},
identity: {
username: tCreds.username,
password: `oauth:${tCreds.token.access_token}`,
},
channels: channels, // Replace with your channel
});
await tClient.connect();
return { mClient, dClient, tClient };
};
module.exports = initializeClients;
+12
View File
@@ -0,0 +1,12 @@
const { Events, ActivityType } = require('discord.js')
module.exports = {
name: Events.ClientReady,
once: false,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`)
client.user.setActivity("chaos & comf", {
type: ActivityType.Streaming,
url: "https://www.twitch.tv/desq_blocki"
});
}
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
name: 'Mongo/ClientReady',
once: false,
execute(stream) {
console.log(`Connected to ${stream.address}`)
}
}
+8
View File
@@ -0,0 +1,8 @@
// Event triggered by client connected to server
module.exports = {
name: 'Twitch/Connected',
once: false,
execute(address, port) {
console.log(`Connected to ${address}:${port}`)
}
}
+106
View File
@@ -0,0 +1,106 @@
const { dClient, mClient, tClient } = require('../..')
require('dotenv').configDotenv
const knownBots = new Set(['streamlabs', 'nightbot', 'moobot', 'soundalerts', 'streamelements', 'remasuri_bot', 'commanderroot', 'x__hel_bot__x'])
async function updateChatMode(userID, setTo) {
let state
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) {
const result = await Helix.users.get(user);
return result.data[0].id
}
async function verifyUser(channel, username) {
let db = mClient.db("shoutouts")
let col = db.collection(channel)
let query = { user: username }
let status = await col.findOne(query)
if (!status) {
dClient.emit('TwitchShoutout', "VIP", channel, username)
await col.insertOne({
user: username
})
}
}
module.exports = {
name: 'Twitch/Message',
once: false,
async execute(channel, userstate, message, self) {
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":
// 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.",
"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.",
"You deserve every warm laugh, bright smile, and all the genuine kindness given to you.",
"There are few things as precious as your time. Thank you for sharing with us.",
"Your feelings are valid and you are not less than for having them.",
"I've seen all the work you do and I'm so very proud of you.",
"You are dearly cherished and I want to thank you for being so wonderfully you.",
"You. Are. Worthy. Enough. And I will tell you as many times as it takes for you to believe it.",
"Every day you give your best and that is so admirable. Don't let anyone tell you otherwise.",
"Your light is like that of the full moon, brightening our lives even in our darkest moments.",
"It's okay to take a moment to catch your breath. Even machines require maintenance from time to time.",
"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)
tClient.say(channel, positive_Messages[rdm])
break;
default:
break;
}
}
// ---------------VIP Handler-------------------
if (!userstate.badges?.vip) { return }
verifyUser(channel, userstate.username)
// ---------------------------------------------
}
}
+8
View File
@@ -0,0 +1,8 @@
// Event triggered by Raid
module.exports = {
name: 'Twitch/Raided',
once: false,
execute(stream) {
console.log(`Connected to ${stream.address}`)
}
}
+8
View File
@@ -0,0 +1,8 @@
// Event triggered by SubscribersOnly mode
module.exports = {
name: 'Twitch/Subscribers',
once: false,
execute(stream) {
console.log(`Connected to ${stream.address}`)
}
}
View File
View File
+23
View File
@@ -0,0 +1,23 @@
const fs = require('node:fs')
const path = require('node:path')
module.exports = (dClient) => {
const folderPath = path.join(__dirname, '../events')
const eventFolders = fs.readdirSync(folderPath)
for (const folder of eventFolders) {
const eventsPath = path.join(folderPath, folder);
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'))
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file)
const event = require(filePath)
if (event.once) {
dClient.once(event.name, (...args) => event.execute(...args, dClient))
// added client to commomerate global usage
} else {
dClient.on(event.name, (...args) => event.execute(...args, dClient))
// added client to commomerate global usage
}
}
}
}
+205 -72
View File
@@ -1,93 +1,132 @@
const { MongoClient } = require('mongodb');
require('dotenv').config()
const mClient = new MongoClient(process.env.mongo_uri)
exports.mClient = mClient;
require('dotenv').config();
const fs = require('node:fs');
const { Client, GatewayIntentBits } = require('discord.js');
const tmi = require('tmi.js');
const axios = require('axios');
// Validate required environment variables
function validateEnvVariables() {
const requiredVars = ['MONGO_URI', 'MONGO_DB'];
const missingVars = requiredVars.filter((varName) => !process.env[varName]);
async function connectToMongo() {
await mClient.connect();
console.log('Connected to MongoDB');
async function getChannelInformation(broadcasterId) {
const endpoint = `channels?broadcaster_id=${broadcasterId}`;
const channelInfo = await makeTwitchRequest(endpoint);
if (channelInfo) {
console.log('Channel Information:', channelInfo);
} else {
console.log('Failed to retrieve channel information.');
}
if (missingVars.length > 0) {
console.error(`Missing required environment variables: ${missingVars.join(', ')}`);
process.exit(1);
}
getChannelInformation(process.env.test_id);
}
connectToMongo();
async function saveToken(accessToken, refreshToken, expires_in) {
let db = mClient.db(process.env.mongo_db);
let tokenCollection = db.collection("twitch-token");
validateEnvVariables();
// Initialize MongoDB Client
const mClient = new MongoClient(process.env.MONGO_URI);
async function initializeMongoClient() {
try {
await mClient.connect();
console.log('MongoDB connected successfully.');
// Emit event or proceed with dependent initializations here
} catch (error) {
console.error('Failed to connect to MongoDB:', error);
process.exit(1); // Exit the process if the connection fails
}
}
initializeMongoClient();
// Function to save tokens to MongoDB
async function saveToken(access_token, refresh_token, expiresIn, scope) {
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const tokenData = {
accessToken,
refreshToken,
expires_in,
obtainedAt: Date.now()
access_token,
refresh_token,
scope,
expiresIn,
obtainedAt: Date.now(),
};
await tokenCollection.updateOne({}, { $set: tokenData }, { upsert: true });
console.log('TTV Token updated in MongoDB');
await credentialCollection.updateOne(
{ service: 'twitch' },
{ $set: { token: tokenData } },
{ upsert: true }
);
console.log('Twitch token updated in MongoDB');
}
async function getStoredToken() {
let db = mClient.db(process.env.mongo_db);
let tokenCollection = db.collection("twitch-token");
const tokenData = await tokenCollection.findOne({});
if (tokenData) {
// Function to retrieve stored tokens from MongoDB
async function getStoredToken() {
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
if (tCreds) {
console.log('Loaded token from MongoDB');
return tokenData;
return tCreds.token;
} else {
console.log('No token found in MongoDB');
return null;
}
}
const axios = require('axios');
// Function to refresh the access token
let isRefreshingToken = false;
let refreshPromise = null;
async function refreshAccessToken() {
let db = mClient.db(process.env.mongo_db)
let credentialCollection = db.collection('credentials')
let credentials = await credentialCollection.find({ service: 'twitch' }).toArray()
if (isRefreshingToken) {
return refreshPromise; // Return the existing refresh promise if a refresh is already in progress
}
isRefreshingToken = true;
refreshPromise = (async () => {
try {
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const credentials = await credentialCollection.findOne({ service: 'twitch' });
const token = await getStoredToken();
if (!token) {
throw new Error('No token found! Please authenticate manually.');
}
const response = await axios.post('https://id.twitch.tv/oauth2/token', null, {
params: {
grant_type: 'refresh_token',
refresh_token: token.refresh_token,
client_id: credentials.client_id,
scope: token.scope,
client_secret: credentials.client_secret,
},
});
const newAccessToken = response.data.access_token;
const newRefreshToken = response.data.refresh_token || token.refresh_token;
const newExpirationDate = response.data.expires_in || 0;
await saveToken(newAccessToken, newRefreshToken, newExpirationDate, token.scope);
console.log('Access token refreshed successfully.');
return newAccessToken;
} catch (error) {
console.error('Failed to refresh access token:', error.response?.data || error.message);
throw error;
} finally {
isRefreshingToken = false;
refreshPromise = null;
}
})();
return refreshPromise;
}
// Function to make requests to the Twitch API
async function makeTwitchRequest(endpoint) {
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const credentials = await credentialCollection.findOne({ service: 'twitch' });
const token = await getStoredToken();
if (!token) {
console.error('No token found! Please authenticate manually.');
return;
}
try {
const response = await axios.post('https://id.twitch.tv/oauth2/token', null, {
params: {
grant_type: 'refresh_token',
refresh_token: token.refreshToken,
client_id: credentials.client_id,
client_secret: credentials.client_secret
}
});
const newAccessToken = response.data.access_token;
const newRefreshToken = response.data.refresh_token || token.refreshToken;
const newExpirationDate = response.data.expires_in || 0
await saveToken(newAccessToken, newRefreshToken, newExpirationDate);
console.log('Access token refreshed successfully');
} catch (error) {
console.error('Failed to refresh access token:', error.response?.data || error.message);
}
}
async function makeTwitchRequest(endpoint) {
let db = mClient.db(process.env.mongo_db)
let credentialCollection = db.collection('credentials')
let credentials = await credentialCollection.findOne({ service: 'twitch' })
let token = await getStoredToken();
if (!token.access_token) {
console.error('No access token available.');
return;
@@ -97,14 +136,13 @@ async function makeTwitchRequest(endpoint) {
const response = await axios.get(`https://api.twitch.tv/helix/${endpoint}`, {
headers: {
'Client-Id': credentials.client_id,
'Authorization': `Bearer ${token.access_token}`
}
'Authorization': `Bearer ${token.access_token}`,
},
});
return response.data;
} catch (error) {
if (error.response?.status === 401) {
console.log(error)
console.log('Access token expired. Refreshing...');
const newToken = await refreshAccessToken();
if (newToken) {
@@ -114,4 +152,99 @@ async function makeTwitchRequest(endpoint) {
console.error('Twitch API request failed:', error.response?.data || error.message);
}
}
}
// Initialize Discord Client
const dClient = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.MessageContent,
],
});
exports.dClient = dClient;
// Load Discord event handlers
fs.readdirSync('./handlers').forEach((handler) => {
require(`./handlers/${handler}`)(dClient);
});
// Function to log in to Discord
async function dLogin() {
try {
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const dCreds = await credentialCollection.findOne({ service: 'discord' });
if (!dCreds || !dCreds.token) {
throw new Error('Discord credentials not found.');
}
await dClient.login(dCreds.token);
console.log('Discord client logged in successfully.');
} catch (error) {
console.error('Discord login failed:', error);
}
}
dLogin();
// Function to log in to Twitch
async function tLogin() {
const channels = ['desq_blocki'];
const db = mClient.db(process.env.MONGO_DB);
const credentialCollection = db.collection('credentials');
const tCreds = await credentialCollection.findOne({ service: 'twitch' });
if (!tCreds || !tCreds.username || !tCreds.token || !tCreds.token.access_token) {
console.error('Twitch credentials are missing or incomplete.');
return;
}
const tClient = new tmi.Client({
options: { debug: false },
connection: {
reconnect: true,
secure: true,
},
identity: {
username: tCreds.username,
password: `oauth:${tCreds.token.access_token}`,
},
channels: channels,
});
tClient.connect();
// Handle Twitch events
tClient.on('connected', (address, port) => {
dClient.emit('Twitch/Connected', address, port);
});
tClient.on('message', async (channel, userstate, message, self) => {
dClient.emit('Twitch/Message', channel, userstate, message, self);
});
tClient.on('raided', (channel, username, viewers) => {
dClient.emit('Twitch/Raided', channel, username, viewers);
});
tClient.on('subscribers', (channel, enabled) => {
dClient.emit('Twitch/Subscribers', channel, enabled);
});
tClient.on('disconnected', (reason) => {
console.log('Twitch client disconnected:', reason);
if (reason.includes('Login authentication failed')) {
refreshAccessToken()
.then(() => tClient.connect())
.catch((error) => {
console.error('Failed to refresh token and reconnect:', error);
});
}
});
}
tLogin();