irc-radio-bot/index.js

337 lines
9.9 KiB
JavaScript

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)
}
})
}