From a5e4281d08f2b897d9c47e5134acf052d1466e95 Mon Sep 17 00:00:00 2001 From: Daniel Huber Date: Thu, 11 Jul 2019 18:27:54 +0200 Subject: [PATCH 1/2] Wait for authentication in twitch bot and report invalid credentials --- .../chat/TwitchChatConnectListener.scala | 25 ++++++++++++ .../twitch/chat/TwitchChatConnector.scala | 40 ++++++++++++++----- 2 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnectListener.scala diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnectListener.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnectListener.scala new file mode 100644 index 00000000..e3a74bfa --- /dev/null +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnectListener.scala @@ -0,0 +1,25 @@ +package org.codeoverflow.chatoverflow.requirement.service.twitch.chat + +import org.pircbotx.hooks.events.{ConnectAttemptFailedEvent, ConnectEvent, NoticeEvent} +import org.pircbotx.hooks.{Event, ListenerAdapter} + +/** + * Handles connection events for the TwitchChatConnector. + * Calls the callback function once the bot connected and reports connection errors. + * @param fn the callback which will be called once suitable event has been received. + * The first param informs whether the connection could be established successfully + * and the second param includes a error description if something has gone wrong. + */ +class TwitchChatConnectListener(fn: (Boolean, String) => Unit) extends ListenerAdapter { + override def onEvent(event: Event): Unit = { + event match { + case _: ConnectEvent => fn(true, "") + case e: ConnectAttemptFailedEvent => fn(false, "couldn't connect to irc chat server") + case e: NoticeEvent => + if (e.getNotice.contains("authentication failed")) { + fn(false, "authentication failed") + } + case _ => + } + } +} diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala index 3ee7107f..7a54e24f 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala @@ -15,10 +15,12 @@ import scala.collection.mutable.ListBuffer */ class TwitchChatConnector(override val sourceIdentifier: String) extends Connector(sourceIdentifier) with WithLogger { private val twitchChatListener = new TwitchChatListener + private val connectionListener = new TwitchChatConnectListener(onConnect) private val oauthKey = "oauth" override protected var requiredCredentialKeys: List[String] = List(oauthKey) override protected var optionalCredentialKeys: List[String] = List() private var bot: PircBotX = _ + private var status: Option[(Boolean, String)] = None private val channels = ListBuffer[String]() def addMessageEventListener(listener: MessageEvent => Unit): Unit = { @@ -63,6 +65,7 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect .setName(credentials.get.credentialsIdentifier) .setServerPassword(password.getOrElse("")) .addListener(twitchChatListener) + .addListener(connectionListener) .buildConfiguration() } else { logger error "No credentials set!" @@ -71,33 +74,47 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect } + /** + * Gets called by the TwitchChatConnectListener when the bot has connected. + * Saves the passed information into the status variable. + */ + private def onConnect(success: Boolean, msg: String): Unit = { + status.synchronized { + // tell the thread which starts the connector that the status has been reported + status.notify() + status = Some((success, msg)) + } + } + /** * Starts the connector, e.g. creates a connection with its platform. */ override def start(): Boolean = { bot = new PircBotX(getConfig) startBot() - true } - private def startBot(): Unit = { - - var errorCount = 0 - + private def startBot(): Boolean = { new Thread(() => { bot.startBot() }).start() - while (bot.getState != PircBotX.State.CONNECTED && errorCount < 30) { - logger info "Waiting while the bot is connecting..." - Thread.sleep(100) - errorCount += 1 + logger info "Waiting while the bot is connecting and logging in..." + status.synchronized { + status.wait(10000) + } + + if (status.isEmpty) { + logger error "Bot couldn't connect within timeout of 10 seconds." + return false } - if (errorCount >= 30) { - logger error "Fatal. Unable to start bot." + val (success, msg) = status.get + if (!success) { + logger error s"Bot couldn't connect. Reason: $msg." } + success } /** @@ -106,6 +123,7 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect override def stop(): Boolean = { bot.sendIRC().quitServer() bot.close() + status = None true } } From 1223ff989138a23ec19ce3be4e9fc5f74db005bb Mon Sep 17 00:00:00 2001 From: Daniel Huber Date: Thu, 11 Jul 2019 18:30:36 +0200 Subject: [PATCH 2/2] Clear joined channels of TwitchChatConnector on stop If the channels aren't cleared and the connector is restarted multiple times it won't join into any channels that the connector has joined before the restart because it still thinks it has joined them already --- .../requirement/service/twitch/chat/TwitchChatConnector.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala index 7a54e24f..ebd1fb06 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/TwitchChatConnector.scala @@ -124,6 +124,7 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect bot.sendIRC().quitServer() bot.close() status = None + channels.clear() true } }