diff --git a/src/main/scala/org/codeoverflow/chatoverflow/configuration/ConfigurationService.scala b/src/main/scala/org/codeoverflow/chatoverflow/configuration/ConfigurationService.scala index 9b23f887..846717b1 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/configuration/ConfigurationService.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/configuration/ConfigurationService.scala @@ -1,6 +1,6 @@ package org.codeoverflow.chatoverflow.configuration -import java.io.File +import java.io.{File, PrintWriter} import org.codeoverflow.chatoverflow.WithLogger import org.codeoverflow.chatoverflow.api.io @@ -10,7 +10,7 @@ import org.codeoverflow.chatoverflow.framework.PluginFramework import org.codeoverflow.chatoverflow.instance.PluginInstanceRegistry import org.codeoverflow.chatoverflow.registry.TypeRegistry -import scala.xml.{Elem, Node} +import scala.xml.{Elem, Node, PrettyPrinter} /** * The configuration service provides methods to work with serialized state information. @@ -153,7 +153,9 @@ class ConfigurationService(val configFilePath: String) extends WithLogger { * Saves the xml content to the config xml. */ private def saveXML(xmlContent: Node): Unit = { - xml.XML.save(configFilePath, xmlContent) + val writer = new PrintWriter(configFilePath) + writer.print(new PrettyPrinter(120,2).format(xmlContent)) + writer.close() logger info "Saved config file." } diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/InputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/InputImpl.scala index 67cd65b4..e68074f5 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/InputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/InputImpl.scala @@ -4,9 +4,9 @@ import org.codeoverflow.chatoverflow.WithLogger import org.codeoverflow.chatoverflow.api.io.input.Input import org.codeoverflow.chatoverflow.connector.Connector -import scala.collection.mutable +import scala.reflect.ClassTag -abstract class InputImpl[C <: Connector] extends Connection[C] with Input with WithLogger { +abstract class InputImpl[C <: Connector](implicit ct: ClassTag[C]) extends Connection[C] with Input with WithLogger { /** * Inits this connection, checks if teh source connector is defined, and can be inited, then calls start diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/OutputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/OutputImpl.scala index c6c4c715..f517ef0e 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/OutputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/OutputImpl.scala @@ -4,9 +4,9 @@ import org.codeoverflow.chatoverflow.WithLogger import org.codeoverflow.chatoverflow.api.io.output.Output import org.codeoverflow.chatoverflow.connector.Connector -import scala.collection.mutable +import scala.reflect.ClassTag -abstract class OutputImpl[C <: Connector] extends Connection[C] with Output with WithLogger { +abstract class OutputImpl[C <: Connector](implicit ct: ClassTag[C]) extends Connection[C] with Output with WithLogger { /** * Inits this connection, checks if teh source connector is defined, and can be inited, then calls start diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatInputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatInputImpl.scala index a35bfa97..7468a435 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatInputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatInputImpl.scala @@ -22,7 +22,7 @@ import scala.collection.mutable.ListBuffer @Impl(impl = classOf[DiscordChatInput], connector = classOf[DiscordChatConnector]) class DiscordChatInputImpl extends InputImpl[DiscordChatConnector] with DiscordChatInput with WithLogger { - private var channelId = getSourceIdentifier + private var channelId: Option[String] = None private val messages: ListBuffer[DiscordChatMessage] = ListBuffer[DiscordChatMessage]() private val privateMessages: ListBuffer[DiscordChatMessage] = ListBuffer[DiscordChatMessage]() private val messageHandler = ListBuffer[Consumer[DiscordChatMessage]]() @@ -33,7 +33,6 @@ class DiscordChatInputImpl extends InputImpl[DiscordChatConnector] with DiscordC private val privateMessageDeleteHandler = ListBuffer[Consumer[DiscordChatMessage]]() override def start(): Boolean = { - setChannel(getSourceIdentifier) sourceConnector.get.addMessageReceivedListener(onMessage) sourceConnector.get.addMessageUpdateListener(onMessageUpdate) sourceConnector.get.addMessageDeleteListener(onMessageDelete) @@ -47,15 +46,17 @@ class DiscordChatInputImpl extends InputImpl[DiscordChatConnector] with DiscordC */ private def onMessage(event: MessageReceivedEvent): Unit = { if (event.getMessage.getType == MessageType.DEFAULT) { - val message = DiscordChatInputImpl.parse(event.getMessage) - event.getChannelType match { - case ChannelType.TEXT if event.getTextChannel.getId == channelId => - messageHandler.foreach(_.accept(message)) - messages += message - case ChannelType.PRIVATE => - privateMessageHandler.foreach(_.accept(message)) - privateMessages += message - case _ => //Unknown channel, do nothing + if (channelId.isDefined) { + val message = DiscordChatInputImpl.parse(event.getMessage) + event.getChannelType match { + case ChannelType.TEXT if event.getTextChannel.getId == channelId.get => + messageHandler.foreach(_.accept(message)) + messages += message + case ChannelType.PRIVATE => + privateMessageHandler.foreach(_.accept(message)) + privateMessages += message + case _ => //Unknown channel, do nothing + } } } } @@ -94,20 +95,22 @@ class DiscordChatInputImpl extends InputImpl[DiscordChatConnector] with DiscordC * @param event a event with an deleted message */ private def onMessageDelete(event: MessageDeleteEvent): Unit = { - val id = event.getMessageId - event.getChannelType match { - case ChannelType.TEXT if event.getTextChannel.getId == channelId => - val i = messages.indexWhere(_.getId == id) - if (i != -1) { - val oldMessage = messages.remove(i) - messageDeleteHandler.foreach(_.accept(oldMessage)) - } - case ChannelType.PRIVATE => - val i = privateMessages.indexWhere(_.getId == id) - if (i != -1) { - val oldMessage = privateMessages.remove(i) - privateMessageDeleteHandler.foreach(_.accept(oldMessage)) - } + if (channelId.isDefined) { + val id = event.getMessageId + event.getChannelType match { + case ChannelType.TEXT if event.getTextChannel.getId == channelId.get => + val i = messages.indexWhere(_.getId == id) + if (i != -1) { + val oldMessage = messages.remove(i) + messageDeleteHandler.foreach(_.accept(oldMessage)) + } + case ChannelType.PRIVATE => + val i = privateMessages.indexWhere(_.getId == id) + if (i != -1) { + val oldMessage = privateMessages.remove(i) + privateMessageDeleteHandler.foreach(_.accept(oldMessage)) + } + } } } @@ -122,26 +125,35 @@ class DiscordChatInputImpl extends InputImpl[DiscordChatConnector] with DiscordC privateMessages.filter(_.getTimestamp > currentTime - lastMilliseconds).toList.asJava } - override def registerMessageHandler(handler: Consumer[DiscordChatMessage]): Unit = messageHandler += handler + override def registerMessageHandler(handler: Consumer[DiscordChatMessage]): Unit = { + if (channelId.isEmpty) throw new IllegalStateException("first set the channel for this input") + messageHandler += handler + } override def registerPrivateMessageHandler(handler : Consumer[DiscordChatMessage]): Unit = privateMessageHandler += handler - override def registerMessageEditHandler(handler: BiConsumer[DiscordChatMessage, DiscordChatMessage]): Unit = messageEditHandler += handler + override def registerMessageEditHandler(handler: BiConsumer[DiscordChatMessage, DiscordChatMessage]): Unit = { + if (channelId.isEmpty) throw new IllegalStateException("first set the channel for this input") + messageEditHandler += handler + } override def registerPrivateMessageEditHandler(handler: BiConsumer[DiscordChatMessage, DiscordChatMessage]): Unit = privateMessageEditHandler += handler - override def registerMessageDeleteHandler(handler: Consumer[DiscordChatMessage]): Unit = messageDeleteHandler += handler + override def registerMessageDeleteHandler(handler: Consumer[DiscordChatMessage]): Unit = { + if (channelId.isEmpty) throw new IllegalStateException("first set the channel for this input") + messageDeleteHandler += handler + } override def registerPrivateMessageDeleteHandler(handler: Consumer[DiscordChatMessage]): Unit = privateMessageDeleteHandler += handler override def setChannel(channelId: String): Unit = { sourceConnector.get.getTextChannel(channelId) match { - case Some(_) => this.channelId = channelId + case Some(_) => this.channelId = Some(channelId.trim) case None => throw new IllegalArgumentException("Channel with that id doesn't exist") } } - override def getChannelId: String = channelId + override def getChannelId: String = channelId.get override def getMessage(messageId: String): DiscordChatMessage = messages.find(_.getId == messageId).getOrElse(privateMessages.find(_.getId == messageId).orNull) diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatOutputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatOutputImpl.scala index f8460f05..7e174873 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatOutputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/discord/impl/DiscordChatOutputImpl.scala @@ -3,44 +3,30 @@ package org.codeoverflow.chatoverflow.requirement.service.discord.impl import org.codeoverflow.chatoverflow.WithLogger import org.codeoverflow.chatoverflow.api.io.output.chat.DiscordChatOutput import org.codeoverflow.chatoverflow.registry.Impl -import org.codeoverflow.chatoverflow.requirement.Connection +import org.codeoverflow.chatoverflow.requirement.OutputImpl import org.codeoverflow.chatoverflow.requirement.service.discord.DiscordChatConnector /** * This is the implementation of the discord chat output, using the discord connector. */ @Impl(impl = classOf[DiscordChatOutput], connector = classOf[DiscordChatConnector]) -class DiscordChatOutputImpl extends Connection[DiscordChatConnector] with DiscordChatOutput with WithLogger { +class DiscordChatOutputImpl extends OutputImpl[DiscordChatConnector] with DiscordChatOutput with WithLogger { - private var channelId: String = _ + private var channelId: Option[String] = None + override def start(): Boolean = true override def setChannel(channelId: String): Unit = { sourceConnector.get.getTextChannel(channelId) match { - case Some(_) => this.channelId = channelId + case Some(_) => this.channelId = Some(channelId.trim) case None => throw new IllegalArgumentException("Channel with that id doesn't exist") } } - override def getChannelId: String = channelId + override def getChannelId: String = channelId.get - override def init(): Boolean = { - if (sourceConnector.isDefined) { - if (sourceConnector.get.isRunning || sourceConnector.get.init()) { - setChannel(getSourceIdentifier) - true - } else false - } else { - logger warn "Source Connector not set." - false - } - } - - override def sendChatMessage(message: String): Unit = sourceConnector.get.sendChatMessage(channelId, message) - - override def serialize(): String = getSourceIdentifier - - override def deserialize(value: String): Unit = { - setSourceConnector(value) + override def sendChatMessage(message: String): Unit = { + val channel = channelId.getOrElse(throw new IllegalStateException("first set the channel for this output")) + sourceConnector.get.sendChatMessage(channel, message) } } 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 7c72afe0..4cb40543 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 @@ -6,6 +6,8 @@ import org.pircbotx.cap.EnableCapHandler import org.pircbotx.hooks.events.{MessageEvent, UnknownEvent} import org.pircbotx.{Configuration, PircBotX} +import scala.collection.mutable.ListBuffer + /** * The twitch connector connects to the irc service to work with chat messages. * @@ -17,7 +19,7 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect override protected var requiredCredentialKeys: List[String] = List(oauthKey) override protected var optionalCredentialKeys: List[String] = List() private var bot: PircBotX = _ - private var currentChannel: String = _ + private val channels = ListBuffer[String]() def addMessageEventListener(listener: MessageEvent => Unit): Unit = { twitchChatListener.addMessageEventListener(listener) @@ -27,24 +29,19 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect twitchChatListener.addUnknownEventListener(listener) } - def setChannel(channel: String): Unit = { - // Todo: Leave channel - setCurrentChannel(channel) - bot.send().joinChannel(currentChannel) - // TODO: TEST! + def joinChannel(channel: String): Unit = { + bot.send().joinChannel(channel) + channels += channel } - private def setCurrentChannel(channel: String): Unit = { - if (channel.startsWith("#")) { - currentChannel = channel.toLowerCase - } else { - currentChannel = "#" + channel.toLowerCase - } - } + def isJoined(channel: String): Boolean = channels.contains(channel) override def getUniqueTypeString: String = this.getClass.getName - def sendChatMessage(chatMessage: String): Unit = bot.send().message(currentChannel, chatMessage) + def sendChatMessage(channel: String, chatMessage: String): Unit = { + if (!isJoined(channel)) throw new IllegalArgumentException(s"you must join the '$channel' channel, before you can send messages to it") + bot.send().message(channel, chatMessage) + } private def getConfig: Configuration = { @@ -56,8 +53,6 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect logger warn s"key '$oauthKey' not found in credentials for '$sourceIdentifier'." } - setCurrentChannel(sourceIdentifier) - new Configuration.Builder() .setAutoNickChange(false) .setOnJoinWhoEnabled(false) @@ -67,7 +62,6 @@ class TwitchChatConnector(override val sourceIdentifier: String) extends Connect .addServer("irc.chat.twitch.tv") .setName(credentials.get.credentialsIdentifier) .setServerPassword(password.getOrElse("")) - .addAutoJoinChannel(currentChannel) .addListener(twitchChatListener) .buildConfiguration() } else { diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatInputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatInputImpl.scala index 9e9943b7..af8e3e90 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatInputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatInputImpl.scala @@ -30,6 +30,8 @@ class TwitchChatInputImpl extends InputImpl[chat.TwitchChatConnector] with Twitc private val messageHandler = ListBuffer[Consumer[TwitchChatMessage]]() private val privateMessageHandler = ListBuffer[Consumer[TwitchChatMessage]]() + private var currentChannel: Option[String] = None + override def start(): Boolean = { sourceConnector.get.addMessageEventListener(onMessage) sourceConnector.get.addUnknownEventListener(onUnknown) @@ -37,28 +39,30 @@ class TwitchChatInputImpl extends InputImpl[chat.TwitchChatConnector] with Twitc } private def onMessage(event: MessageEvent): Unit = { - val message = event.getMessage - val color = if (event.getV3Tags.get("color").contains("#")) event.getV3Tags.get("color") else "" - val subscriber = event.getV3Tags.get("subscriber") == "1" - val moderator = event.getV3Tags.get("mod") == "1" - val broadcaster = event.getV3Tags.get("badges").contains("broadcaster/1") - val turbo = event.getV3Tags.get("badges").contains("turbo/1") - val author = new TwitchChatMessageAuthor(event.getUser.getNick,color, broadcaster, moderator, subscriber, turbo) - val channel = new Channel(event.getChannelSource) - val emoticons = new java.util.ArrayList[ChatEmoticon]() - wholeEmoticonRegex.findAllMatchIn(event.getV3Tags.get("emotes")).foreach(matchedElement => { - val id = matchedElement.group(1) - emoticonRegex.findAllMatchIn(matchedElement.group(2)).foreach(matchedElement => { - val index = matchedElement.group(1).toInt - val regex = message.substring(index, matchedElement.group(2).toInt + 1) - val emoticon = new TwitchChatEmoticon(regex, id, index) - emoticons.add(emoticon) + if (currentChannel.isDefined && event.getChannelSource == currentChannel.get) { + val message = event.getMessage + val color = if (event.getV3Tags.get("color").contains("#")) event.getV3Tags.get("color") else "" + val subscriber = event.getV3Tags.get("subscriber") == "1" + val moderator = event.getV3Tags.get("mod") == "1" + val broadcaster = event.getV3Tags.get("badges").contains("broadcaster/1") + val turbo = event.getV3Tags.get("badges").contains("turbo/1") + val author = new TwitchChatMessageAuthor(event.getUser.getNick, color, broadcaster, moderator, subscriber, turbo) + val channel = new Channel(event.getChannelSource) + val emoticons = new java.util.ArrayList[ChatEmoticon]() + wholeEmoticonRegex.findAllMatchIn(event.getV3Tags.get("emotes")).foreach(matchedElement => { + val id = matchedElement.group(1) + emoticonRegex.findAllMatchIn(matchedElement.group(2)).foreach(matchedElement => { + val index = matchedElement.group(1).toInt + val regex = message.substring(index, matchedElement.group(2).toInt + 1) + val emoticon = new TwitchChatEmoticon(regex, id, index) + emoticons.add(emoticon) + }) }) - }) - val msg = new TwitchChatMessage(author, message, event.getTimestamp, channel, emoticons) + val msg = new TwitchChatMessage(author, message, event.getTimestamp, channel, emoticons) - messageHandler.foreach(consumer => consumer.accept(msg)) - messages += msg + messageHandler.foreach(consumer => consumer.accept(msg)) + messages += msg + } } private def onUnknown(event: UnknownEvent): Unit = { @@ -76,6 +80,7 @@ class TwitchChatInputImpl extends InputImpl[chat.TwitchChatConnector] with Twitc } override def getLastMessages(lastMilliseconds: Long): java.util.List[TwitchChatMessage] = { + if (currentChannel.isEmpty) throw new IllegalStateException("first set the channel for this input") val currentTime = Calendar.getInstance.getTimeInMillis messages.filter(_.getTimestamp > currentTime - lastMilliseconds).toList.asJava @@ -88,10 +93,15 @@ class TwitchChatInputImpl extends InputImpl[chat.TwitchChatConnector] with Twitc privateMessages.filter(_.getTimestamp > currentTime - lastMilliseconds).toList.asJava } - override def registerMessageHandler(handler: Consumer[TwitchChatMessage]): Unit = messageHandler += handler + override def registerMessageHandler(handler: Consumer[TwitchChatMessage]): Unit = { + if (currentChannel.isEmpty) throw new IllegalStateException("first set the channel for this input") + messageHandler += handler + } override def registerPrivateMessageHandler(handler: Consumer[TwitchChatMessage]): Unit = privateMessageHandler += handler - override def setChannel(channel: String): Unit = sourceConnector.get.setChannel(channel) - + override def setChannel(channel: String): Unit = { + currentChannel = Some(channel.trim) + if (!sourceConnector.get.isJoined(channel.trim)) sourceConnector.get.joinChannel(channel.trim) + } } \ No newline at end of file diff --git a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatOutputImpl.scala b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatOutputImpl.scala index b6d2dced..cc7d0c05 100644 --- a/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatOutputImpl.scala +++ b/src/main/scala/org/codeoverflow/chatoverflow/requirement/service/twitch/chat/impl/TwitchChatOutputImpl.scala @@ -12,7 +12,19 @@ import org.codeoverflow.chatoverflow.requirement.service.twitch.chat @Impl(impl = classOf[TwitchChatOutput], connector = classOf[chat.TwitchChatConnector]) class TwitchChatOutputImpl extends OutputImpl[chat.TwitchChatConnector] with TwitchChatOutput with WithLogger { - override def sendChatMessage(message: String): Unit = sourceConnector.get.sendChatMessage(message) + private var currentChannel: Option[String] = None + + override def sendChatMessage(message: String): Unit = { + currentChannel match { + case Some(value) => sourceConnector.get.sendChatMessage(value, message) + case None => throw new IllegalStateException("first set the channel for this output") + } + } override def start(): Boolean = true + + override def setChannel(channel: String): Unit = { + currentChannel = Some(channel.trim) + if (!sourceConnector.get.isJoined(channel.trim)) sourceConnector.get.joinChannel(channel.trim) + } }