UNPKG

twitch-chat-cli

Version:

CLI for reading Twitch chat

158 lines (134 loc) 3.99 kB
#!/usr/bin/env node const fs = require('fs-extra') const os = require('os') const path = require('path') const chalk = require('chalk') const program = require('commander') const ora = require('ora') const fetch = require('node-fetch') const request = require('request-promise') const Promise = require('bluebird') const writeFile = Promise.promisify(fs.writeFile) const termImg = require('term-img') const rimraf = require('rimraf') const config = require('./config') const connect = require('./client') const HOME_DIR = os.homedir() const CONFIG_DIR = path.join(HOME_DIR, config.dirName) const CONFIG_FILE = path.join(CONFIG_DIR, config.configFile) const EMOTICON_DIR = path.join(CONFIG_DIR, config.emoticonDir) const IMAGES_DIR = path.join(EMOTICON_DIR, config.imagesDir) const API_BASE_URL = config.apiBaseURL const EMOTE_API_URL = `${API_BASE_URL}/channels` const GLOBAL_CHANNEL = config.globalChannel const getEmotes = async channel => { const url = `${EMOTE_API_URL}/${channel}` const res = await fetch(url) const json = await res.json() return json } const fetchEmotes = async (channel, emotes, isGlobal) => { const msg = isGlobal ? 'Fetching global emotes.' : `Fetching emotes for ${chalk.bold(channel)}.` const spinner = ora(msg) spinner.start() // make sure all images have been fetched before continuing await Promise.all( emotes.map(async emote => { const { id } = emote const dest = path.join(IMAGES_DIR, `${id}.png`) const data = await request({ url: `https://static-cdn.jtvnw.net/emoticons/v1/${id}/1.0`, encoding: null }) const buf = Buffer.from(data, 'utf8') await writeFile(dest, buf) }) ) spinner.succeed() } const createEmoteMap = emoteList => { const map = {} emoteList.forEach(emotes => { emotes.forEach(emote => { const { id, code } = emote const img = termImg.string(path.join(IMAGES_DIR, `${id}.png`)) map[code] = img }) }) return map } const handleConnect = async channel => { if (!fs.existsSync(CONFIG_DIR) || !fs.existsSync(CONFIG_FILE)) { console.log( `No login credentials found. First add your oAuth token generated from ${chalk.underline( 'https://twitchapps.com/tmi/' )}` ) return } fs.ensureDirSync(EMOTICON_DIR) fs.ensureDirSync(IMAGES_DIR) let spinner = ora(`Checking for emoticon api.`) spinner.start() const failMsg = 'No emoticon api found. Emotes will be displayed as text.' let api = false try { const res = await fetch(API_BASE_URL) const json = await res.json() if (json.status) { spinner.succeed() api = true } else { spinner.fail(failMsg) } } catch (e) { spinner.fail(failMsg) } let globalEmotes let channelEmotes if (api) { spinner = ora('Fetching channel emoticon metadata.') spinner.start() globalEmotes = await getEmotes(GLOBAL_CHANNEL) channelEmotes = await getEmotes(channel) spinner.succeed() await fetchEmotes(GLOBAL_CHANNEL, globalEmotes, true) await fetchEmotes(channel, channelEmotes, false) } const emotes = api ? createEmoteMap([globalEmotes, channelEmotes]) : {} const credentials = JSON.parse(fs.readFileSync(CONFIG_FILE)) connect( credentials, channel, emotes ) } program.version('1.2.4') program .command('add <username> <token>') .description('Add an OAuth token') .action((username, token, cmd) => { fs.ensureDirSync(CONFIG_DIR) const login = { username, token } fs.writeFileSync(CONFIG_FILE, JSON.stringify(login)) }) program .command('connect <channel>') .description('Connect to a specified Twitch channel') .action((channel, cmd) => { handleConnect(channel) }) program .command('clear') .description('Clear cached emoticons') .action(cmd => { rimraf(IMAGES_DIR, () => { console.log('Cached emoticons cleared 🗑️.') }) }) program.parse(process.argv)