diff --git a/defaultReactions.json b/defaultReactions.json new file mode 100644 index 0000000..c98fecd --- /dev/null +++ b/defaultReactions.json @@ -0,0 +1,34 @@ +{ + "oui": { + "prefix": ":thumbsup:", + "description": "Je suis d’accord" + }, + "non": { + "prefix": ":thumbsdown:", + "description": "Je ne suis pas d’accord" + }, + "parole": { + "prefix": ":raised_hand:", + "description": "Je veux parler" + }, + "réponse": { + "prefix": ":raised_hands:", + "description": "Je veux répondre rapidement" + }, + "suffit": { + "prefix": ":octagonal_sign:", + "description": "On tourne en rond" + }, + "écoute": { + "prefix": ":hear_no_evil:", + "description": "On ne s’écoute pas" + }, + "love": { + "prefix": ":heart_eyes:", + "description": "J’adore" + }, + "dab": { + "prefix": ":dab:", + "description": "Dab" + } +} diff --git a/educ.js b/educ.js index 26ec0c3..02efdce 100644 --- a/educ.js +++ b/educ.js @@ -1,134 +1,227 @@ -const Discord = require('discord.js'); -const client = new Discord.Client(); +/* Run dotenv */ +require('dotenv').config(); +const fs = require('fs'); -client.on('ready', () => { - console.log('Logged in as ${client.user.tag}'); -}); +/* Deep clone of objects */ +const clonedeep = require('lodash.clonedeep') -// l'ID du channel de test (test-bot) pour plus tard -// var testchan_id = '691952512332202064'; +/* Generate random string for token */ +const randomstring = require("randomstring"); + +/* Liste des réactions possibles par défaut et de leurs représentation actuelle */ +const defaultReactions = JSON.parse(fs.readFileSync('defaultReactions.json', 'utf8')); -/* Liste des réactions possibles et de leurs représentants actuel */ -var reactions = { - 'oui': { - 'prefix': ':thumbsup:', - 'description': 'Je suis d’accord', - }, - 'non': { - 'prefix': ':thumbsdown:', - 'description': 'Je ne suis pas d’accord', - }, - 'parole': { - 'prefix': ':raised_hand:', - 'description': 'Je veux parler', - }, - 'réponse': { - 'prefix': ':raised_hands:', - 'description': 'Je veux répondre rapidement', - }, - 'suffit': { - 'prefix': ':octagonal_sign:', - 'description': 'On tourne en rond', - }, - 'écoute': { - 'prefix': ':hear_no_evil:', - 'description': 'On ne s’écoute pas', - }, - 'love': { - 'prefix': ':heart_eyes:', - 'description': 'J’adore', - }, - 'dab': { - 'prefix': ':dab:', - 'description': 'Dab', - }, -}; -for (var index in reactions) { - reactions[index].people = [] + +/* The html page will render data passed in WS */ +const WebSocket = require('ws') +const wss = new WebSocket.Server({ port: process.env.WS_PORT }) +wss.on('connection', ws => { + ws.isAlive = true; + ws.on('pong', heartbeat); + ws.on('message', message => { + const data = JSON.parse(message) + if (!('channel' in data && 'action' in data && 'web_token' in data)) { + ws.send('{"error":"invalid request"}') + return + } + else if (!(data['action'] in wsActions)) { + ws.send('{"error":"bad action"}') + return + } + else if (!(data['channel'] in channels && channels[data['channel']].web_token === data['web_token'])) { + ws.send('{"error": "bad channel"}') + return + } + else { + wsActions[data['action']](data, channels[data['channel']], ws) + } + }) +}) +function heartbeat() { + this.isAlive = true; +} +const interval = setInterval(function ping() { + wss.clients.forEach(function (ws) { + if (ws.isAlive === false) return ws.terminate(); + + ws.isAlive = false; + ws.ping(()=>{}); + }); +}, 30000); + +wss.on('close', function close() { + clearInterval(interval); +}); + +// TODO penser à prévenir le web quand un type de réaction est ajoutée/supprimée +const wsActions = { + 'init': wsInit, + 'add_reaction': wsAddReaction, + 'remove': wsRemoveReaction, + 'reset': wsReset, +} +function wsInit (data, channel, ws) { + channel.ws_clients.push(ws) + ws.send(JSON.stringify(channel.reactions)) +} +function wsAddReaction (data, channel, ws) { + if (!('reaction' in data)) { + ws.send('{"error":"No reaction supplied", "action":"add"}') + } +} +function wsRemoveReaction (data, channel, ws) { + if (!('reaction' in data)) { + ws.send('{"error":"No reaction supplied", "action":"add"}') + } +} +function wsReset (data, channel, ws) { + } + + /* Liste des channels où on peut lire avec le dernier message que l’on y a envoyé */ -/* 1282482389371398137183: */ var channels = {} - -var last_post = null; /* On se souvient du derier post que ce bot a envoyé pour le virer dès que possible */ - -client.on('message', msg => { - if(msg.content === '!educpop-reset') { //TODO room parametrized - for (var index in reactions) { - reactions[index].people = [] - } - reply(msg) +function educpopReset (channel) { + for (var index in channel.reactions) { + channel.reactions[index].people = [] } - else if (msg.content === '!educpop-enable') { - var id = msg.channel.id - if (id in channels) { +} + +function educpopAddChannel (discordChannel) { + channels [discordChannel.id] = { + 'last_msg': null, /* On se souvient du dernier post que ce bot a envoyé pour le virer dès que possible */ + 'discord_channel': discordChannel, + 'ws_clients': [], + 'web_token': randomstring.generate(), /* web auth */ + 'reactions': Object.assign({}, clonedeep(defaultReactions)), + 'pause': false, + } + educpopReset(channels[discordChannel.id]) +} + +function educpopDelChannel (channel) { + delete channels[channel.discord_channel.id] +} + +function educpopPause (channel) { + channel.pause = true +} +// TODO wait for database before using this +function educpopAddReaction (channel, word, prefix, description) { + channels[channelId].reactions[word] = {'prefix': prefix, 'description': description, 'people':[]} +} +function educpopDelReaction (channelId, word) { + delete channels[channelId].reactions[word] +} + +function educpopAddPerson (channel, username, reaction) { + var liste = channel.reactions[reaction].people + if (liste.indexOf(username) < 0) { + liste.push(username) + } +} + +function educpopDelPerson (channel, username, reaction) { + var liste = channel.reactions[reaction].people + var index = liste.indexOf(username) + if (index >= 0) { + liste.splice(index, 1) + } +} + + + + +/* Connexion à discord */ +const Discord = require('discord.js'); +const client = new Discord.Client(); +client.on('ready', () => { + console.log('Connected to Discord as ' + client.user.tag); +}); +/* Discord message center */ +client.on('message', msg => { + if (msg.content === '!educpop-enable') { + if (msg.channel.id in channels) { msg.reply('J’écoute déjà ce canal texte.') } else { - channels[id] = null + educpopAddChannel(msg.channel) msg.reply('J’écoute maintenant ce canal texte.') } + return } - else if (msg.content === '!educpop-disable') { + if (msg.content === '!educpop-disable') { var id = msg.channel.id if (id in channels) { - delete channels[id] + educpopDelChannel(id) msg.reply('Je n’écoute plus ce canal texte.') } else { msg.reply('Je n’écoutais pas ce canal texte :o') } + return } - else if (msg.content === '!educpop-help') { - var text = 'Tapez simplement le mot-clé ci-dessous pour être comptabilisé. Tapez un - immédiatement suivi du mot-clé pour être retiré du compte : -oui !' + if (msg.content === '!educpop-help') { + msg.reply('Voilà comment m’utiliser. Taper :\n!educpop-enable pour activer le bot sur ce salon ;\n!educpop-disable pour le désactiver ;\n!educpop-list pour voir la liste des réactions possibles ;\n!educpop-reset pour remettre les compteurs à zéro.') + return + } + + /* Listen only to enabled channels */ + if(!(msg.channel.id in channels)) { return } + var channel = channels[msg.channel.id] + var reactions = channel.reactions + for( index in channels) { + console.log(index) + console.log(channels[index].reactions) + } + console.log(true) + + if(msg.content === '!educpop-reset') { + educpopReset + reply(channel) + } else if (msg.content === '!educpop-list') { + var text = 'Tapez simplement le mot-clé ci-dessous pour être comptabilisé. Tapez un - immédiatement suivi du mot-clé pour être retiré du compte : -oui par exemple !' for (var index in reactions) { text += '\n' + reactions[index].prefix + index + ' : ' + reactions[index].description } - msg.author.send(text) - msg.delete() - } - /* Listen to educ pop stuff only in authorized channels */ - else if(!(msg.channel.id in channels)) { - return; + msg.reply(text) } /* save and ignore own messages */ - else if(msg.author.username === 'bod-educ-pop'){ // TODO only log it if answer to educpop - channels[msg.channel.id] = msg; + else if(msg.author.username === process.env.BOT_USERNAME){ + if (msg.content.startsWith(':')) /* Save if its educpop summary */ + channel.last_msg = msg; } /* Educ pop stuff */ else if(msg.content.startsWith('-')) { - var content = msg.content.slice(1) + const content = msg.content.slice(1) if (content.toLowerCase() in reactions) { - var reaction = reactions[content].people - var index = reaction.indexOf(msg.author.username) - if (index >= 0) { - reaction.splice(index, 1) - } - reply(msg) + educpopAddPerson(channel, msg.author.username, content.toLowerCase()) + msg.delete() + reply(channel) } } else if (msg.content.toLowerCase() in reactions) { - var reaction = reactions[msg.content].people - if (reaction.indexOf(msg.author.username) < 0) { - reaction.push(msg.author.username) - } - reply(msg) + var reaction = msg.content.toLowerCase() + educpopAddPerson(channel, msg.author.username, msg.content.toLowerCase()) + msg.delete() + reply(channel) } }); -function reply (msg) { +/* Recap educ-pop state on discord. Delete action message and last educ-pop recap */ +function reply (channel) { var text = '' - for (var index in reactions) { - if (reactions[index].people.length > 0) - text += '\n' + reactions[index].prefix + String(reactions[index].people) + for (var index in channel.reactions) { + if (channel.reactions[index].people.length > 0) + text += '\n' + channel.reactions[index].prefix + String(channel.reactions[index].people) } if (text === '') { text = 'Personne ne s’est manifesté :/' } - - if(channels[msg.channel.id]) channels[msg.channel.id].delete() - msg.reply(text) - msg.delete() + if(channel.last_msg) channel.last_msg.delete() + channel.discord_channel.send(text) } -client.login('NjkxOTUzMDQzMDcxMzAzNzIy.Xnnhng.pYBFO2ogooVs2AyYz8Pk6AKhMoo'); + +/* Actual discord login */ +client.login(process.env.DISCORD_TOKEN);