Restore Core Functionality
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
mongo_uri=
|
||||
mongo_db=
|
||||
test_id=
|
||||
prefix=
|
||||
+49
@@ -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;
|
||||
@@ -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"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
name: 'Mongo/ClientReady',
|
||||
once: false,
|
||||
execute(stream) {
|
||||
console.log(`Connected to ${stream.address}`)
|
||||
}
|
||||
}
|
||||
@@ -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}`)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
// ---------------------------------------------
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Event triggered by Raid
|
||||
module.exports = {
|
||||
name: 'Twitch/Raided',
|
||||
once: false,
|
||||
execute(stream) {
|
||||
console.log(`Connected to ${stream.address}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Event triggered by SubscribersOnly mode
|
||||
module.exports = {
|
||||
name: 'Twitch/Subscribers',
|
||||
once: false,
|
||||
execute(stream) {
|
||||
console.log(`Connected to ${stream.address}`)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
async function refreshAccessToken() {
|
||||
let db = mClient.db(process.env.mongo_db)
|
||||
let credentialCollection = db.collection('credentials')
|
||||
let credentials = await credentialCollection.find({ service: 'twitch' }).toArray()
|
||||
// Function to refresh the access token
|
||||
let isRefreshingToken = false;
|
||||
let refreshPromise = null;
|
||||
|
||||
const token = await getStoredToken();
|
||||
if (!token) {
|
||||
console.error('No token found! Please authenticate manually.');
|
||||
return;
|
||||
async function refreshAccessToken() {
|
||||
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.refreshToken,
|
||||
refresh_token: token.refresh_token,
|
||||
client_id: credentials.client_id,
|
||||
client_secret: credentials.client_secret
|
||||
}
|
||||
scope: token.scope,
|
||||
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
|
||||
const newRefreshToken = response.data.refresh_token || token.refresh_token;
|
||||
const newExpirationDate = response.data.expires_in || 0;
|
||||
|
||||
await saveToken(newAccessToken, newRefreshToken, newExpirationDate);
|
||||
console.log('Access token refreshed successfully');
|
||||
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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
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.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) {
|
||||
@@ -115,3 +153,98 @@ 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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user