Playing with certificates to run HTTPS server. Not working yet.
This commit is contained in:
3
src/main/resources/application.conf
Normal file
3
src/main/resources/application.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
akka {
|
||||
loglevel = "DEBUG"
|
||||
}
|
||||
15
src/main/scala/eu/xeppaka/bot1/BotUri.scala
Normal file
15
src/main/scala/eu/xeppaka/bot1/BotUri.scala
Normal file
@@ -0,0 +1,15 @@
|
||||
package eu.xeppaka.bot1
|
||||
|
||||
import akka.http.scaladsl.model.Uri
|
||||
|
||||
case class BotUri(botId: String) {
|
||||
private val baseUri = Uri(s"https://api.telegram.org/bot$botId")
|
||||
|
||||
val getMe: Uri = baseUri.withPath(baseUri.path / "getMe")
|
||||
|
||||
val setWebhook: Uri = baseUri.withPath(baseUri.path / "setWebhook")
|
||||
|
||||
val deleteWebhook: Uri = baseUri.withPath(baseUri.path / "deleteWebhook")
|
||||
|
||||
val getWebhookInfo: Uri = baseUri.withPath(baseUri.path / "getWebhookInfo")
|
||||
}
|
||||
@@ -1,39 +1,46 @@
|
||||
package eu.xeppaka.bot1
|
||||
|
||||
import java.io.InputStream
|
||||
import java.security.{KeyStore, SecureRandom}
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.marshalling.Marshal
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server.directives.LoggingMagnet
|
||||
import akka.http.scaladsl.server.{Route, RouteResult}
|
||||
import akka.http.scaladsl.unmarshalling.Unmarshal
|
||||
import akka.http.scaladsl.{ConnectionContext, Http, HttpExt, HttpsConnectionContext}
|
||||
import akka.stream.ActorMaterializer
|
||||
import com.typesafe.sslconfig.akka.AkkaSSLConfig
|
||||
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
|
||||
|
||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||
import scala.io.StdIn
|
||||
import TelegramEntities._
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import akka.http.scaladsl.model.{HttpRequest, StatusCodes}
|
||||
import akka.http.scaladsl.server.directives.LoggingMagnet
|
||||
import akka.http.scaladsl.unmarshalling.Unmarshal
|
||||
import spray.json._
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
|
||||
implicit val getMeFormat: RootJsonFormat[GetMe] = jsonFormat4(GetMe)
|
||||
implicit val responseFormat: RootJsonFormat[Response[GetMe]] = jsonFormat4(Response[GetMe])
|
||||
}
|
||||
class TelegramBotServer(botId: String, port: Int, httpsContext: Option[HttpsConnectionContext])(implicit val actorSystem: ActorSystem) {
|
||||
|
||||
class TelegramBotServer extends JsonSupport {
|
||||
import eu.xeppaka.bot1.TelegramEntities._
|
||||
|
||||
def run(): Unit = {
|
||||
implicit val actorSystem: ActorSystem = ActorSystem("telegram-bot")
|
||||
implicit val materializer: ActorMaterializer = ActorMaterializer()
|
||||
implicit val executionContext: ExecutionContextExecutor = actorSystem.dispatcher
|
||||
private val botUri = BotUri(botId)
|
||||
private implicit val materializer: ActorMaterializer = ActorMaterializer()
|
||||
private implicit val executionContext: ExecutionContextExecutor = actorSystem.dispatcher
|
||||
|
||||
val bindingFuture = Http().bindAndHandle(botRoutes(), "localhost", 8080)
|
||||
private val http: HttpExt = Http()
|
||||
private val hookId = UUID.randomUUID().toString
|
||||
private val webhookUri = Uri(s"https://xeppaka.eu:8443/$hookId")
|
||||
private val bindingFuture = http.bindAndHandle(botRoutes(hookId),
|
||||
"127.0.0.1",
|
||||
port,
|
||||
connectionContext = httpsContext.getOrElse(http.defaultClientHttpsContext))
|
||||
|
||||
StdIn.readLine()
|
||||
println(s"webhook path: $webhookUri")
|
||||
|
||||
def stop(): Unit = {
|
||||
bindingFuture
|
||||
.andThen { case _ => http.shutdownAllConnectionPools() }
|
||||
.flatMap(_.unbind())
|
||||
.onComplete(_ => actorSystem.terminate())
|
||||
}
|
||||
@@ -43,11 +50,11 @@ class TelegramBotServer extends JsonSupport {
|
||||
println(res)
|
||||
}
|
||||
|
||||
def botRoutes()(implicit actorSystem: ActorSystem, materializer: ActorMaterializer, executionContext: ExecutionContextExecutor): Route = {
|
||||
path("test") {
|
||||
get {
|
||||
def botRoutes(hookId: String): Route = {
|
||||
path(hookId) {
|
||||
post {
|
||||
logRequestResult(LoggingMagnet(_ => printRequestMethodAndResponseStatus)) {
|
||||
onComplete(getBotInfo()) {
|
||||
onComplete(getBotInfo) {
|
||||
case Success(res) => complete(res.ok.toString)
|
||||
case Failure(ex) => complete(StatusCodes.InternalServerError, "Boooom!")
|
||||
}
|
||||
@@ -56,17 +63,73 @@ class TelegramBotServer extends JsonSupport {
|
||||
}
|
||||
}
|
||||
|
||||
def getBotInfo()(implicit actorSystem: ActorSystem, materializer: ActorMaterializer, executionContext: ExecutionContextExecutor): Future[Response[GetMe]] = {
|
||||
Http().singleRequest(HttpRequest(uri = "https://api.telegram.org/bot570855144:AAEv7b817cuq2JJI9f2kG5B9G3zW1x-btz4/getMe")).flatMap(Unmarshal(_).to[Response[GetMe]])
|
||||
def getBotInfo: Future[Response[GetMe]] = {
|
||||
http.singleRequest(HttpRequest(uri = botUri.getMe)).flatMap(Unmarshal(_).to[Response[GetMe]])
|
||||
}
|
||||
|
||||
def setWebhook(): Unit = {
|
||||
def setWebhook(): Future[HttpResponse] = {
|
||||
val hook = Webhook(webhookUri.toString())
|
||||
Marshal(hook)
|
||||
.to[MessageEntity]
|
||||
.flatMap(entity => http.singleRequest(HttpRequest(uri = botUri.setWebhook, method = HttpMethods.POST, entity = entity)))
|
||||
// .flatMap(Unmarshal(_).to[Response[String]])
|
||||
}
|
||||
|
||||
def deleteWebhook(): Future[Response[String]] = {
|
||||
http
|
||||
.singleRequest(HttpRequest(uri = botUri.deleteWebhook, method = HttpMethods.POST))
|
||||
.flatMap(Unmarshal(_).to[Response[String]])
|
||||
}
|
||||
|
||||
def getWebhookInfo(): Future[Response[WebhookInfo]] = {
|
||||
http
|
||||
.singleRequest(HttpRequest(uri = botUri.getWebhookInfo, method = HttpMethods.GET))
|
||||
.flatMap(Unmarshal(_).to[Response[WebhookInfo]])
|
||||
}
|
||||
}
|
||||
|
||||
object TelegramBotServer {
|
||||
private val botId = "570855144:AAEv7b817cuq2JJI9f2kG5B9G3zW1x-btz4"
|
||||
|
||||
def apply(port: Int, httpsContext: Option[HttpsConnectionContext])(implicit actorSystem: ActorSystem): TelegramBotServer = new TelegramBotServer(botId, port, httpsContext)(actorSystem)
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
new TelegramBotServer().run()
|
||||
val httpsContext = createHttpsConnectionContext
|
||||
|
||||
implicit val actorSystem: ActorSystem = ActorSystem("telegram-bot")
|
||||
implicit val executionContext: ExecutionContextExecutor = actorSystem.dispatcher
|
||||
|
||||
val tbs = TelegramBotServer(8443, Some(createHttpsConnectionContext))
|
||||
//tbs.setWebhook()
|
||||
|
||||
tbs
|
||||
.getWebhookInfo()
|
||||
.onComplete(println(_))
|
||||
|
||||
StdIn.readLine()
|
||||
|
||||
tbs.deleteWebhook()
|
||||
.onComplete(r => tbs.stop())
|
||||
}
|
||||
|
||||
def createHttpsConnectionContext: HttpsConnectionContext = {
|
||||
val password: Array[Char] = "changeit".toCharArray // do not store passwords in code, read them from somewhere safe!
|
||||
|
||||
val ks: KeyStore = KeyStore.getInstance("PKCS12")
|
||||
val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12")
|
||||
|
||||
require(keystore != null, "Keystore required!")
|
||||
ks.load(keystore, password)
|
||||
|
||||
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
|
||||
keyManagerFactory.init(ks, password)
|
||||
|
||||
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
|
||||
tmf.init(ks)
|
||||
|
||||
val sslContext: SSLContext = SSLContext.getInstance("TLS")
|
||||
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
|
||||
|
||||
ConnectionContext.https(sslContext)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package eu.xeppaka.bot1
|
||||
|
||||
object TelegramEntities {
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import spray.json._
|
||||
|
||||
object TelegramEntities extends SprayJsonSupport with DefaultJsonProtocol {
|
||||
|
||||
case class Response[T](ok: Boolean,
|
||||
description: Option[String],
|
||||
error_code: Option[Int],
|
||||
description: Option[String] = None,
|
||||
error_code: Option[Int] = None,
|
||||
result: T)
|
||||
|
||||
case class GetMe(id: Int, is_bot: Boolean, first_name: String, username: String)
|
||||
@@ -19,29 +22,29 @@ object TelegramEntities {
|
||||
latitude: Float)
|
||||
|
||||
case class Update(update_id: Int,
|
||||
message: Option[Message],
|
||||
edited_message: Option[Message],
|
||||
channel_post: Option[Message],
|
||||
edited_channel_post: Option[Message],
|
||||
inline_query: Option[InlineQuery],
|
||||
chosen_inline_result: Option[ChosenInlineResult],
|
||||
callback_query: Option[CallbackQuery],
|
||||
shipping_query: Option[ShippingQuery],
|
||||
pre_checkout_query: Option[PreCheckoutQuery])
|
||||
message: Option[Message] = None,
|
||||
edited_message: Option[Message] = None,
|
||||
channel_post: Option[Message] = None,
|
||||
edited_channel_post: Option[Message] = None,
|
||||
inline_query: Option[InlineQuery] = None,
|
||||
chosen_inline_result: Option[ChosenInlineResult] = None,
|
||||
callback_query: Option[CallbackQuery] = None,
|
||||
shipping_query: Option[ShippingQuery] = None,
|
||||
pre_checkout_query: Option[PreCheckoutQuery] = None)
|
||||
|
||||
case class ChosenInlineResult(result_id: String,
|
||||
from: User,
|
||||
location: Option[Location],
|
||||
inline_message_id: Option[String],
|
||||
location: Option[Location] = None,
|
||||
inline_message_id: Option[String] = None,
|
||||
query: String)
|
||||
|
||||
case class CallbackQuery(id: String,
|
||||
from: User,
|
||||
message: Option[Message],
|
||||
inline_message_id: Option[String],
|
||||
message: Option[Message] = None,
|
||||
inline_message_id: Option[String] = None,
|
||||
chat_instance: String,
|
||||
data: Option[String],
|
||||
game_short_name: Option[String])
|
||||
data: Option[String] = None,
|
||||
game_short_name: Option[String] = None)
|
||||
|
||||
case class ShippingQuery(id: String,
|
||||
from: User,
|
||||
@@ -60,20 +63,20 @@ object TelegramEntities {
|
||||
currency: String,
|
||||
total_amount: Int,
|
||||
invoice_payload: String,
|
||||
shipping_option_id: Option[String],
|
||||
order_info: Option[OrderInfo])
|
||||
shipping_option_id: Option[String] = None,
|
||||
order_info: Option[OrderInfo] = None)
|
||||
|
||||
case class OrderInfo(name: Option[String],
|
||||
phone_number: Option[String],
|
||||
email: Option[String],
|
||||
shipping_address: Option[ShippingAddress])
|
||||
case class OrderInfo(name: Option[String] = None,
|
||||
phone_number: Option[String] = None,
|
||||
email: Option[String] = None,
|
||||
shipping_address: Option[ShippingAddress] = None)
|
||||
|
||||
case class User(id: Int,
|
||||
is_bot: Boolean,
|
||||
first_name: String,
|
||||
last_name: Option[String],
|
||||
username: Option[String],
|
||||
language_code: Option[String])
|
||||
last_name: Option[String] = None,
|
||||
username: Option[String] = None,
|
||||
language_code: Option[String] = None)
|
||||
|
||||
case class Message()
|
||||
|
||||
@@ -81,23 +84,39 @@ object TelegramEntities {
|
||||
|
||||
case class Chat(id: Int,
|
||||
`type`: String,
|
||||
title: Option[String],
|
||||
username: Option[String],
|
||||
first_name: Option[String],
|
||||
last_name: Option[String],
|
||||
all_members_are_administrators: Option[Boolean],
|
||||
photo: Option[ChatPhoto],
|
||||
description: Option[String],
|
||||
invite_link: Option[String],
|
||||
pinned_message: Option[Message],
|
||||
sticker_set_name: Option[String],
|
||||
can_set_sticker_set: Option[Boolean]
|
||||
)
|
||||
title: Option[String] = None,
|
||||
username: Option[String] = None,
|
||||
first_name: Option[String] = None,
|
||||
last_name: Option[String] = None,
|
||||
all_members_are_administrators: Option[Boolean] = None,
|
||||
photo: Option[ChatPhoto] = None,
|
||||
description: Option[String] = None,
|
||||
invite_link: Option[String] = None,
|
||||
pinned_message: Option[Message] = None,
|
||||
sticker_set_name: Option[String] = None,
|
||||
can_set_sticker_set: Option[Boolean] = None)
|
||||
|
||||
case class InputFile()
|
||||
|
||||
case class Webhook(url: String,
|
||||
certificate: Option[InputFile],
|
||||
max_connections: Option[Int],
|
||||
allowed_updates: Option[Seq[String]])
|
||||
certificate: Option[InputFile] = None,
|
||||
max_connections: Option[Int] = None,
|
||||
allowed_updates: Option[Seq[String]] = None)
|
||||
|
||||
case class WebhookInfo(url: String,
|
||||
has_custom_certificate: Boolean,
|
||||
pending_update_count: Int,
|
||||
last_error_date: Option[Int] = None,
|
||||
last_error_message: Option[String] = None,
|
||||
max_connections: Option[Int] = None,
|
||||
allowed_updates: Option[Seq[String]] = None)
|
||||
|
||||
implicit val inputFileFormat: RootJsonFormat[InputFile] = jsonFormat0(InputFile)
|
||||
implicit val webHookFormat: RootJsonFormat[Webhook] = jsonFormat4(Webhook)
|
||||
implicit val webHookInfoFormat: RootJsonFormat[WebhookInfo] = jsonFormat7(WebhookInfo)
|
||||
implicit val getMeFormat: RootJsonFormat[GetMe] = jsonFormat4(GetMe)
|
||||
// responses
|
||||
implicit val responseGetMeFormat: RootJsonFormat[Response[GetMe]] = jsonFormat4(Response[GetMe])
|
||||
implicit val responseWebhookInfoFormat: RootJsonFormat[Response[WebhookInfo]] = jsonFormat4(Response[WebhookInfo])
|
||||
implicit val responseStringFormat: RootJsonFormat[Response[String]] = jsonFormat4(Response[String])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user