From 2b14f0bcc5d890a190aac98871e907a95dd42104 Mon Sep 17 00:00:00 2001 From: handlessmidas Date: Mon, 2 Mar 2020 18:37:11 +0300 Subject: [PATCH 1/4] Added random cat generation --- build.sbt | 6 ++++- src/main/scala/bot/BotStarter.scala | 40 +++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index 2350a09..548dca0 100644 --- a/build.sbt +++ b/build.sbt @@ -4,4 +4,8 @@ version := "0.1" scalaVersion := "2.12.9" -libraryDependencies += "com.bot4s" %% "telegram-core" % "4.4.0-RC2" \ No newline at end of file +libraryDependencies ++= Seq( + "com.bot4s" %% "telegram-core" % "4.4.0-RC2", + "com.softwaremill.sttp" %% "json4s" % "1.7.2", + "org.json4s" %% "json4s-native" % "3.6.0" +) \ No newline at end of file diff --git a/src/main/scala/bot/BotStarter.scala b/src/main/scala/bot/BotStarter.scala index 565a9a9..45802b5 100644 --- a/src/main/scala/bot/BotStarter.scala +++ b/src/main/scala/bot/BotStarter.scala @@ -1,5 +1,6 @@ package bot +import bot.BotStarter.Service import cats.instances.future._ import cats.syntax.functor._ import com.bot4s.telegram.api.RequestHandler @@ -7,7 +8,8 @@ import com.bot4s.telegram.api.declarative.Commands import com.bot4s.telegram.clients.{FutureSttpClient, ScalajHttpClient} import com.bot4s.telegram.future.{Polling, TelegramBot} import com.bot4s.telegram.models.{Message, User} -import com.softwaremill.sttp.SttpBackendOptions +import com.softwaremill.sttp.{SttpBackendOptions, sttp} +import com.softwaremill.sttp.json4s.asJson import com.softwaremill.sttp.okhttp.{OkHttpBackend, OkHttpFutureBackend} import slogging.{LogLevel, LoggerConfig, PrintLoggerFactory} @@ -17,8 +19,12 @@ import scala.collection.mutable.Queue import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext, Future} import scala.io.Source +import scala.util.Random +import scala.io.Source +import com.softwaremill.sttp._ +import com.softwaremill.sttp.json4s._ -class BotStarter(override val client: RequestHandler[Future]) extends TelegramBot +class BotStarter(override val client: RequestHandler[Future], val service: Service) extends TelegramBot with Polling with Commands[Future] { @@ -29,7 +35,7 @@ class BotStarter(override val client: RequestHandler[Future]) extends TelegramBo case None => reply("Register error").void case Some(user) => { registeredUsers += user - reply(s"You're registered.\n Your id is ${user.id}").void + reply(s"You're registered.\nYour id is ${user.id}").void } } } @@ -41,19 +47,43 @@ class BotStarter(override val client: RequestHandler[Future]) extends TelegramBo } reply(usersString).void } + + onCommand("/cat") { implicit msg => + service.getCat().flatMap(reply(_)).void + } } object BotStarter { + + implicit val serialization = org.json4s.native.Serialization + + case class Response(data: List[Data]) + case class Data(link: String) + + class Service(implicit val backend: SttpBackend[Future, Nothing]) { + implicit val ec: ExecutionContext = ExecutionContext.global + val request: RequestT[Id, Response, Nothing] = sttp + .header("Authorization", "Client-ID 2a47c24862afdf7") + .get(uri"https://api.imgur.com/3/gallery/search?q=cats") + .response(asJson[Response]) + + def getCat() = backend.send(request).map { response => + scala.util.Random.shuffle(response.unsafeBody.data).head.link + } + } + def main(args: Array[String]): Unit = { implicit val ec: ExecutionContext = ExecutionContext.global - implicit val backend = OkHttpFutureBackend( + implicit val backend: SttpBackend[Future, Nothing] = OkHttpFutureBackend( SttpBackendOptions.Default.socksProxy("ps8yglk.ddns.net", 11999) ) val fileSource = Source.fromFile("token.txt") val token = fileSource.mkString fileSource.close() - val bot = new BotStarter(new FutureSttpClient(token)) + + val service: Service = new Service() + val bot = new BotStarter(new FutureSttpClient(token), service) Await.result(bot.run(), Duration.Inf) } } \ No newline at end of file From f5d51f60026803026431db082af8fc40b2d0cbbf Mon Sep 17 00:00:00 2001 From: handlessmidas Date: Fri, 1 May 2020 15:38:47 +0500 Subject: [PATCH 2/4] Divided into classes --- src/main/scala/bot/BotStarter.scala | 69 +++++++++++++------------ src/main/scala/bot/MessageHandler.scala | 21 ++++++++ src/main/scala/bot/Service.scala | 24 +++++++++ src/main/scala/bot/UserHandler.scala | 16 ++++++ 4 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 src/main/scala/bot/MessageHandler.scala create mode 100644 src/main/scala/bot/Service.scala create mode 100644 src/main/scala/bot/UserHandler.scala diff --git a/src/main/scala/bot/BotStarter.scala b/src/main/scala/bot/BotStarter.scala index 45802b5..387fcc7 100644 --- a/src/main/scala/bot/BotStarter.scala +++ b/src/main/scala/bot/BotStarter.scala @@ -1,6 +1,5 @@ package bot -import bot.BotStarter.Service import cats.instances.future._ import cats.syntax.functor._ import com.bot4s.telegram.api.RequestHandler @@ -23,67 +22,73 @@ import scala.util.Random import scala.io.Source import com.softwaremill.sttp._ import com.softwaremill.sttp.json4s._ +import org.json4s.native.Serialization -class BotStarter(override val client: RequestHandler[Future], val service: Service) extends TelegramBot +class BotStarter(override val client: RequestHandler[Future], val service: Service, + val userHandler: UserHandler, val messageHandler: MessageHandler) extends TelegramBot with Polling with Commands[Future] { - val registeredUsers: mutable.Set[User] = mutable.Set[User]() - onCommand("/start") { implicit msg => msg.from match { case None => reply("Register error").void - case Some(user) => { - registeredUsers += user - reply(s"You're registered.\nYour id is ${user.id}").void - } + case Some(user) => + userHandler.register(user) + reply(s"You're registered.\nYour id is ${user.id}").void } } onCommand("/users") { implicit msg => - var usersString = "" - registeredUsers.foreach { - it => usersString += s"${it.firstName} ${it.lastName}\n" - } - reply(usersString).void + reply(userHandler.show()).void } - onCommand("/cat") { implicit msg => - service.getCat().flatMap(reply(_)).void + onCommand("/check") { implicit msg => + msg.from match { + case None => reply("Error.").void + case Some(user) => + val messages = messageHandler.show(user.id.toString) + messageHandler.clear(user.id.toString) + reply(messages).void + } } -} - -object BotStarter { - - implicit val serialization = org.json4s.native.Serialization - case class Response(data: List[Data]) - case class Data(link: String) - - class Service(implicit val backend: SttpBackend[Future, Nothing]) { - implicit val ec: ExecutionContext = ExecutionContext.global - val request: RequestT[Id, Response, Nothing] = sttp - .header("Authorization", "Client-ID 2a47c24862afdf7") - .get(uri"https://api.imgur.com/3/gallery/search?q=cats") - .response(asJson[Response]) + onCommand("/send") { implicit msg => + var from_id : Int = 0 + msg.from match { + case None => from_id = 0 + case Some(user) => from_id = user.id + } - def getCat() = backend.send(request).map { response => - scala.util.Random.shuffle(response.unsafeBody.data).head.link + msg.text match { + case None => reply("Enter non-empty message").void + case Some(s) => + val id: String = s.slice(6, 15) + val text: String = s.slice(16, s.length) + messageHandler.send(from_id.toString, id, text) + reply(s"Message was sent to $id").void } } + onCommand("/cat") { implicit msg => + service.getCat.flatMap(reply(_)).void + } +} + +object BotStarter { def main(args: Array[String]): Unit = { implicit val ec: ExecutionContext = ExecutionContext.global implicit val backend: SttpBackend[Future, Nothing] = OkHttpFutureBackend( SttpBackendOptions.Default.socksProxy("ps8yglk.ddns.net", 11999) ) + val userHandler = new UserHandler + val messageHandler = new MessageHandler val fileSource = Source.fromFile("token.txt") val token = fileSource.mkString fileSource.close() val service: Service = new Service() - val bot = new BotStarter(new FutureSttpClient(token), service) + val bot = new BotStarter(new FutureSttpClient(token), service, userHandler, messageHandler) Await.result(bot.run(), Duration.Inf) } } \ No newline at end of file diff --git a/src/main/scala/bot/MessageHandler.scala b/src/main/scala/bot/MessageHandler.scala new file mode 100644 index 0000000..66bb0ef --- /dev/null +++ b/src/main/scala/bot/MessageHandler.scala @@ -0,0 +1,21 @@ +package bot + +import scala.collection.mutable + +class MessageHandler { + var messagesUsers: mutable.Map[String, mutable.ListBuffer[(String, String)]] = + mutable.Map[String, mutable.ListBuffer[(String, String)]]().withDefaultValue(mutable.ListBuffer()) + + def send(senderId: String, recieverId: String, message: String): Unit = { + if (!messagesUsers.contains(recieverId)) { + messagesUsers(recieverId) = mutable.ListBuffer() + } + messagesUsers(recieverId) += Tuple2(senderId, message) + } + + def show(id: String): String = messagesUsers(id).foldLeft(""){ + (last, p) => last + s"From: ${p._1}\n${p._2}\n\n" + } + + def clear(id: String): Unit = messagesUsers(id).clear() +} diff --git a/src/main/scala/bot/Service.scala b/src/main/scala/bot/Service.scala new file mode 100644 index 0000000..dd71658 --- /dev/null +++ b/src/main/scala/bot/Service.scala @@ -0,0 +1,24 @@ +package bot + +import com.softwaremill.sttp.{SttpBackendOptions, sttp} +import com.softwaremill.sttp.json4s.asJson + +import scala.concurrent.{Await, ExecutionContext, Future} +import com.softwaremill.sttp._ +import org.json4s.native.Serialization + +case class Response(data: List[Data]) +case class Data(link: String) + +class Service(implicit val backend: SttpBackend[Future, Nothing], + implicit val serialization : Serialization.type = org.json4s.native.Serialization, + implicit val ec: ExecutionContext = ExecutionContext.global) { + val request: RequestT[Id, Response, Nothing] = sttp + .header("Authorization", "Client-ID 2a47c24862afdf7") + .get(uri"https://api.imgur.com/3/gallery/search?q=cats") + .response(asJson[Response]) + + def getCat: Future[String] = backend.send(request).map { response => + scala.util.Random.shuffle(response.unsafeBody.data).head.link + } +} \ No newline at end of file diff --git a/src/main/scala/bot/UserHandler.scala b/src/main/scala/bot/UserHandler.scala new file mode 100644 index 0000000..96fb193 --- /dev/null +++ b/src/main/scala/bot/UserHandler.scala @@ -0,0 +1,16 @@ +package bot + +import com.bot4s.telegram.models.User + +import scala.collection.mutable + +class UserHandler { + + val registeredUsers: mutable.Set[User] = mutable.Set[User]() + + def register(user: User): Unit = registeredUsers += user + + def show(): String = registeredUsers.foldLeft("") { + (last, user) => last + s"${user.firstName} ${user.lastName.getOrElse("")}, id: ${user.id}\n" + } +} From c2ed21074e1a149e66d393bcbabe9047c132804e Mon Sep 17 00:00:00 2001 From: handlessmidas Date: Sun, 3 May 2020 17:12:16 +0500 Subject: [PATCH 3/4] Added DB support --- build.sbt | 10 +++++- src/main/resourses/reference.conf | 6 ++++ src/main/scala/bot/BotStarter.scala | 36 ++++++++++++------- src/main/scala/bot/DBMessageHandler.scala | 43 +++++++++++++++++++++++ src/main/scala/bot/DBUserHandler.scala | 38 ++++++++++++++++++++ 5 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 src/main/resourses/reference.conf create mode 100644 src/main/scala/bot/DBMessageHandler.scala create mode 100644 src/main/scala/bot/DBUserHandler.scala diff --git a/build.sbt b/build.sbt index 548dca0..ee855e4 100644 --- a/build.sbt +++ b/build.sbt @@ -8,4 +8,12 @@ libraryDependencies ++= Seq( "com.bot4s" %% "telegram-core" % "4.4.0-RC2", "com.softwaremill.sttp" %% "json4s" % "1.7.2", "org.json4s" %% "json4s-native" % "3.6.0" -) \ No newline at end of file +) + +libraryDependencies ++= Seq( + "com.typesafe.slick" %% "slick" % "3.3.1", + "org.slf4j" % "slf4j-nop" % "1.7.26", + "com.typesafe.slick" %% "slick-hikaricp" % "3.3.1" +) + +libraryDependencies += "com.h2database" % "h2" % "1.4.200" \ No newline at end of file diff --git a/src/main/resourses/reference.conf b/src/main/resourses/reference.conf new file mode 100644 index 0000000..1601bc7 --- /dev/null +++ b/src/main/resourses/reference.conf @@ -0,0 +1,6 @@ +h2mem1 = { + url = "jdbc:h2:mem:test1" + driver = org.h2.Driver + connectionPool = disabled + keepAliveConnection = true +} \ No newline at end of file diff --git a/src/main/scala/bot/BotStarter.scala b/src/main/scala/bot/BotStarter.scala index 387fcc7..d840dd4 100644 --- a/src/main/scala/bot/BotStarter.scala +++ b/src/main/scala/bot/BotStarter.scala @@ -23,9 +23,10 @@ import scala.io.Source import com.softwaremill.sttp._ import com.softwaremill.sttp.json4s._ import org.json4s.native.Serialization +import slick.lifted.TableQuery class BotStarter(override val client: RequestHandler[Future], val service: Service, - val userHandler: UserHandler, val messageHandler: MessageHandler) extends TelegramBot + val userHandler: DBUserHandler, val messageHandler: DBMessageHandler) extends TelegramBot with Polling with Commands[Future] { @@ -33,22 +34,23 @@ class BotStarter(override val client: RequestHandler[Future], val service: Servi msg.from match { case None => reply("Register error").void case Some(user) => - userHandler.register(user) - reply(s"You're registered.\nYour id is ${user.id}").void + userHandler.register(user).flatMap(_ => + reply(s"You're registered.\nYour id is ${user.id}").void) } } onCommand("/users") { implicit msg => - reply(userHandler.show()).void + userHandler.show.flatMap(reply(_).void) } onCommand("/check") { implicit msg => msg.from match { case None => reply("Error.").void case Some(user) => - val messages = messageHandler.show(user.id.toString) - messageHandler.clear(user.id.toString) - reply(messages).void + messageHandler.show(user.id.toString).flatMap(messages => + messageHandler.clear(user.id.toString).flatMap(_ => + reply(messages).void) + ) } } @@ -81,14 +83,24 @@ object BotStarter { SttpBackendOptions.Default.socksProxy("ps8yglk.ddns.net", 11999) ) - val userHandler = new UserHandler - val messageHandler = new MessageHandler + val users = TableQuery[Users] + val messages = TableQuery[Messages] + val userHandler = new DBUserHandler(users, messages) + val messageHandler = new DBMessageHandler(users, messages) + + val service: Service = new Service() + val fileSource = Source.fromFile("token.txt") val token = fileSource.mkString fileSource.close() - val service: Service = new Service() - val bot = new BotStarter(new FutureSttpClient(token), service, userHandler, messageHandler) - Await.result(bot.run(), Duration.Inf) + val init = for { + _ <- userHandler.init() + _ <- messageHandler.init() + bot = new BotStarter(new FutureSttpClient(token), service, + userHandler, messageHandler) + _ <- bot.run() + } yield () + Await.result(init, Duration.Inf) } } \ No newline at end of file diff --git a/src/main/scala/bot/DBMessageHandler.scala b/src/main/scala/bot/DBMessageHandler.scala new file mode 100644 index 0000000..341126b --- /dev/null +++ b/src/main/scala/bot/DBMessageHandler.scala @@ -0,0 +1,43 @@ +package bot + +import scala.concurrent.{Await, Future} +import slick.jdbc.H2Profile.api._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.Duration + +class Messages(tag: Tag) extends Table[(Int, String, Int, Int)](tag, "Messages"){ + def id = column[Int]("Id", O.PrimaryKey) + def message = column[String]("Message") + def senderId = column[Int]("SenderId") + def receiverId = column[Int]("ReceiverId") + def * = (id, message, senderId, receiverId) +} + +class DBMessageHandler(users: TableQuery[Users], messages: TableQuery[Messages]) { + lazy val database = Database.forConfig("h2mem1") + + def init(): Future[Unit] = { + database.run(messages.schema.createIfNotExists) + } + + def send(senderId: String, recieverId: String, message: String): Unit = { + val req = for { + _ <- messages += (-1, message, senderId.toInt, recieverId.toInt) + } yield() + database.run(req) + } + + def show(id: String): Future[String] = { + val req = for { + idMessages <- messages.filter(it => it.receiverId === id.toInt).result + } yield idMessages + database.run(req).flatMap(seq => Future ( + seq.map(it => s"From ${it._3}\n${it._2}\n").mkString("\n")) + ) + } + + def clear(id: String): Future[Unit] = { + database.run(messages.delete).flatMap(_ => Future()) + } +} diff --git a/src/main/scala/bot/DBUserHandler.scala b/src/main/scala/bot/DBUserHandler.scala new file mode 100644 index 0000000..bda9f2d --- /dev/null +++ b/src/main/scala/bot/DBUserHandler.scala @@ -0,0 +1,38 @@ +package bot + +import scala.concurrent.{Await, Future} +import slick.jdbc.H2Profile.api._ +import com.bot4s.telegram.models.User +import slick.jdbc.H2Profile + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.Duration + +class Users(tag: Tag) extends Table[(Int, String, String)](tag, "Messages"){ + def id = column[Int]("Id", O.PrimaryKey) + def name = column[String]("Name") + def username = column[String]("Username") + def * = (id, name, username) +} + +class DBUserHandler(users: TableQuery[Users], messages: TableQuery[Messages]) { + lazy val database = Database.forConfig("h2mem1") + + def init(): Future[Unit] = { + database.run(users.schema.createIfNotExists) + } + + def register(user: User): Future[Unit] = { + val req = for { + _ <- users += (user.id, user.firstName + " " + user.lastName.getOrElse(""), user.username.getOrElse("")) + } yield () + database.run(req) + } + + def show: Future[String] = { + val req = for { + allUsers <- users.result + } yield allUsers + database.run(req).flatMap(seq => Future(seq.map(it => s"${it._2}, id: ${it._1}").mkString("\n"))) + } +} From a3bfb020ee1b9f9a1db48d644df2bfc75089d7b3 Mon Sep 17 00:00:00 2001 From: handlessmidas Date: Fri, 22 May 2020 16:12:47 +0500 Subject: [PATCH 4/4] Fixes --- src/main/scala/bot/BotStarter.scala | 2 +- src/main/scala/bot/DBMessageHandler.scala | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/scala/bot/BotStarter.scala b/src/main/scala/bot/BotStarter.scala index d840dd4..ec545a0 100644 --- a/src/main/scala/bot/BotStarter.scala +++ b/src/main/scala/bot/BotStarter.scala @@ -55,7 +55,7 @@ class BotStarter(override val client: RequestHandler[Future], val service: Servi } onCommand("/send") { implicit msg => - var from_id : Int = 0 + var from_id: Int = 0 msg.from match { case None => from_id = 0 case Some(user) => from_id = user.id diff --git a/src/main/scala/bot/DBMessageHandler.scala b/src/main/scala/bot/DBMessageHandler.scala index 341126b..da850fb 100644 --- a/src/main/scala/bot/DBMessageHandler.scala +++ b/src/main/scala/bot/DBMessageHandler.scala @@ -16,15 +16,17 @@ class Messages(tag: Tag) extends Table[(Int, String, Int, Int)](tag, "Messages") class DBMessageHandler(users: TableQuery[Users], messages: TableQuery[Messages]) { lazy val database = Database.forConfig("h2mem1") + var messageId = 0 def init(): Future[Unit] = { database.run(messages.schema.createIfNotExists) } - def send(senderId: String, recieverId: String, message: String): Unit = { + def send(senderId: String, receiverId: String, message: String): Future[Unit] = { val req = for { - _ <- messages += (-1, message, senderId.toInt, recieverId.toInt) + _ <- messages += (messageId, message, senderId.toInt, receiverId.toInt) } yield() + messageId += 1 database.run(req) } @@ -38,6 +40,6 @@ class DBMessageHandler(users: TableQuery[Users], messages: TableQuery[Messages]) } def clear(id: String): Future[Unit] = { - database.run(messages.delete).flatMap(_ => Future()) + database.run(messages.filter(it => it.receiverId === id.toInt).delete).flatMap(_ => Future()) } }