diff --git a/.github/workflows/main_twitch-bn-bot.yml b/.github/workflows/main_twitch-bn-bot.yml index a9b34eb..ea74dc3 100644 --- a/.github/workflows/main_twitch-bn-bot.yml +++ b/.github/workflows/main_twitch-bn-bot.yml @@ -35,6 +35,8 @@ jobs: envKey_MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }} envKey_MYSQL_DB: ${{ secrets.MYSQL_DB }} envKey_STATUS_URL: ${{ secrets.BN_STATUS_URL }} + envKey_WHITE_LIST_ADMINS: ${{ secrets.WHITE_LIST_ADMINS }} + envKey_WHITE_LIST_EDITORS: ${{ secrets.WHITE_LIST_EDITORS }} - name: Set up Node.js version uses: actions/setup-node@v4 with: diff --git a/config/index.js b/config/index.js index 1459761..feecd3e 100644 --- a/config/index.js +++ b/config/index.js @@ -26,5 +26,7 @@ module.exports = { password: process.env.MYSQL_PASSWORD, db: process.env.MYSQL_DB }, - statusUrl: process.env.STATUS_URL + statusUrl: process.env.STATUS_URL, + whitelistAdmins: process.env.WHITE_LIST_ADMINS.split(','), + whitelistEditors: process.env.WHITE_LIST_EDITORS.split(',') } diff --git a/handlers/ban.js b/handlers/ban.js new file mode 100644 index 0000000..9d3fb25 --- /dev/null +++ b/handlers/ban.js @@ -0,0 +1,35 @@ +const config = require("../config") +const TwitchService = require('../services/twitch') + +class Ban { + async getUnbanRequests(target, bot) { + const result = await TwitchService.getUnbanRequests() + if (result !== null) { + const text = result.length > 0 ? `Hay ${result.length} solicitudes de desbaneo pendientes de revisar (${this._getUserNames(result)})` : 'No hay solicitudes de desbaneo pendientes de revisar.' + await bot.say(target, text) + } + } + + async ban(target, username, bot, duration) { + const user = await TwitchService.getUser(username) + + let durationNumber = parseInt(duration) + if (typeof durationNumber !== 'number') { + durationNumber = null + } + + if (user) { + await TwitchService.banUser(user.id, duration) + } + } + + async unban(target, username, bot) { + const user = await TwitchService.getUser(username) + if (user) { + await TwitchService.unBanUser(user.id) + } + } +} + + +module.exports = Ban diff --git a/handlers/generic.js b/handlers/generic.js index 81a9a6b..5f59d19 100644 --- a/handlers/generic.js +++ b/handlers/generic.js @@ -20,6 +20,14 @@ class Generic { const position = Math.floor(Math.random() * randomLinks.links.length) bot.say(target, `Mi OF ${config.externalUrl}/OF/${config.twitch.channels}${position}`) } + + setTitle (target, text, bot) { + bot.say(target, `!title ${text}`) + } + + setGame (target, text, bot) { + bot.say(target, `!game ${text}`) + } } diff --git a/handlers/index.js b/handlers/index.js index 9328cdd..bf8b99b 100644 --- a/handlers/index.js +++ b/handlers/index.js @@ -4,6 +4,7 @@ const Train = require('./Train') const Stream = require('./Stream') const Birthday = require('./Birthday') const TempsDeFlors = require('./TempsDeFlors') +const Ban = require('./Ban') const Events = require('./Events') module.exports = { @@ -13,5 +14,6 @@ module.exports = { stream: new Stream(), birthday: new Birthday(), tempsDeFlors: new TempsDeFlors(), + ban: new Ban(), events: new Events() } diff --git a/handlers/stream.js b/handlers/stream.js index c3413ee..e796c94 100644 --- a/handlers/stream.js +++ b/handlers/stream.js @@ -15,14 +15,6 @@ class Stream { await BrowserService.refreshPage().catch(() => { console.error('refreshPage on refreshPage')}) } - async getUnbanRequests(target, bot) { - const result = await TwitchService.getUnbanRequests() - if (result !== null) { - const text = result.length > 0 ? `Hay ${result.length} solicitudes de desbaneo pendientes de revisar (${this._getUserNames(result)})` : 'No hay solicitudes de desbaneo pendientes de revisar.' - await bot.say(target, text) - } - } - _getUserNames (unbanRequests) { let text if (unbanRequests.length === 1){ diff --git a/lib/inputParser.js b/lib/inputParser.js index b5c98d5..5563e75 100644 --- a/lib/inputParser.js +++ b/lib/inputParser.js @@ -52,31 +52,55 @@ class InputParser { } isAskingForTFVisited (text) { - return text.toLowerCase().startsWith('!visto') + // return text.toLowerCase().startsWith('!visto') } isAskingForTFNotVisited (text) { - return text.toLowerCase().startsWith('!borrar') || text.toLowerCase().startsWith('!novisto') + //return text.toLowerCase().startsWith('!borrar') || text.toLowerCase().startsWith('!novisto') } isAskingForTFActiveSpot(text) { - return text.toLowerCase().startsWith('!activo') || text.toLowerCase().startsWith('!activa') + //return text.toLowerCase().startsWith('!activo') || text.toLowerCase().startsWith('!activa') } isAskingForTFDeactivateSpot(text) { - return text.toLowerCase().startsWith('!desactivar') || text.toLowerCase().startsWith('!desactivo') || text.toLowerCase().startsWith('!desactiva') + //return text.toLowerCase().startsWith('!desactivar') || text.toLowerCase().startsWith('!desactivo') || text.toLowerCase().startsWith('!desactiva') } isAskingForTFSpot (text) { - return text.toLowerCase().startsWith('!punto') + //return text.toLowerCase().startsWith('!punto') } isAskingForTFScreenshot (text) { - return text.toLowerCase().startsWith('!foto') + // return text.toLowerCase().startsWith('!foto') } isAskingForTFSpotsCount (text) { - return text.toLowerCase().startsWith('!puntos') || text.toLowerCase().startsWith('!flores') + //return text.toLowerCase().startsWith('!puntos') || text.toLowerCase().startsWith('!flores') + } + + + isAskingToTimeoutUser (text) { + const words = ['timeout', 'expulsar', 'castigar'] + return words.some(word => text.toLowerCase().startsWith(`!${word}`)) + } + + isAskingToBanUser (text) { + const words = ['ban', 'ejecutar', 'fusilar', 'exterminar', 'matar', 'aniquilar', 'liquidar', 'ajusticiar'] + return words.some(word => text.toLowerCase().startsWith(`!${word}`)) + } + + isAskingTOUnbanUser (text) { + const words = ['unban', 'indultar', 'perdonar', 'condonar', 'amnistiar', 'eximir', 'absolver'] + return words.some(word => text.toLowerCase().startsWith(`!${word}`)) + } + + isAskingToSetTitle (text) { + return text.toLowerCase().startsWith('!titulo') + } + + isAskingToSetGame (text) { + return text.toLowerCase().startsWith('!categoria') } isAskingForTarracoMangaEvent (text) { diff --git a/lib/messenger.js b/lib/messenger.js index 723be09..5686bf0 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -118,7 +118,7 @@ class Messenger { setTimeout(() => { this.cooldown.bans = false }, 30000) - return handlers.stream.getUnbanRequests(target, this.bot) + return handlers.ban.getUnbanRequests(target, this.bot) } } @@ -130,7 +130,6 @@ class Messenger { }, 15000) return handlers.tempsDeFlors.getSpot(target, textSplit[1], this.bot, context['room-id']) } - } if (textSplit.length > 0 && inputParser.isAskingForTFSpotsCount(textSplit[0])) { @@ -191,6 +190,26 @@ class Messenger { } } + if (textSplit.length > 1 && inputParser.isAskingToTimeoutUser(textSplit[0]) && this._isAdmin(context)){ + return handlers.ban.ban(target, textSplit[1], this.bot, textSplit[2] || 600) + } + + if (textSplit.length > 1 && inputParser.isAskingToBanUser(textSplit[0]) && this._isAdmin(context)){ + return handlers.ban.ban(target, textSplit[1], this.bot) + } + + if (textSplit.length > 1 && inputParser.isAskingTOUnbanUser(textSplit[0]) && this._isAdmin(context)){ + return handlers.ban.unban(target, textSplit[1], this.bot) + } + + if (textSplit.length > 1 && inputParser.isAskingToSetTitle(textSplit[0]) && this._isEditor(context)){ + return handlers.generic.setTitle(target, textSplit.slice(1).join(' '), this.bot) + } + + if (textSplit.length > 1 && inputParser.isAskingToSetGame(textSplit[0]) && this._isEditor(context)) { + return handlers.generic.setGame(target, textSplit.slice(1).join(' '), this.bot) + } + if (textSplit.length > 0 && inputParser.isAskingForTarracoMangaEvent(textSplit[0])){ return handlers.events.sendTarracoMangaEvent() } @@ -219,6 +238,14 @@ class Messenger { _isBroadcaster(context) { return context['user-id'] === context['room-id'] } + + _isAdmin(context){ + return config.whitelistAdmins.includes(context['user-id']) + } + + _isEditor(context){ + return config.whitelistEditors.includes(context['user-id']) + } } module.exports = Messenger diff --git a/services/twitch.js b/services/twitch.js index 5c41e1d..16b6a25 100644 --- a/services/twitch.js +++ b/services/twitch.js @@ -36,7 +36,7 @@ async function getStream() { async function getUnbanRequests () { let result - const options = await _getHeaders() + let options = await _getHeaders() const channel = await dbManager.getChannel(config.twitch.channels).lean() const endpoint = `${endpointPrefix}/moderation/unban_requests?broadcaster_id=${channel.roomId}&moderator_id=${config.twitch.userId}&status=pending` @@ -52,6 +52,72 @@ async function getUnbanRequests () { return result } +async function banUser (user, duration) { + let result + let options = await _getHeaders() + options.method = 'POST' + const channel = await dbManager.getChannel(config.twitch.channels).lean() + const endpoint = `${endpointPrefix}/moderation/bans?broadcaster_id=${channel.roomId}&moderator_id=${config.twitch.userId}` + + const body = { + data : { + user_id: user, + ...duration && {duration: duration}, + reason: 'Bot' + } + } + + options.body = JSON.stringify(body) + try { + const response = await fetch(endpoint, options) + const data = await response.json() + result = data?.data !== null ?? null + + } catch { + result = null + } + + return result +} + +async function unBanUser (user) { + let result + let options = await _getHeaders() + options.method = 'DELETE' + const channel = await dbManager.getChannel(config.twitch.channels).lean() + const endpoint = `${endpointPrefix}/moderation/bans?broadcaster_id=${channel.roomId}&moderator_id=${config.twitch.userId}&user_id=${user}` + + try { + const response = await fetch(endpoint, options) + const data = await response.json() + result = data?.status === 204 ?? null + + } catch (e) { + result = null + } + + return result +} + +async function getUser (userName) { + let result + let options = await _getHeaders() + options.method = 'GET' + const endpoint = `${endpointPrefix}/users?login=${userName}` + + try { + const response = await fetch(endpoint, options) + const data = await response.json() + result = data?.data?.[0] ?? null + + } catch { + result = null + } + + return result + +} + async function sendAnnouncement(text, color) { const options = await _getHeaders() options.method = 'POST' @@ -106,6 +172,9 @@ module.exports = { saveLastUpdate, getUnbanRequests, setActiveSpot, + banUser, + unBanUser, + getUser, sendAnnouncement }