Skip to content

Commit

Permalink
moved source for core to separate repo
Browse files Browse the repository at this point in the history
  • Loading branch information
pjazdzewski1990 committed Jun 23, 2015
1 parent 21ace5e commit fd86947
Show file tree
Hide file tree
Showing 35 changed files with 1,943 additions and 0 deletions.
37 changes: 37 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name := "slack-scala-bot-core"

version := "0.1"

scalaVersion := "2.11.5"

organization := "io.scalac"

libraryDependencies ++= {
val akkaVersion = "2.3.9"
Seq(
"org.mockito" % "mockito-core" % "1.10.19",
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
"io.spray" %% "spray-json" % "1.3.1",
"io.spray" %% "spray-client" % "1.3.1",
"io.spray" %% "spray-can" % "1.3.2",
"com.wandoulabs.akka" %% "spray-websocket" % "0.1.4",
"joda-time" % "joda-time" % "2.7",
"org.joda" % "joda-convert" % "1.7",
"org.scalatest" %% "scalatest" % "2.2.1" % "test",
"log4j" % "log4j" % "1.2.17",
"org.slf4j" % "slf4j-api" % "1.7.5",
"org.slf4j" % "slf4j-log4j12" % "1.7.5",
"org.scala-lang.modules" %% "scala-xml" % "1.0.2",
"org.scala-lang" % "scala-compiler" % "2.10.2",
"org.scala-lang" % "jline" % "2.10.2",
"org.twitter4j" % "twitter4j-core" % "4.0.0",
"com.typesafe.slick" %% "slick" % "2.1.0",
"com.h2database" % "h2" % "1.4.186"
)
}

resolvers += "spray repo" at "http://repo.spray.io"

publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
14 changes: 14 additions & 0 deletions src/main/resources/log4j.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%-20.20t | %-5p] %c - %m%n"/>
</layout>
</appender>
<root>
<priority value="debug"/>
<appender-ref ref="console"/>
</root>
</log4j:configuration>
7 changes: 7 additions & 0 deletions src/main/scala/io/scalac/slack/BotModules.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.scalac.slack

import akka.actor.{ActorContext, ActorRef}

trait BotModules {
def registerModules(context: ActorContext, websocketClient: ActorRef): Unit
}
25 changes: 25 additions & 0 deletions src/main/scala/io/scalac/slack/Config.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.scalac.slack

import com.typesafe.config.ConfigFactory
import io.scalac.slack.api.APIKey

/**
* Created on 20.01.15 22:17
*/
object Config {
def websocketKey: String = config.getString("websocket.key")

private val config = ConfigFactory.load()

def apiKey: APIKey = APIKey(config.getString("api.key"))

def baseUrl(endpoint: String) = config.getString("api.base.url") + endpoint

def scalaLibraryPath = config.getString("scalaLibraryPath")

def consumerKey: String = config.getString("twitter.consumerKey")
def consumerKeySecret: String = config.getString("twitter.consumerKeySecret")
def accessToken: String = config.getString("twitter.accessToken")
def accessTokenSecret: String = config.getString("twitter.accessTokenSecret")
def twitterGuardians: String = config.getString("twitter.guardians")
}
36 changes: 36 additions & 0 deletions src/main/scala/io/scalac/slack/IncomingMessageProcessor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.scalac.slack

import akka.actor.{Actor, ActorLogging}
import io.scalac.slack.common._
import spray.json._

/**
* Created on 08.02.15 23:36
* Incoming message processor should parse incoming string
* and change into proper protocol
*/
class IncomingMessageProcessor(eventBus: MessageEventBus) extends Actor with ActorLogging {

import io.scalac.slack.common.MessageJsonProtocol._

override def receive: Receive = {

case s: String =>
try {
val mType = s.parseJson.convertTo[MessageType]
val incomingMessage: IncomingMessage = mType match {
case MessageType("hello", _) => Hello
case MessageType("pong", _) => Pong
case MessageType("message", None) => s.parseJson.convertTo[BaseMessage]
case _ =>
UndefinedMessage(s)
}
eventBus.publish(incomingMessage)
}
catch {
case e : Exception =>
eventBus.publish(UndefinedMessage(s))
}
case ignored => //nothing special
}
}
27 changes: 27 additions & 0 deletions src/main/scala/io/scalac/slack/MessageEventBus.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.scalac.slack

import akka.event.{ActorEventBus, LookupClassification}
import io.scalac.slack.common._

/**
* Created on 08.02.15 22:16
*/
class MessageEventBus extends ActorEventBus with LookupClassification {
override type Event = MessageEvent

override type Classifier = MessageEventType

override protected def mapSize(): Int = 2

override protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event
}

override protected def classify(event: Event): Classifier = {
event match {
case im: IncomingMessage => Incoming
case om: OutgoingMessage => Outgoing
case rich: RichOutboundMessage => Outgoing
}
}
}
29 changes: 29 additions & 0 deletions src/main/scala/io/scalac/slack/OutgoingMessageProcessor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.scalac.slack

import akka.actor.{Actor, ActorLogging, ActorRef}
import io.scalac.slack.common._
import io.scalac.slack.websockets.WebSocket

/**
* Created on 08.02.15 23:00
* Outgoing message protocol should change received
* protocol into string and send it to websocket
*/
class OutgoingMessageProcessor(wsActor: ActorRef, eventBus: MessageEventBus) extends Actor with ActorLogging {

override def receive: Receive = {
case Ping =>
wsActor ! WebSocket.Send(Ping.toJson)

case msg: OutboundMessage =>
wsActor ! WebSocket.Send(msg.toJson)

case ignored => //nothing else

}

@throws[Exception](classOf[Exception])
override def preStart(): Unit = {
eventBus.subscribe(self, Outgoing)
}
}
26 changes: 26 additions & 0 deletions src/main/scala/io/scalac/slack/OutgoingRichMessageProcessor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.scalac.slack

