Add files via upload
This commit is contained in:
26
.env
Normal file
26
.env
Normal file
@@ -0,0 +1,26 @@
|
||||
# https://dev.twitch.tv/console
|
||||
TWITCH_CLIENT_ID=
|
||||
TWITCH_CLIENT_SECRET=
|
||||
|
||||
|
||||
# You can generate an OAuth token by using this Twitch authentication link in your browser:
|
||||
# https://id.twitch.tv/oauth2/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost&response_type=token&scope=chat:read+chat:edit
|
||||
# After authorizing your Twitch account, you'll get a token in the URL.
|
||||
# Alternatively, use https://twitchtokengenerator.com/ to generate an OAuth token easily.
|
||||
TWITCH_OAUTH=oauth:
|
||||
TWITCH_ACCESS_TOKEN=
|
||||
|
||||
|
||||
# This is simply your Twitch username.
|
||||
# example: noahpombass
|
||||
TWITCH_USERNAME=
|
||||
|
||||
# Type your twitch username and copy the Twitch ID: output
|
||||
# https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/
|
||||
TWITCH_CHANNEL=
|
||||
|
||||
|
||||
DB_HOST=
|
||||
DB_USER=
|
||||
DB_NAME=
|
||||
DB_PASSWORD=
|
||||
75
db.sql
Normal file
75
db.sql
Normal file
@@ -0,0 +1,75 @@
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- Database: `twitchapi`
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `twitchapi` DEFAULT CHARACTER SET utf8mb4;
|
||||
USE `twitchapi`;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `blacklist_users`
|
||||
--
|
||||
|
||||
CREATE TABLE `blacklist_users` (
|
||||
`id` int NOT NULL,
|
||||
`username` varchar(255) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `watch_time`
|
||||
--
|
||||
|
||||
CREATE TABLE `watch_time` (
|
||||
`id` int NOT NULL,
|
||||
`username` varchar(255) NOT NULL,
|
||||
`session_start` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`total_watch_time` int DEFAULT '0',
|
||||
`points` int DEFAULT '0',
|
||||
`last_seen` datetime DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
--
|
||||
-- Indexes for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- Indexes for table `blacklist_users`
|
||||
--
|
||||
ALTER TABLE `blacklist_users`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `username` (`username`);
|
||||
|
||||
--
|
||||
-- Indexes for table `watch_time`
|
||||
--
|
||||
ALTER TABLE `watch_time`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `username` (`username`);
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `blacklist_users`
|
||||
--
|
||||
ALTER TABLE `blacklist_users`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `watch_time`
|
||||
--
|
||||
ALTER TABLE `watch_time`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
|
||||
199
index.js
Normal file
199
index.js
Normal file
@@ -0,0 +1,199 @@
|
||||
require("dotenv").config();
|
||||
const tmi = require("tmi.js");
|
||||
const mysql = require("mysql2");
|
||||
const axios = require("axios"); // Used to call the Twitch API
|
||||
|
||||
/**
|
||||
* Database Connection - MySQL
|
||||
*/
|
||||
const db = mysql.createConnection({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
});
|
||||
|
||||
db.connect((err) => {
|
||||
if (err) {
|
||||
console.error("❌ Error connecting to MySQL:", err);
|
||||
return;
|
||||
}
|
||||
console.log("✅ Connected to MySQL!");
|
||||
});
|
||||
|
||||
/**
|
||||
* Twitch Bot Connection
|
||||
*/
|
||||
const client = new tmi.Client({
|
||||
identity: {
|
||||
username: process.env.TWITCH_USERNAME,
|
||||
password: process.env.TWITCH_OAUTH,
|
||||
},
|
||||
channels: [process.env.TWITCH_CHANNEL],
|
||||
});
|
||||
|
||||
client
|
||||
.connect()
|
||||
.then(() => console.log(`✅ Bot connected to channel: ${process.env.TWITCH_CHANNEL}`))
|
||||
.catch((err) => console.error("❌ Error connecting to Twitch chat:", err));
|
||||
|
||||
let viewers = new Set(); // Store active viewers
|
||||
|
||||
/**
|
||||
* Tracks viewers' watch time in MySQL database
|
||||
*/
|
||||
client.on("chat", (channel, user) => {
|
||||
const username = user.username;
|
||||
|
||||
db.query("SELECT * FROM watch_time WHERE username = ?", [username], (err, results) => {
|
||||
if (err) {
|
||||
console.error("❌ Error fetching user:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.length > 0) {
|
||||
db.query("UPDATE watch_time SET last_seen = NOW() WHERE username = ?", [username]);
|
||||
} else {
|
||||
db.query("INSERT INTO watch_time (username, session_start, last_seen, total_watch_time, points) VALUES (?, NOW(), NOW(), 0, 0)", [username]);
|
||||
}
|
||||
});
|
||||
|
||||
viewers.add(username);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the stream is currently live
|
||||
*/
|
||||
async function isStreamLive() {
|
||||
try {
|
||||
const response = await axios.get(`https://api.twitch.tv/helix/streams?user_login=${process.env.TWITCH_CHANNEL}`, {
|
||||
headers: {
|
||||
"Client-ID": process.env.TWITCH_CLIENT_ID,
|
||||
Authorization: `Bearer ${process.env.TWITCH_ACCESS_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data.data.length > 0) {
|
||||
console.log("✅ Stream is live!");
|
||||
return true;
|
||||
} else {
|
||||
console.log("🚫 Stream is offline.");
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Error fetching Twitch API:", error.response?.data || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user is blacklisted
|
||||
*/
|
||||
async function isUserBlacklisted(username) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query("SELECT * FROM blacklist_users WHERE username = ?", [username], (err, results) => {
|
||||
if (err) {
|
||||
console.error("❌ Error fetching blacklist:", err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(results.length > 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodically update watch time every 5 minutes if stream is live
|
||||
*/
|
||||
let wasLive = false;
|
||||
setInterval(async () => {
|
||||
const live = await isStreamLive();
|
||||
|
||||
if (live) {
|
||||
if (!wasLive) console.log("🎥 Stream started! Updating watch time...");
|
||||
wasLive = true;
|
||||
|
||||
for (const username of viewers) {
|
||||
try {
|
||||
const blacklisted = await isUserBlacklisted(username);
|
||||
if (blacklisted) {
|
||||
console.log(`🚫 ${username} is blacklisted. No time counted.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
db.query("SELECT TIMESTAMPDIFF(SECOND, session_start, last_seen) AS time_watched FROM watch_time WHERE username = ?", [username], (err, results) => {
|
||||
if (err) {
|
||||
console.error("❌ Error fetching user watch time:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.length > 0) {
|
||||
const timeWatched = results[0].time_watched;
|
||||
|
||||
if (timeWatched >= 60) {
|
||||
db.query("UPDATE watch_time SET total_watch_time = total_watch_time + 60, points = points + 1 WHERE username = ?", [username]);
|
||||
} else {
|
||||
console.log(`🚫 ${username} watched less than a minute. No time counted.`);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`❌ Error checking blacklist for ${username}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Updated watch time for ${viewers.size} viewers!`);
|
||||
viewers.clear();
|
||||
} else {
|
||||
if (wasLive) console.log("🚫 Stream ended. Stopping watch time counting.");
|
||||
wasLive = false;
|
||||
}
|
||||
}, 300000);
|
||||
|
||||
/**
|
||||
* Chat commands for watch time and blacklist management
|
||||
*/
|
||||
client.on("chat", (channel, user, message, self) => {
|
||||
if (self) return;
|
||||
|
||||
const args = message.split(" ");
|
||||
const command = args[0].toLowerCase();
|
||||
const targetUser = args[1]?.toLowerCase();
|
||||
|
||||
if (command === "!watchtime") {
|
||||
db.query("SELECT total_watch_time FROM watch_time WHERE username = ?", [user.username], (err, results) => {
|
||||
if (err) {
|
||||
console.error("❌ Error fetching watch time:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.length > 0) {
|
||||
let watchTimeSec = results[0].total_watch_time;
|
||||
let watchTimeFormatted = new Date(watchTimeSec * 1000).toISOString().substr(11, 8);
|
||||
client.say(channel, `@${user.username}, you've watched for ${watchTimeFormatted} while the stream was online! ⏳`);
|
||||
} else {
|
||||
client.say(channel, `@${user.username}, you have no recorded watch time.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (command === "!addblacklist" && targetUser) {
|
||||
db.query("INSERT INTO blacklist_users (username) VALUES (?) ON DUPLICATE KEY UPDATE username = username", [targetUser], (err) => {
|
||||
if (err) {
|
||||
console.error("❌ Error adding user to blacklist:", err);
|
||||
return;
|
||||
}
|
||||
client.say(channel, `🚫 ${targetUser} has been added to the blacklist!`);
|
||||
});
|
||||
}
|
||||
|
||||
if (command === "!removeblacklist" && targetUser) {
|
||||
db.query("DELETE FROM blacklist_users WHERE username = ?", [targetUser], (err) => {
|
||||
if (err) {
|
||||
console.error("❌ Error removing user from blacklist:", err);
|
||||
return;
|
||||
}
|
||||
client.say(channel, `✅ ${targetUser} has been removed from the blacklist!`);
|
||||
});
|
||||
}
|
||||
});
|
||||
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"dotenv": "^16.4.7",
|
||||
"mysql2": "^3.12.0",
|
||||
"tmi.js": "^1.8.5"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user