From 2ad1712169d22acb886cf37d191a85fe42d62609 Mon Sep 17 00:00:00 2001 From: Lluis Date: Tue, 9 Jul 2024 23:00:22 +0200 Subject: [PATCH 1/2] implement ban system --- handlers/ban.js | 35 +++++++++++++++++++++ handlers/generic.js | 4 +++ handlers/index.js | 4 ++- lib/inputParser.js | 20 ++++++++++++ lib/messenger.js | 16 ++++++++++ services/twitch.js | 74 +++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 handlers/ban.js diff --git a/handlers/ban.js b/handlers/ban.js new file mode 100644 index 0000000..fe3b240 --- /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) { + const result = await TwitchService.banUser(user.id, duration) + } + } + + async unban(target, username, bot) { + const user = await TwitchService.getUser(username) + if (user) { + const result = await TwitchService.unBanUser(user.id) + } + } +} + + +module.exports = Ban diff --git a/handlers/generic.js b/handlers/generic.js index 81a9a6b..10b784e 100644 --- a/handlers/generic.js +++ b/handlers/generic.js @@ -20,6 +20,10 @@ 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}`) + } } diff --git a/handlers/index.js b/handlers/index.js index 49846b6..e05c737 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') module.exports = { generic: new Generic(), @@ -11,5 +12,6 @@ module.exports = { train: new Train(), stream: new Stream(), birthday: new Birthday(), - tempsDeFlors: new TempsDeFlors() + tempsDeFlors: new TempsDeFlors(), + ban: new Ban() } diff --git a/lib/inputParser.js b/lib/inputParser.js index 437c75a..f4f2558 100644 --- a/lib/inputParser.js +++ b/lib/inputParser.js @@ -78,6 +78,26 @@ class InputParser { isAskingForTFSpotsCount (text) { 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') + } } module.exports = InputParser diff --git a/lib/messenger.js b/lib/messenger.js index 2689403..df83b43 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -190,6 +190,22 @@ class Messenger { return handlers.stream.captureScreenshot(target, this.bot, this.notifier.bot, context['display-name'], context['room-id']) } } + + if (textSplit.length > 1 && inputParser.isAskingToTimeoutUser(textSplit[0])){ + return handlers.ban.ban(target, textSplit[1], this.bot, textSplit[2] || 600) + } + + if (textSplit.length > 1 && inputParser.isAskingToBanUser(textSplit[0])){ + return handlers.ban.ban(target, textSplit[1], this.bot) + } + + if (textSplit.length > 1 && inputParser.isAskingTOUnbanUser(textSplit[0])){ + return handlers.ban.unban(target, this.bot) + } + + if (textSplit.length > 1 && inputParser.isAskingToSetTitle(textSplit[0])){ + return handlers.generic.setTitle(target, textSplit.slice(1).join(' '), this.bot) + } } handleHosting (channel, target, viewers) { diff --git a/services/twitch.js b/services/twitch.js index bbf9274..822328c 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,73 @@ 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 + + } 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?.data ?? null + + } catch { + 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 getChannel () { return dbManager.getChannel(config.twitch.channels).lean() } @@ -84,7 +151,10 @@ module.exports = { getChannel, saveLastUpdate, getUnbanRequests, - setActiveSpot + setActiveSpot, + banUser, + unBanUser, + getUser } From fc04c26976bf96a3f8f88a22d64cb90d621f7431 Mon Sep 17 00:00:00 2001 From: Lluis Date: Fri, 12 Jul 2024 00:43:46 +0200 Subject: [PATCH 2/2] bans and editor commands --- .github/workflows/main_twitch-bn-bot.yml | 2 ++ config/index.js | 4 +++- handlers/ban.js | 4 ++-- handlers/generic.js | 4 ++++ handlers/stream.js | 8 -------- lib/inputParser.js | 18 ++++++++++------- lib/messenger.js | 25 +++++++++++++++++------- services/twitch.js | 9 +++++---- 8 files changed, 45 insertions(+), 29 deletions(-) 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 index fe3b240..9d3fb25 100644 --- a/handlers/ban.js +++ b/handlers/ban.js @@ -19,14 +19,14 @@ class Ban { } if (user) { - const result = await TwitchService.banUser(user.id, duration) + await TwitchService.banUser(user.id, duration) } } async unban(target, username, bot) { const user = await TwitchService.getUser(username) if (user) { - const result = await TwitchService.unBanUser(user.id) + await TwitchService.unBanUser(user.id) } } } diff --git a/handlers/generic.js b/handlers/generic.js index 10b784e..5f59d19 100644 --- a/handlers/generic.js +++ b/handlers/generic.js @@ -24,6 +24,10 @@ class Generic { setTitle (target, text, bot) { bot.say(target, `!title ${text}`) } + + setGame (target, text, bot) { + bot.say(target, `!game ${text}`) + } } 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 f4f2558..c32153e 100644 --- a/lib/inputParser.js +++ b/lib/inputParser.js @@ -52,31 +52,31 @@ 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') } @@ -98,6 +98,10 @@ class InputParser { isAskingToSetTitle (text) { return text.toLowerCase().startsWith('!titulo') } + + isAskingToSetGame (text) { + return text.toLowerCase().startsWith('!categoria') + } } module.exports = InputParser diff --git a/lib/messenger.js b/lib/messenger.js index df83b43..de97877 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,21 +190,25 @@ class Messenger { } } - if (textSplit.length > 1 && inputParser.isAskingToTimeoutUser(textSplit[0])){ + 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])){ + 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])){ - return handlers.ban.unban(target, 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])){ + 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) + } } handleHosting (channel, target, viewers) { @@ -231,6 +234,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 822328c..5a689be 100644 --- a/services/twitch.js +++ b/services/twitch.js @@ -71,7 +71,7 @@ async function banUser (user, duration) { try { const response = await fetch(endpoint, options) const data = await response.json() - result = data?.data ?? null + result = data?.data !== null ?? null } catch { result = null @@ -90,9 +90,9 @@ async function unBanUser (user) { try { const response = await fetch(endpoint, options) const data = await response.json() - result = data?.data ?? null + result = data?.status === 204 ?? null - } catch { + } catch (e) { result = null } @@ -140,7 +140,8 @@ async function _getHeaders () { return { headers: { 'Client-Id': config.twitch.clientId, - 'Authorization': 'Bearer ' + token.accessToken + 'Authorization': 'Bearer ' + token.accessToken, + 'Content-Type': 'application/json' } } }