Created
June 16, 2025 01:55
-
-
Save luke10x/a0e2a2e478ac2f4b5ff9e35783d7b57e to your computer and use it in GitHub Desktop.
Twitch bot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import tmi from 'tmi.js'; | |
import { exec, execSync } from 'child_process'; | |
import fs from 'fs'; | |
import path from 'path'; | |
// Define your Twitch credentials | |
const opts = { | |
identity: { | |
username: 'your_twitch_username', // same as your nick in Go | |
password: process.env.TWITCH_KEY // same OAuth token | |
}, | |
channels: [ | |
'#10x_developer' // same as your Go channel | |
], | |
connection: { | |
secure: true, // use TLS | |
reconnect: true // auto-reconnect if disconnected | |
} | |
}; | |
// Create a client with the options | |
const client = new tmi.Client(opts); | |
let lastPingTime = 0; | |
client.on('connected', (addr, port) => { | |
console.log(`Connected to ${addr}:${port}`); | |
}); | |
client.on('ping', ping => { | |
console.log('Received ping event;'); | |
sendPingMessageIfTimeElapsed(30_000); | |
}); | |
// Message received | |
client.on('message', (channel, tags, message, self) => { | |
// if (self) return; // Ignore messages from the bot itself | |
try { | |
console.log ({message}) | |
const regex = /^!sr\s+https:\/\/www\.youtube\.com\/watch\?v=([^\s]+)$/; | |
const match = message.match(regex); | |
if (match) { | |
const url = `https://www.youtube.com/watch?v=${match[1]}`; | |
lastPingTime = Date.now(); | |
downloadVideo(url).then(dlResults => { | |
const vlcCmd = `ffplay -autoexit -nodisp -volume 25 "${dlResults.filename.replace(/'/g, "\'")}"`; | |
console.log('Executing ', {vlcCmd}); | |
try { | |
execSync(vlcCmd); | |
} catch(Err) { | |
console.error('failed to execture', { vlcCmd, Err }); | |
} | |
lastPingTime = Date.now(); | |
}); | |
} else { | |
const regex = /^!tts\s+(.+)$/; | |
const match = message.match(regex); | |
if (match) { | |
const ttsCmd = `say -v rishi "${match[1]}"`; | |
console.log('Executing ', {ttsCmd}); | |
execSync(ttsCmd) | |
} else { | |
console.log('N'); | |
} | |
} | |
} catch (error) { | |
console.log("Error while processing people message:", error); | |
const ttsCmd = `say -v rishi "Error while processing people message"`; | |
console.log('Executing ', {ttsCmd}); | |
execSync(ttsCmd).toString().trim(); | |
} | |
}); | |
client.connect().catch(console.error); | |
async function downloadVideo(videoUrl) { | |
const outputDirectory = '/Users/lape/Music/yt/%(id)s-%(title)s.%(ext)s'; | |
const downloadCommand = `yt-dlp -j --no-simulate -o '${outputDirectory}' ${videoUrl}`; | |
console.log(`Executing download command: ${downloadCommand}`); | |
try { | |
const output = execSync(downloadCommand).toString().trim(); | |
const { filename, title } = JSON.parse(output); | |
return { filename, title }; | |
} catch (err) { | |
console.error(`Error downloading video: ${err}`); | |
} | |
} | |
function sendPingMessageIfTimeElapsed(timeThresholdMs) { | |
const currentTime = Date.now(); | |
const timeElapsed = currentTime - lastPingTime; | |
if (timeElapsed > timeThresholdMs) { | |
console.log('Ellapsed time is enough to play song', {timeElapsed, lastPingTime, timeThresholdMs}) | |
lastPingTime = currentTime // reset timing | |
getRandomSongId('/Users/lape/Music/yt/').then(randomSongId => { | |
const url = `https://www.youtube.com/watch?v=${randomSongId}`; | |
client.say('#10x_developer', '!sr ' + url) | |
.catch((error) => { | |
console.error('Failed to send message:', error); | |
}); | |
}); | |
} else { | |
console.log('not yet', {timeElapsed}) | |
} | |
} | |
// Function to get file names from a directory and filter based on the prefix criteria | |
async function getRandomSongId(directoryPath) { | |
try { | |
const files = []; | |
const entries = await fs.promises.readdir(directoryPath, { withFileTypes: true }); | |
for (const entry of entries) { | |
const fullPath = path.join(directoryPath, entry.name); | |
if (entry.isDirectory()) { | |
} else { | |
// Add the file path | |
files.push(entry.name); | |
} | |
} | |
// Function to extract prefix before the first '-' and check for valid characters | |
const getPrefix = (filePath) => { | |
// const prefix = filePath.split('-')[0]; | |
const isValidPrefix = /^[a-zA-Z0-9]+$/.test(prefix); | |
return prefix; | |
}; | |
// Filter the files based on the prefix criteria | |
const matchingFiles = files | |
.filter(f => f.length > 11) | |
.map(filePath => filePath.slice(0,11)); | |
const randomIndex = Math.floor(Math.random() * matchingFiles.length); | |
const randomId = matchingFiles[randomIndex]; | |
return randomId; | |
} catch (error) { | |
console.error('Error reading files:', error); | |
return []; | |
} | |
} | |
// const videoUrl = 'https://www.youtube.com/watch?v=cbB3iGRHtqA'; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment