const radio = require('radio-stream') const request = require('request') const irc = require('irc-upd') const models = require('./models').models const Help = require('./libs/Help') const ArtistSong = require('./libs/ArtistSong') const When = require('./libs/When') const Statistics = require('./libs/Statistics') const List = require('./libs/List') const Preferences = require('./libs/Preferences') // Init des librairies const artistSong = new ArtistSong(models) const when = new When(models) const stats = new Statistics(models) const list = new List(models) const preferences = new Preferences(models) let isMute = false let titlePath = null let artistPath = null if (process.env.STREAM_PATH_TITLE) { titlePath = process.env.STREAM_PATH_TITLE.split('.') } if (process.env.STREAM_PATH_ARTIST) { artistPath = process.env.STREAM_PATH_ARTIST.split('.') } // Sauvegarde temporaire du morceau en cours de lecture let currentSong = { artist: null, title: null } // Sauvegarde des précédents morceaux (des 2 derniers à cause d'un bug sur Heavy1) const previousSongs = [ { artist: null, title: null }, { artist: null, title: null } ] // Initialisation du bot IRC const bot = new irc.Client(process.env.IRC_SERVER, process.env.IRC_NICKNAME, { userName: process.env.IRC_USER_NAME, realName: process.env.IRC_REAL_NAME, password: process.env.IRC_PASSWORD, channels: [process.env.IRC_CHANNEL] }) /** * Fonction permettant d'extraire des infos depuis un objet JSON * @param {Object} json * @param {String} array */ const extractInfoFromJSON = (json, path) => { let value = json for (let i = 0; i < path.length; i += 1) { if (path[i].indexOf('[') !== -1) { const extractKeyIdex = path[i].split('[') const key = extractKeyIdex[0] const index = extractKeyIdex[1].replace(']', '') value = value[key][index] } else { value = value[path[i]] } } return value } /** * Fonction d'aficher le morceau en cours de lecture (et de le sauvegarder en base) */ const displayCurrentSong = () => { // Just for debug console.info(`Now playing: ${currentSong.artist} - ${currentSong.title}`) // Publish song on IRC channel botSay(process.env.IRC_CHANNEL, `${currentSong.artist} - ${currentSong.title}`) // Save song for history artistSong.saveCurrent({ radio: process.env.RADIO_ALIAS, artist: currentSong.artist, title: currentSong.title }) // Notify some people ? artistSong.notify(botSay, process.env.IRC_CHANNEL, currentSong) } // Initialisation du stream radio if (!process.env.STREAM_TYPE || process.env.STREAM_TYPE === 'radio') { const stream = radio.createReadStream(process.env.STREAM_URL) stream.on('connect', function () { console.info('Radio Stream connected!') console.info(stream.headers) }) // Listener on new song stream.on('metadata', function (data) { const song = data.substring(13, data.length - 1).replace(/\0/g, '').replace('\';', '') // Extract artist = title from song const splitted = song.split(' - ') const artist = splitted[0] const title = splitted[1] currentSong = { title: title, artist: artist } displayCurrentSong() }) } else { setInterval(() => { request({ method: 'GET', url: process.env.STREAM_URL }, (error, response, body) => { if (!error && response.statusCode === 200) { try { const res = JSON.parse(process.env.STREAM_PARSE ? body.substr(body.indexOf('{')).replace(');', '') : body) let title = null let artist = null switch (process.env.STREAM_TYPE) { case 'json': title = extractInfoFromJSON(res, titlePath) artist = extractInfoFromJSON(res, artistPath) currentSong = { title: title, artist: artist } break case 'alien': try { for (let i = 0; i < res.length; i += 1) { if (res[i].name === 'Cult') { currentSong = { title: res[i].nowPlaying.track.title, artist: res[i].nowPlaying.track.artist } break } } } catch (e) { console.error('ERR:', e) } break } if (currentSong.title && currentSong.artist && (previousSongs[0].title !== currentSong.title && previousSongs[0].artist !== currentSong.artist) && (previousSongs[1].title !== currentSong.title && previousSongs[1].artist !== currentSong.artist) ) { previousSongs[1] = previousSongs[0] previousSongs[0] = currentSong displayCurrentSong() } } catch (e) { error = e console.error('getStream error:', error) } } else { console.error('ERR:', error) } }) }, 4000) } // Gestion des erreurs du bot IRC bot.addListener('error', function (message) { console.error('ERR:', message) }) /** * Fonction permettant de publier un message sur IRC * @param {String} where * @param {String} what * @param {Boolean} dontDisplayRadio * @param {Boolean} forceSend */ const botSay = (where, what, dontDisplayRadio, forceSend) => { if (!isMute || forceSend || where.indexOf('#') !== 0) { const message = `${(!dontDisplayRadio ? `${process.env.RADIO_ALIAS} : ` : '')}${what}` bot.say(where, message) } } /** * Gestion des actions * @param {String} where * @param {String} message * @param {String} from */ const actions = (where, message, from) => { const exploded = message.split(' ') const admins = (process.env.ADMINISTRATORS || 'ALL').toLowerCase().split(',') let allowedCmds = ['ALL'] if (process.env.AVAILABLE_CMD) { allowedCmds = process.env.AVAILABLE_CMD.split(',') } if (where.indexOf('#') === 0 && process.env[`AVAILABLE_CMD_${where.replace('#', '')}`]) { allowedCmds = process.env[`AVAILABLE_CMD_${where.replace('#', '')}`].split(',') } const unknownCmd = () => { if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!help') !== -1) { botSay(where, 'Commande inconnue, tape !help si tu es perdu', false, true) } else { botSay(where, 'Commande inconnue...', false, true) } } // Le message publié est pour le bot if (exploded[0].indexOf(process.env.IRC_NICKNAME) === 0) { switch (exploded[1]) { case '!artist': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!artist') !== -1) { artistSong.action(exploded[1].replace('!', ''), botSay, from, currentSong, exploded) } else { unknownCmd() } break case '!song': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!song') !== -1) { artistSong.action(exploded[1].replace('!', ''), botSay, from, currentSong, exploded) } else { unknownCmd() } break case '!help': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!help') !== -1) { Help.show(botSay, where, exploded, allowedCmds) } else { unknownCmd() } break case '!when': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!when') !== -1) { when.action(botSay, where, exploded) } else { unknownCmd() } break case '!stats': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!stats') !== -1) { stats.action(botSay, where, exploded) } else { unknownCmd() } break case '!list': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!list') !== -1) { list.action(botSay, where, exploded) } else { unknownCmd() } break case '!notifications': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!notifications') !== -1) { preferences.notifications(botSay, where, from, exploded) } else { unknownCmd() } break case '!stream': if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!stream') !== -1) { botSay(where, process.env.PUBLIC_RADIO_STREAM) } else { unknownCmd() } break case '!mute': if (admins[0] === 'ALL' || allowedCmds.indexOf(from.toLowerCase()) !== -1) { if (allowedCmds[0] === 'ALL' || allowedCmds.indexOf('!mute') !== -1) { if (exploded[2] && ['on', 'off', 'state'].indexOf(exploded[2]) !== -1) { switch (exploded[2]) { case 'on': isMute = true botSay(where, "Pff ! c'est toujours pareil ici :'(", false, true) break case 'off': isMute = false botSay(where, "Merci ! j'avais tellement de chose à te dire...", false, true) break default: botSay(where, isMute ? 'Je ne dirais rien !' : "T'inquiète ! tu vas l'avoir ta notif !", false, true) } } else { botSay(where, '!help mute', false, true) } } else { unknownCmd() } } else { botSay(where, 'Bien tenté mais...') } break default: unknownCmd() break } } } // Listener for public message bot.addListener(`message${process.env.IRC_CHANNEL}`, function (from, to, message) { actions(process.env.IRC_CHANNEL, message.args.splice(1).join(' '), from) }) // Listener for private message bot.addListener('pm', function (from, to, message) { actions(from, message.args.join(' '), from) }) // Say hello! if (process.env.SAY_HELLO && process.env.SAY_HELLO === 'true') { bot.addListener('join', (channel, who) => { // Welcome them in! if (who !== process.env.IRC_NICKNAME) { botSay(process.env.IRC_CHANNEL, 'Hello ' + who + '! Have a metal day! \\m/(-.-)\\m/', true, true) } }) }