All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
im.actor.server.bot.BotExtension.scala Maven / Gradle / Ivy
package im.actor.server.bot
import java.net.URLEncoder
import akka.actor._
import akka.http.scaladsl.util.FastFuture
import akka.util.Timeout
import com.google.protobuf.ByteString
import im.actor.api.rpc.users.ApiSex
import im.actor.config.ActorConfig
import im.actor.server.acl.ACLUtils
import im.actor.server.api.http.HttpApi
import im.actor.server.bot.http.BotsHttpHandler
import im.actor.server.db.DbExtension
import im.actor.server.model.AuthSession
import im.actor.server.office.EntityNotFound
import im.actor.server.persist.{ AuthIdRepo, AuthSessionRepo }
import im.actor.server.user.UserExtension
import im.actor.util.misc.IdUtils
import org.apache.commons.codec.digest.DigestUtils
import org.joda.time.DateTime
import shardakka.keyvalue.SimpleKeyValue
import shardakka.{ Codec, IntCodec, ShardakkaExtension }
import slick.dbio.DBIO
import scala.concurrent.Future
import scala.concurrent.forkjoin.ThreadLocalRandom
object BotExtension extends ExtensionId[BotExtension] with ExtensionIdProvider {
private[bot] val tokensKV = "BotsTokens"
private[bot] val whTokensKV = "BotsWHTokens"
private[bot] def whUserTokensKV(userId: Int) = s"BotsWHUserTokens-$userId"
override def createExtension(system: ExtendedActorSystem): BotExtension = new BotExtension(system)
override def lookup() = BotExtension
}
private[bot] final class BotExtension(_system: ActorSystem) extends Extension {
import _system._
type Token = String
type UserId = Int
type AuthId = Long
private implicit val system = _system
private implicit val timeout = Timeout(ActorConfig.defaultTimeout)
private val db = DbExtension(system).db
private lazy val userExt = UserExtension(system)
private val shardakka = ShardakkaExtension(system)
lazy val tokensKV = shardakka.simpleKeyValue(BotExtension.tokensKV, IntCodec)
private lazy val globalHooksKV: SimpleKeyValue[BotWebHook] = shardakka.simpleKeyValue(BotExtension.whTokensKV, BotWebHookCodec)
private def hooksKV(userId: UserId): SimpleKeyValue[String] =
shardakka.simpleKeyValue(BotExtension.whUserTokensKV(userId))
lazy val botServerBlueprint = new BotServerBlueprint(system)
HttpApi(system).registerRoute("bots") { implicit system ⇒
new BotsHttpHandler(this).routes
}
/**
* Creates a bot user
*
* @param nickname
* @param name
* @param isAdmin
* @return
*/
def create(nickname: String, name: String, isAdmin: Boolean): Future[(Token, UserId)] =
for {
id ← userExt.nextId()
result ← create(id, nickname, name, isAdmin)
} yield result
/**
* Creates a bot user
*
* @param userId
* @param nickname
* @param name
* @param isAdmin
* @return token future
*/
def create(userId: UserId, nickname: String, name: String, isAdmin: Boolean): Future[(Token, UserId)] = {
val token = ACLUtils.randomHash()
for {
user ← userExt.create(
userId = userId,
accessSalt = ACLUtils.nextAccessSalt(),
nickname = Some(nickname),
name = name,
countryCode = "US",
sex = ApiSex.Unknown,
isBot = true,
isAdmin = isAdmin
)
_ ← tokensKV.upsert(token, userId)
} yield (token, userId)
}
/**
* Check if the bot user already exists
*
* @param userId
* @return Future containing check result
*/
def exists(userId: UserId): Future[Boolean] = {
userExt.getApiStruct(userId, 0, 0) map (_ ⇒ true) recover {
case _: EntityNotFound ⇒ false
}
}
/**
* Gets userId associated with token
*
* @param token
* @return user id
*/
def findUserId(token: String): Future[Option[UserId]] =
tokensKV.get(token)
/**
* Gets webhook by token
*
* @param token
* @return
*/
def findWebHook(token: String): Future[Option[BotWebHook]] = globalHooksKV.get(token)
/**
* Gets webhook's token by name and userId
*
* @param userId
* @param name
* @return optional token
*/
def findToken(userId: UserId, name: String): Future[Option[String]] = hooksKV(userId).get(name)
/**
* Finds bot webhook
*
* @param userId
* @return
*/
def findWebHooks(userId: UserId): Future[Seq[String]] =
hooksKV(userId).getKeys()
/**
* Check if webhook exists
*
* @param userId
* @param name
* @return
*/
def webHookExists(userId: UserId, name: String): Future[Boolean] =
hooksKV(userId).get(name).map(_.nonEmpty)
/**
* Register webhook
*
* @param userId
* @param name
* @return
*/
def registerWebHook(userId: UserId, name: String): Future[String] = {
val token = genToken()
globalHooksKV.get(token) flatMap {
case Some(_) ⇒ registerWebHook(userId, name)
case None ⇒
val hook = BotWebHook(userId, name)
for {
_ ← globalHooksKV.upsert(token, hook)
_ ← hooksKV(userId).upsert(name, token)
} yield token
}
}
def getHookUrl(token: String): String =
s"${ActorConfig.baseUrl}/v1/bots/hooks/${URLEncoder.encode(token, "UTF-8")}"
def getAuthSession(userId: UserId): Future[AuthSession] = getOrCreateAuthSession(userId)
private def genToken(): String =
DigestUtils.md5Hex(ThreadLocalRandom.current().nextLong().toString)
private def getOrCreateAuthSession(userId: Int): Future[AuthSession] = {
db.run(AuthSessionRepo.findFirstByUserId(userId)) flatMap {
case Some(session) ⇒ FastFuture.successful(session)
case None ⇒
for {
authId ← db.run(getOrCreateAuthId(userId))
session = AuthSession(
userId = userId,
id = IdUtils.nextIntId(),
authId = authId,
appId = 0,
appTitle = "Bot",
deviceTitle = "Bot",
deviceHash = Array.empty,
authTime = new DateTime,
authLocation = "",
latitude = None,
longitude = None
)
_ ← db.run(AuthSessionRepo.create(session))
} yield session
}
}
private def getOrCreateAuthId(userId: Int): DBIO[AuthId] = {
AuthIdRepo.findFirstIdByUserId(userId) flatMap {
case Some(authId) ⇒
DBIO.successful(authId)
case None ⇒
val authId = ACLUtils.randomLong()
for {
_ ← AuthIdRepo.create(authId, None, None)
_ ← DBIO.from(userExt.auth(userId, authId))
} yield authId
}
}
}
private object BotWebHookCodec extends Codec[BotWebHook] {
override def fromBytes(bytes: ByteString): BotWebHook = BotWebHook.parseFrom(bytes.toByteArray)
override def toBytes(value: BotWebHook): ByteString = ByteString.copyFrom(value.toByteArray)
override def toString(bytes: ByteString): String = fromBytes(bytes).toString
}