From 6efca4a015601a6bf767a849d7971136a825f518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Nosin=CC=81ski?= Date: Thu, 2 Jul 2015 14:15:57 +0200 Subject: [PATCH 1/3] Moved SlackBotActor to core --- .../scalac/slack/common/BotInfoKeeper.scala | 7 ++ .../io/scalac/slack/common/Shutdownable.scala | 6 ++ .../slack/common/actors/SlackBotActor.scala | 83 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/main/scala/io/scalac/slack/common/BotInfoKeeper.scala create mode 100644 src/main/scala/io/scalac/slack/common/Shutdownable.scala create mode 100644 src/main/scala/io/scalac/slack/common/actors/SlackBotActor.scala diff --git a/src/main/scala/io/scalac/slack/common/BotInfoKeeper.scala b/src/main/scala/io/scalac/slack/common/BotInfoKeeper.scala new file mode 100644 index 0000000..c3988e2 --- /dev/null +++ b/src/main/scala/io/scalac/slack/common/BotInfoKeeper.scala @@ -0,0 +1,7 @@ +package io.scalac.slack.common + +import io.scalac.slack.api.BotInfo + +object BotInfoKeeper { + var current: Option[BotInfo] = None +} diff --git a/src/main/scala/io/scalac/slack/common/Shutdownable.scala b/src/main/scala/io/scalac/slack/common/Shutdownable.scala new file mode 100644 index 0000000..c679df4 --- /dev/null +++ b/src/main/scala/io/scalac/slack/common/Shutdownable.scala @@ -0,0 +1,6 @@ +package io.scalac.slack.common + +trait Shutdownable { + + def shutdown(): Unit +} diff --git a/src/main/scala/io/scalac/slack/common/actors/SlackBotActor.scala b/src/main/scala/io/scalac/slack/common/actors/SlackBotActor.scala new file mode 100644 index 0000000..2efeec2 --- /dev/null +++ b/src/main/scala/io/scalac/slack/common/actors/SlackBotActor.scala @@ -0,0 +1,83 @@ +package io.scalac.slack.common.actors + +import java.util.concurrent.TimeUnit + +import akka.actor.{Actor, ActorLogging, ActorRef, Props} +import akka.util.Timeout +import io.scalac.slack.api.{ApiActor, ApiTest, AuthData, AuthTest, BotInfo, Connected, RegisterModules, RtmData, RtmStart, RtmStartResponse, Start, Stop} +import io.scalac.slack.common.{BotInfoKeeper, RegisterDirectChannels, RegisterUsers, Shutdownable} +import io.scalac.slack.websockets.{WSActor, WebSocket} +import io.scalac.slack.{BotModules, Config, MessageEventBus, MigrationInProgress, OutgoingRichMessageProcessor, SlackError} + +import scala.concurrent.duration._ + +class SlackBotActor(modules: BotModules, eventBus: MessageEventBus, master: Shutdownable, usersStorageOpt: Option[ActorRef] = None) extends Actor with ActorLogging { + + import context.{dispatcher, system} + + val api = context.actorOf(Props[ApiActor]) + val richProcessor = context.actorOf(Props(classOf[OutgoingRichMessageProcessor], api, eventBus)) + val websocketClient = system.actorOf(Props(classOf[WSActor], eventBus), "ws-actor") + + var errors = 0 + + override def receive: Receive = { + case Start => + //test connection + log.info("trying to connect to Slack's server...") + api ! ApiTest() + case Stop => + master.shutdown() + case Connected => + log.info("connected successfully...") + log.info("trying to auth") + api ! AuthTest(Config.apiKey) + case ad: AuthData => + log.info("authenticated successfully") + log.info("request for websocket connection...") + api ! RtmStart(Config.apiKey) + case RtmData(url) => + log.info("fetched WSS URL") + log.info(url) + log.info("trying to connect to websockets channel") + val dropProtocol = url.drop(6) + val host = dropProtocol.split('/')(0) + val resource = dropProtocol.drop(host.length) + + implicit val timeout: Timeout = 5.seconds + + log.info(s"Connecting to host [$host] and resource [$resource]") + + websocketClient ! WebSocket.Connect(host, 443, resource, withSsl = true) + + context.system.scheduler.scheduleOnce(Duration.create(5, TimeUnit.SECONDS), self, RegisterModules) + + case bi@BotInfo(_, _) => + BotInfoKeeper.current = Some(bi) + case RegisterModules => + modules.registerModules(context, websocketClient) + case MigrationInProgress => + log.warning("MIGRATION IN PROGRESS, next try for 10 seconds") + system.scheduler.scheduleOnce(10.seconds, self, Start) + case se: SlackError if errors < 10 => + errors += 1 + log.error(s"connection error [$errors], repeat for 10 seconds") + log.error(s"SlackError occured [${se.toString}]") + system.scheduler.scheduleOnce(10.seconds, self, Start) + case se: SlackError => + log.error(s"SlackError occured [${se.toString}]") + master.shutdown() + case res: RtmStartResponse => + if(usersStorageOpt.isDefined) { + val userStorage = usersStorageOpt.get + + userStorage ! RegisterUsers(res.users: _*) + userStorage ! RegisterDirectChannels(res.ims: _*) + } + + case WebSocket.Release => + websocketClient ! WebSocket.Release + + } + +} From 3d3be1c3219c26c336ef51c473297ae036ebce08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Nosin=CC=81ski?= Date: Thu, 2 Jul 2015 14:26:50 +0200 Subject: [PATCH 2/3] Moved CommandsRecognizerBot into core --- .../bots/system/CommandsRecognizerBot.scala | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/scala/io/scalac/slack/bots/system/CommandsRecognizerBot.scala diff --git a/src/main/scala/io/scalac/slack/bots/system/CommandsRecognizerBot.scala b/src/main/scala/io/scalac/slack/bots/system/CommandsRecognizerBot.scala new file mode 100644 index 0000000..87cd837 --- /dev/null +++ b/src/main/scala/io/scalac/slack/bots/system/CommandsRecognizerBot.scala @@ -0,0 +1,43 @@ +package io.scalac.slack.bots.system + +import io.scalac.slack.MessageEventBus +import io.scalac.slack.bots.IncomingMessageListener +import io.scalac.slack.common.{BaseMessage, BotInfoKeeper, Command} + +class CommandsRecognizerBot(override val bus: MessageEventBus) extends IncomingMessageListener { + + val commandChar = '$' + + def receive: Receive = { + + case bm@BaseMessage(text, channel, user, dateTime, edited) => + //COMMAND links list with bot's nam jack can be called: + // jack link list + // jack: link list + // @jack link list + // @jack: link list + // $link list + def changeIntoCommand(pattern: String): Boolean = { + if (text.trim.startsWith(pattern)) { + val tokenized = text.trim.drop(pattern.length).trim.split("\\s") + publish(Command(tokenized.head, tokenized.tail.toList.filter(_.nonEmpty), bm)) + true + } + false + } + + //call by commad character + if (!changeIntoCommand(commandChar.toString)) + BotInfoKeeper.current match { + case Some(bi) => + //call by name + changeIntoCommand(bi.name + ":") || + changeIntoCommand(bi.name) || + //call by ID + changeIntoCommand(s"<@${bi.id}>:") || + changeIntoCommand(s"<@${bi.id}>") + + case None => //nothing to do! + } + } +} From 0308054154a6cd002b8ab0b30868fe7810ea7480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Nosin=CC=81ski?= Date: Thu, 2 Jul 2015 14:31:08 +0200 Subject: [PATCH 3/3] Moved HelpBot into core --- .../io/scalac/slack/bots/system/HelpBot.scala | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/scala/io/scalac/slack/bots/system/HelpBot.scala diff --git a/src/main/scala/io/scalac/slack/bots/system/HelpBot.scala b/src/main/scala/io/scalac/slack/bots/system/HelpBot.scala new file mode 100644 index 0000000..2646aa2 --- /dev/null +++ b/src/main/scala/io/scalac/slack/bots/system/HelpBot.scala @@ -0,0 +1,20 @@ +package io.scalac.slack.bots.system + +import io.scalac.slack.MessageEventBus +import io.scalac.slack.bots.AbstractBot +import io.scalac.slack.common.{Command, HelpRequest, OutboundMessage} + +/** + * Maintainer: Patryk + */ +class HelpBot(override val bus: MessageEventBus) extends AbstractBot { + override def act: Receive = { + case Command("help", options, raw) => + publish(HelpRequest(options.headOption, raw.channel)) + } + + override def help(channel: String): OutboundMessage = OutboundMessage(channel, + s"*$name* is for helping. Duh \\n" + + s"`help` - display help from all bots \\n " + + s"`help {botName}` - display help for certain bot module") +}