import akka.actor.{Actor, ActorLogging, ActorRef}
import io.scalac.slack.common._

/**
* Created on 08.02.15 23:00
*
*/
class OutgoingRichMessageProcessor(apiActor: ActorRef, eventBus: MessageEventBus) extends Actor with ActorLogging {

override def receive: Receive = {

case msg: RichOutboundMessage =>
if (msg.elements.nonEmpty)
apiActor ! msg //trasport through WebAPI until RTM support begin

case ignored => //nothing else

}

@throws[Exception](classOf[Exception])
override def preStart(): Unit = {
eventBus.subscribe(self, Outgoing)
}
}
35 changes: 35 additions & 0 deletions src/main/scala/io/scalac/slack/SlackError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.scalac.slack

/**
* Created on 21.01.15 01:23
*/
sealed trait SlackError

object ApiTestError extends SlackError

//no authentication token provided
object NotAuthenticated extends SlackError

//invalid auth token
object InvalidAuth extends SlackError

//token is for deleted user or team
object AccountInactive extends SlackError

//team is being migrated between servers
object MigrationInProgress extends SlackError

case class UnspecifiedError(msg: String) extends SlackError


object SlackError {
def apply(errorName: String) = {
errorName match {
case "not_authed" => NotAuthenticated
case "invalid_auth" => InvalidAuth
case "account_inactive" => AccountInactive
case "migration_in_progress" => MigrationInProgress
case err => new UnspecifiedError(err)
}
}
}
8 changes: 8 additions & 0 deletions src/main/scala/io/scalac/slack/api/APIKey.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.scalac.slack.api

/**
* Created on 20.01.15 22:56
*/
case class APIKey(key: String){
override def toString: String = key
}
84 changes: 84 additions & 0 deletions src/main/scala/io/scalac/slack/api/ApiActor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.scalac.slack.api

import akka.actor.{Actor, ActorLogging}
import io.scalac.slack.api.ResponseObject._
import io.scalac.slack.common.JsonProtocols._
import io.scalac.slack.common.{Attachment, RichOutboundMessage}
import io.scalac.slack.{ApiTestError, Config, SlackError}
import spray.json._

import scala.util.{Failure, Success}

/**
* Created on 21.01.15 20:32
*/
class ApiActor extends Actor with ActorLogging {

import context.dispatcher
import io.scalac.slack.api.Unmarshallers._

override def receive = {

case ApiTest(param, error) =>
log.debug("api.test requested")
val send = sender()
val params = Map("param" -> param, "error" -> error).collect { case (key, Some(value)) => key -> value}

SlackApiClient.get[ApiTestResponse]("api.test", params) onComplete {
case Success(res) =>
if (res.ok) {
send ! Ok(res.args)
}
else {
send ! ApiTestError
}
case Failure(ex) =>
send ! ex

}

case AuthTest(token) =>
log.debug("auth.test requested")
val send = sender()

SlackApiClient.get[AuthTestResponse]("auth.test", Map("token" -> token.key)) onComplete {
case Success(res) =>

if (res.ok)
send ! AuthData(res)
else
send ! SlackError(res.error.get)
case Failure(ex) =>
send ! ex
}

case RtmStart(token) =>
log.debug("rtm.start requested")
val send = sender()

SlackApiClient.get[RtmStartResponse]("rtm.start", Map("token" -> token.key)) onComplete {

case Success(res) =>
if (res.ok)
send ! RtmData(res.url)
send ! res.self
case Failure(ex) =>
send ! ex
}
case msg: RichOutboundMessage =>
log.debug("chat.postMessage requested")

val attachments = msg.elements.filter(_.isValid).map(_.toJson).mkString("[", ",", "]")
val params = Map("token" -> Config.apiKey.key, "channel" -> msg.channel, "as_user" -> "true", "attachments" -> attachments)

SlackApiClient.post[ChatPostMessageResponse]("chat.postMessage", params) onComplete {
case Success(res) =>
if (res.ok) {
log.info("[chat.postMessage]: message delivered: " + res.toString)
}
case Failure(ex) =>
log.error("[chat.postMessage] Error encountered - " + ex.getMessage)
}

}
}
15 changes: 15 additions & 0 deletions src/main/scala/io/scalac/slack/api/ApiClient.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.scalac.slack.api

import spray.http.{HttpMethod, HttpRequest}
import spray.json.JsonReader

import scala.concurrent.Future

/**
* Created on 25.01.15 22:22
*/
trait ApiClient {

def request[T <: ResponseObject](method: HttpMethod, endpoint: String, params: Map[String, String] = Map.empty[String, String])(implicit reader: JsonReader[T]): Future[T]

}
29 changes: 29 additions & 0 deletions src/main/scala/io/scalac/slack/api/Message.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.scalac.slack.api

/**
* Created on 20.01.15 23:59
* Messages sends between actors
*/
sealed trait Message

case object Start extends Message
case object Stop extends Message
case object RegisterModules extends Message

//API CALLS
case class ApiTest(param: Option[String] = None, error: Option[String] = None) extends Message

case class AuthTest(token: APIKey) extends Message

case class RtmStart(token: APIKey) extends Message

//API RESPONSES
case class Ok(args: Option[Map[String, String]]) extends Message

case class AuthData(url: String, team: String, user: String, teamId: String, userId: String) extends Message

case class RtmData(url: String)

object AuthData {
def apply(atr: AuthData) = atr
}
Loading

0 comments on commit fd86947

Please sign in to comment.