mirror of
https://github.com/dyrkin/luxmed-bot.git
synced 2025-12-24 06:28:37 +01:00
Initial commit
This commit is contained in:
122
.gitignore
vendored
Normal file
122
.gitignore
vendored
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Java template
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
### macOS template
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
### Scala template
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-release/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
### Gradle template
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Ignore Gradle GUI config
|
||||||
|
gradle-app.setting
|
||||||
|
|
||||||
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Cache of project
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
|
/.idea/
|
||||||
|
/**/*.iml
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) ${year} ${name}
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
22
README.md
Normal file
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Luxmed Booking Service
|
||||||
|
|
||||||
|
Non official bot for **Portal Pacienta LUX MED**.
|
||||||
|
|
||||||
|
With its help user can book a visit to a doctor, create term monitoring, view upcoming visits and visit history.
|
||||||
|
|
||||||
|
It is available by [@luxmedbot](https://telegram.me/luxmedbot)
|
||||||
|
####To setup your own
|
||||||
|
|
||||||
|
1. create your own telegram bot using [@BotFather](https://telegram.me/botfather)
|
||||||
|
2. add to .bash_profile
|
||||||
|
|
||||||
|
```
|
||||||
|
export TELEGRAM_TOKEN="SOME TOKEN"
|
||||||
|
export SECURITY_SECRET="SOME SECRET FOR ENCODING USER PASSWORDS"
|
||||||
|
```
|
||||||
|
3. install postgres and create db **lbs** with login **lbs** and password **lsb123**
|
||||||
|
4. run using `./gradlew bootRun`
|
||||||
|
5. send `/start` to your bot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6
api/build.gradle
Normal file
6
api/build.gradle
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
dependencies {
|
||||||
|
compile project(':common')
|
||||||
|
|
||||||
|
compile group: "org.scalaj", name: "scalaj-http_2.12", version: "2.3.0"
|
||||||
|
compile group: "org.json4s", name: "json4s-jackson_2.12", version: "3.6.0-M3"
|
||||||
|
}
|
||||||
44
api/src/main/scala/com/lbs/api/ApiBase.scala
Normal file
44
api/src/main/scala/com/lbs/api/ApiBase.scala
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api
|
||||||
|
|
||||||
|
import com.lbs.api.http.headers._
|
||||||
|
import scalaj.http.{Http, HttpRequest}
|
||||||
|
|
||||||
|
trait ApiBase {
|
||||||
|
private val CommonHeaders =
|
||||||
|
Map(
|
||||||
|
Host -> "portalpacjenta.luxmed.pl",
|
||||||
|
Accept -> "*/*",
|
||||||
|
Connection -> "keep-alive",
|
||||||
|
`Accept-Encoding` -> "gzip;q=1.0, compress;q=0.5",
|
||||||
|
`User-Agent` -> "PatientPortal/3.3.0 (pl.luxmed.pp.LUX-MED; build:166; iOS 11.3.0) Alamofire/4.5.1",
|
||||||
|
`Accept-Language` -> "en-PL;q=1.0, ru-PL;q=0.9, pl-PL;q=0.8, uk-PL;q=0.7"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
protected def http(url: String): HttpRequest = {
|
||||||
|
Http(s"https://portalpacjenta.luxmed.pl/PatientPortalMobileAPI/api/$url").headers(CommonHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
167
api/src/main/scala/com/lbs/api/LuxmedApi.scala
Normal file
167
api/src/main/scala/com/lbs/api/LuxmedApi.scala
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.http._
|
||||||
|
import com.lbs.api.http.headers._
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import com.lbs.api.json.model._
|
||||||
|
import scalaj.http.{HttpRequest, HttpResponse}
|
||||||
|
import com.lbs.api.ApiResponseMutators._
|
||||||
|
|
||||||
|
object LuxmedApi extends ApiBase {
|
||||||
|
|
||||||
|
private val dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||||
|
|
||||||
|
def login(username: String, password: String, clientId: String = "iPhone"): Either[Throwable, LoginResponse] = {
|
||||||
|
val request = http("token").
|
||||||
|
header(`Content-Type`, "application/x-www-form-urlencoded").
|
||||||
|
header(`x-api-client-identifier`, clientId).
|
||||||
|
param("client_id", clientId).
|
||||||
|
param("grant_type", "password").
|
||||||
|
param("password", password).
|
||||||
|
param("username", username)
|
||||||
|
post[LoginResponse](request)
|
||||||
|
}
|
||||||
|
|
||||||
|
def refreshToken(refreshToken: String, clientId: String = "iPhone"): Either[Throwable, LoginResponse] = {
|
||||||
|
val request = http("token").
|
||||||
|
header(`Content-Type`, "application/x-www-form-urlencoded").
|
||||||
|
header(`x-api-client-identifier`, clientId).
|
||||||
|
param("client_id", clientId).
|
||||||
|
param("grant_type", "refresh_token").
|
||||||
|
param("refresh_token", refreshToken)
|
||||||
|
post[LoginResponse](request)
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservedVisits(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
toDate: ZonedDateTime = ZonedDateTime.now().plusMonths(3)): Either[Throwable, ReservedVisitsResponse] = {
|
||||||
|
val request = http("visits/reserved").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken").
|
||||||
|
param("fromDate", dateFormat.format(fromDate)).
|
||||||
|
param("toDate", dateFormat.format(toDate))
|
||||||
|
get[ReservedVisitsResponse](request).mutate
|
||||||
|
}
|
||||||
|
|
||||||
|
def visitsHistory(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1),
|
||||||
|
toDate: ZonedDateTime = ZonedDateTime.now(), page: Int = 1, pageSize: Int = 100): Either[Throwable, VisitsHistoryResponse] = {
|
||||||
|
val request = http("visits/history").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken").
|
||||||
|
param("fromDate", dateFormat.format(fromDate)).
|
||||||
|
param("toDate", dateFormat.format(toDate)).
|
||||||
|
param("page", page.toString).
|
||||||
|
param("pageSize", pageSize.toString)
|
||||||
|
get[VisitsHistoryResponse](request).mutate
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservationFilter(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
toDate: Option[ZonedDateTime] = None, cityId: Option[Long] = None, clinicId: Option[Long] = None,
|
||||||
|
serviceId: Option[Long] = None): Either[Throwable, ReservationFilterResponse] = {
|
||||||
|
val request = http("visits/available-terms/reservation-filter").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken").
|
||||||
|
param("cityId", cityId.map(_.toString)).
|
||||||
|
param("clinicId", clinicId.map(_.toString)).
|
||||||
|
param("fromDate", dateFormat.format(fromDate)).
|
||||||
|
param("toDate", toDate.map(dateFormat.format)).
|
||||||
|
param("serviceId", serviceId.map(_.toString))
|
||||||
|
get[ReservationFilterResponse](request).mutate
|
||||||
|
}
|
||||||
|
|
||||||
|
def availableTerms(accessToken: String, tokenType: String, payerId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long, doctorId: Option[Long],
|
||||||
|
fromDate: ZonedDateTime = ZonedDateTime.now(), toDate: Option[ZonedDateTime] = None, timeOfDay: Int = 0,
|
||||||
|
languageId: Long = 10, findFirstFreeTerm: Boolean = false): Either[Throwable, AvailableTermsResponse] = {
|
||||||
|
val request = http("visits/available-terms").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken").
|
||||||
|
param("cityId", cityId.toString).
|
||||||
|
param("doctorId", doctorId.map(_.toString)).
|
||||||
|
param("findFirstFreeTerm", findFirstFreeTerm.toString).
|
||||||
|
param("fromDate", dateFormat.format(fromDate)).
|
||||||
|
param("languageId", languageId.toString).
|
||||||
|
param("payerId", payerId.toString).
|
||||||
|
param("clinicId", clinicId.map(_.toString)).
|
||||||
|
param("serviceId", serviceId.toString).
|
||||||
|
param("timeOfDay", timeOfDay.toString).
|
||||||
|
param("toDate", dateFormat.format(toDate.getOrElse(fromDate.plusMonths(3))))
|
||||||
|
get[AvailableTermsResponse](request).mutate
|
||||||
|
}
|
||||||
|
|
||||||
|
def temporaryReservation(accessToken: String, tokenType: String, temporaryReservationRequest: TemporaryReservationRequest): Either[Throwable, TemporaryReservationResponse] = {
|
||||||
|
val request = http("visits/temporary-reservation").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken")
|
||||||
|
post[TemporaryReservationResponse](request, bodyOpt = Some(temporaryReservationRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteTemporaryReservation(accessToken: String, tokenType: String, temporaryReservationId: Long): Either[Throwable, HttpResponse[String]] = {
|
||||||
|
val request = http(s"visits/temporary-reservation/$temporaryReservationId").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken")
|
||||||
|
delete(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
def valuations(accessToken: String, tokenType: String, valuationsRequest: ValuationsRequest): Either[Throwable, ValuationsResponse] = {
|
||||||
|
val request = http("visits/available-terms/valuations").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken")
|
||||||
|
post[ValuationsResponse](request, bodyOpt = Some(valuationsRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservation(accessToken: String, tokenType: String, reservationRequest: ReservationRequest): Either[Throwable, ReservationResponse] = {
|
||||||
|
val request = http("visits/reserved").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken")
|
||||||
|
post[ReservationResponse](request, bodyOpt = Some(reservationRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteReservation(accessToken: String, tokenType: String, reservationId: Long): Either[Throwable, HttpResponse[String]] = {
|
||||||
|
val request = http(s"visits/reserved/$reservationId").
|
||||||
|
header(`Content-Type`, "application/json").
|
||||||
|
header(Authorization, s"$tokenType $accessToken")
|
||||||
|
delete(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def get[T <: SerializableJsonObject](request: HttpRequest)(implicit mf: scala.reflect.Manifest[T]): Either[Throwable, T] = {
|
||||||
|
request.toEither.map(_.body.as[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def post[T <: SerializableJsonObject](request: HttpRequest, bodyOpt: Option[SerializableJsonObject] = None)(implicit mf: scala.reflect.Manifest[T]): Either[Throwable, T] = {
|
||||||
|
val postRequest = bodyOpt match {
|
||||||
|
case Some(body) => request.postData(body.asJson)
|
||||||
|
case None => request.postForm
|
||||||
|
}
|
||||||
|
postRequest.toEither.map(_.body.as[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def delete(request: HttpRequest): Either[Throwable, HttpResponse[String]] = {
|
||||||
|
request.postForm.method("DELETE").toEither
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
95
api/src/main/scala/com/lbs/api/LuxmedApiAsync.scala
Normal file
95
api/src/main/scala/com/lbs/api/LuxmedApiAsync.scala
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import com.lbs.api.json.model._
|
||||||
|
import scalaj.http.HttpResponse
|
||||||
|
|
||||||
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
|
|
||||||
|
object LuxmedApiAsync {
|
||||||
|
|
||||||
|
private val syncApi = LuxmedApi
|
||||||
|
|
||||||
|
def login(username: String, password: String, clientId: String = "iPhone")(implicit ec: ExecutionContext): Future[LoginResponse] = {
|
||||||
|
async(syncApi.login(username, password, clientId))
|
||||||
|
}
|
||||||
|
|
||||||
|
def refreshToken(refreshToken: String, clientId: String = "iPhone")(implicit ec: ExecutionContext): Future[LoginResponse] = {
|
||||||
|
async(syncApi.refreshToken(refreshToken, clientId))
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservedVisits(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
toDate: ZonedDateTime = ZonedDateTime.now().plusMonths(3))(implicit ec: ExecutionContext): Future[ReservedVisitsResponse] = {
|
||||||
|
async(syncApi.reservedVisits(accessToken, tokenType, fromDate, toDate))
|
||||||
|
}
|
||||||
|
|
||||||
|
def visitsHistory(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1),
|
||||||
|
toDate: ZonedDateTime, page: Int = 1, pageSize: Int = 100)(implicit ec: ExecutionContext): Future[VisitsHistoryResponse] = {
|
||||||
|
async(syncApi.visitsHistory(accessToken, tokenType, fromDate, toDate, page, pageSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservationFilter(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
toDate: Option[ZonedDateTime] = None, cityId: Option[Long] = None,
|
||||||
|
serviceId: Option[Long] = None)(implicit ec: ExecutionContext): Future[ReservationFilterResponse] = {
|
||||||
|
async(syncApi.reservationFilter(accessToken, tokenType, fromDate, toDate, cityId, serviceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
def availableTerms(accessToken: String, tokenType: String, payerId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long, doctorId: Option[Long],
|
||||||
|
fromDate: ZonedDateTime = ZonedDateTime.now(), toDate: Option[ZonedDateTime] = None, timeOfDay: Int = 0,
|
||||||
|
languageId: Long = 10, findFirstFreeTerm: Boolean = true)(implicit ec: ExecutionContext): Future[AvailableTermsResponse] = {
|
||||||
|
async(syncApi.availableTerms(accessToken, tokenType, cityId, payerId, clinicId, serviceId, doctorId, fromDate, toDate, timeOfDay, languageId, findFirstFreeTerm))
|
||||||
|
}
|
||||||
|
|
||||||
|
def temporaryReservation(accessToken: String, tokenType: String, temporaryReservationRequest: TemporaryReservationRequest)(implicit ec: ExecutionContext): Future[TemporaryReservationResponse] = {
|
||||||
|
async(syncApi.temporaryReservation(accessToken, tokenType, temporaryReservationRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteTemporaryReservation(accessToken: String, tokenType: String, temporaryReservationId: Long)(implicit ec: ExecutionContext): Future[HttpResponse[String]] = {
|
||||||
|
async(syncApi.deleteTemporaryReservation(accessToken, tokenType, temporaryReservationId))
|
||||||
|
}
|
||||||
|
|
||||||
|
def valuations(accessToken: String, tokenType: String, valuationsRequest: ValuationsRequest)(implicit ec: ExecutionContext): Future[ValuationsResponse] = {
|
||||||
|
async(syncApi.valuations(accessToken, tokenType, valuationsRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def reservation(accessToken: String, tokenType: String, reservationRequest: ReservationRequest)(implicit ec: ExecutionContext): Future[ReservationResponse] = {
|
||||||
|
async(syncApi.reservation(accessToken, tokenType, reservationRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteReservation(accessToken: String, tokenType: String, reservationId: Long)(implicit ec: ExecutionContext): Future[HttpResponse[String]] = {
|
||||||
|
async(syncApi.deleteReservation(accessToken, tokenType, reservationId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def async[T](f: => Either[Throwable, T])(implicit ec: ExecutionContext) = {
|
||||||
|
Future(f).flatMap {
|
||||||
|
case Right(r) => Future.successful(r)
|
||||||
|
case Left(ex) => Future.failed(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.exception
|
||||||
|
|
||||||
|
case class LuxmedException(code: Int, status: String, message: String) extends Exception(message) {
|
||||||
|
override def toString: String = s"Code: $code, status: $status, message: $message"
|
||||||
|
}
|
||||||
101
api/src/main/scala/com/lbs/api/http/package.scala
Normal file
101
api/src/main/scala/com/lbs/api/http/package.scala
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api
|
||||||
|
|
||||||
|
import com.lbs.api.exception.LuxmedException
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import com.lbs.api.json.model.{LuxmedBaseError, LuxmedCompositeError, LuxmedError, SerializableJsonObject}
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import scalaj.http.{HttpRequest, HttpResponse}
|
||||||
|
|
||||||
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
package object http extends Logger {
|
||||||
|
|
||||||
|
object headers {
|
||||||
|
val `Content-Type` = "Content-Type"
|
||||||
|
val Host = "Host"
|
||||||
|
val Accept = "Accept"
|
||||||
|
val Connection = "Connection"
|
||||||
|
val `Accept-Encoding` = "Accept-Encoding"
|
||||||
|
val `User-Agent` = "User-Agent"
|
||||||
|
val `x-api-client-identifier` = "x-api-client-identifier"
|
||||||
|
val `Accept-Language` = "Accept-Language"
|
||||||
|
val Authorization = "Authorization"
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class HttpResponseWithJsonDeserializationSupport(httpResponse: HttpResponse[String]) {
|
||||||
|
|
||||||
|
def asEntity[T <: SerializableJsonObject](implicit mf: scala.reflect.Manifest[T]): HttpResponse[T] = {
|
||||||
|
httpResponse.copy(body = httpResponse.body.as[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
def asEntityAsync[T <: SerializableJsonObject](implicit mf: scala.reflect.Manifest[T], ec: ExecutionContext): Future[HttpResponse[T]] = {
|
||||||
|
Future(asEntity[T])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class ExtendedHttpRequest(httpRequest: HttpRequest) {
|
||||||
|
|
||||||
|
def toEither: Either[Throwable, HttpResponse[String]] = {
|
||||||
|
toTry.toEither
|
||||||
|
}
|
||||||
|
|
||||||
|
def toTry: Try[HttpResponse[String]] = {
|
||||||
|
LOG.debug(s"Sending request:\n${hidePasswords(httpRequest)}")
|
||||||
|
val httpResponse = Try(httpRequest.asString)
|
||||||
|
LOG.debug(s"Received response:\n$httpResponse")
|
||||||
|
extractLuxmedError(httpResponse) match {
|
||||||
|
case Some(error) => Try(throw error)
|
||||||
|
case None => httpResponse.map(_.throwError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def param(key: String, value: Option[String]): HttpRequest = {
|
||||||
|
value.map(v => httpRequest.param(key, v)).getOrElse(httpRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def luxmedErrorToException[T <: LuxmedBaseError](ler: HttpResponse[T]) = {
|
||||||
|
ler.body match {
|
||||||
|
case e: LuxmedCompositeError =>
|
||||||
|
LuxmedException(ler.code, ler.statusLine, e.errors.map(_.message).mkString("; "))
|
||||||
|
case e: LuxmedError =>
|
||||||
|
LuxmedException(ler.code, ler.statusLine, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def extractLuxmedError(httpResponse: Try[HttpResponse[String]]) = {
|
||||||
|
httpResponse.flatMap(response => Try(response.asEntity[LuxmedCompositeError]).map(luxmedErrorToException).
|
||||||
|
orElse(Try(response.asEntity[LuxmedError]).map(luxmedErrorToException))).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
private def hidePasswords(httpRequest: HttpRequest) = {
|
||||||
|
httpRequest.copy(params = httpRequest.params.map { case (k, v) =>
|
||||||
|
if (k.toLowerCase.contains("passw")) k -> "******" else k -> v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
69
api/src/main/scala/com/lbs/api/json/JsonSerializer.scala
Normal file
69
api/src/main/scala/com/lbs/api/json/JsonSerializer.scala
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.json.model.SerializableJsonObject
|
||||||
|
import org.json4s._
|
||||||
|
import org.json4s.jackson.JsonMethods._
|
||||||
|
|
||||||
|
|
||||||
|
object JsonSerializer {
|
||||||
|
|
||||||
|
private val localDateTimeSerializer = new CustomSerializer[ZonedDateTime](_ => ( {
|
||||||
|
case JString(str) => ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||||
|
}, {
|
||||||
|
case zonedDateTime: ZonedDateTime => JString(zonedDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
private implicit val formats: Formats = DefaultFormats.withStrictArrayExtraction + localDateTimeSerializer
|
||||||
|
|
||||||
|
def extract[T <: SerializableJsonObject](jsonString: String)(implicit mf: scala.reflect.Manifest[T]): T = {
|
||||||
|
parse(jsonString).camelizeKeys.extract[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
def write[T <: SerializableJsonObject](jsonObject: T): String = {
|
||||||
|
pretty(render(Extraction.decompose(jsonObject).pascalizeKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
object extensions {
|
||||||
|
|
||||||
|
implicit class JsonStringToObject(jsonString: String) {
|
||||||
|
def as[T <: SerializableJsonObject](implicit mf: scala.reflect.Manifest[T]): T = {
|
||||||
|
extract[T](jsonString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class JsonObjectToString[T <: SerializableJsonObject](jsonObject: T) {
|
||||||
|
def asJson: String = {
|
||||||
|
write(jsonObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
{
|
||||||
|
"AvailableVisitsTermPresentation": [
|
||||||
|
{
|
||||||
|
"Clinic": {
|
||||||
|
"Id": 6,
|
||||||
|
"Name": "LX Wrocław - Szewska 3A"
|
||||||
|
},
|
||||||
|
"Doctor": {
|
||||||
|
"Id": 38275,
|
||||||
|
"Name": "lek. med. ANNA ABRAMCZYK"
|
||||||
|
},
|
||||||
|
"Impediment": {
|
||||||
|
"ImpedimentText": "",
|
||||||
|
"IsImpediment": false
|
||||||
|
},
|
||||||
|
"IsFree": false,
|
||||||
|
"PayerDetailsList": [
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 3333333,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 0,
|
||||||
|
"ServaId": 6666
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 8547135,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 1,
|
||||||
|
"ServaId": 6666
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReferralRequiredByProduct": false,
|
||||||
|
"ReferralRequiredByService": false,
|
||||||
|
"RoomId": 543,
|
||||||
|
"ScheduleId": 3331908,
|
||||||
|
"ServiceId": 6666,
|
||||||
|
"VisitDate": {
|
||||||
|
"FormattedDate": "26th April, Thu. at 12:40 pm",
|
||||||
|
"StartDateTime": "2018-02-23T11:30:00+02:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
case class AvailableTermsResponse(availableVisitsTermPresentation: List[AvailableVisitsTermPresentation]) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class AvailableVisitsTermPresentation(clinic: IdName, doctor: IdName, payerDetailsList: List[PayerDetails],
|
||||||
|
referralRequiredByProduct: Boolean, referralRequiredByService: Boolean,
|
||||||
|
roomId: Long, scheduleId: Long, serviceId: Long, visitDate: VisitDate) extends SerializableJsonObject
|
||||||
28
api/src/main/scala/com/lbs/api/json/model/IdName.scala
Normal file
28
api/src/main/scala/com/lbs/api/json/model/IdName.scala
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
case class IdName(id: Long, name: String) {
|
||||||
|
def optionalId: Option[Long] = Option(id).filterNot(_ == -1L)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* "access_token": "IDtjG_ECOd_ETYE2fwrCoTcC6bW935cn_nUh6d3BaEa-jvPlHfPLOY5AkF",
|
||||||
|
* "expires_in": 599,
|
||||||
|
* "refresh_token": "d251c66c-49e0-4777-b766-08326d83fa31",
|
||||||
|
* "token_type": "bearer"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
case class LoginResponse(accessToken: String, expiresIn: Int, refreshToken: String, tokenType: String) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
trait LuxmedBaseError
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
case class LuxmedCompositeError(errors: List[LuxmedCompositeMessage]) extends SerializableJsonObject with LuxmedBaseError
|
||||||
|
|
||||||
|
case class LuxmedCompositeMessage(errorCode: Int, message: String) extends SerializableJsonObject
|
||||||
26
api/src/main/scala/com/lbs/api/json/model/LuxmedError.scala
Normal file
26
api/src/main/scala/com/lbs/api/json/model/LuxmedError.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
case class LuxmedError(message: String) extends SerializableJsonObject with LuxmedBaseError
|
||||||
27
api/src/main/scala/com/lbs/api/json/model/PayerDetails.scala
Normal file
27
api/src/main/scala/com/lbs/api/json/model/PayerDetails.scala
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
case class PayerDetails(brandId: Option[Long], contractId: Long, payerId: Long, payerName: String, productElementId: Long,
|
||||||
|
productId: Long, productInContractId: Long, servaAppId: Long, servaId: Long) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"Cities": [
|
||||||
|
{
|
||||||
|
"Id": 5,
|
||||||
|
"Name": "Wrocław"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Clinics": [
|
||||||
|
{
|
||||||
|
"Id": 1405,
|
||||||
|
"Name": "Konsylium Wrocław - Legnicka 40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 7,
|
||||||
|
"Name": "LX Wrocław - Kwidzyńska 6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DefaultPayer": {
|
||||||
|
"Id": 22222,
|
||||||
|
"Name": "FIRMA POLAND SP. Z O.O."
|
||||||
|
},
|
||||||
|
"Doctors": [
|
||||||
|
{
|
||||||
|
"Id": 38275,
|
||||||
|
"Name": "ANNA ABRAMCZYK lek. med."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 15565,
|
||||||
|
"Name": "ANDRZEJ ANDEWSKI dr n. med."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Languages": [
|
||||||
|
{
|
||||||
|
"Id": 11,
|
||||||
|
"Name": "english"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 10,
|
||||||
|
"Name": "polish"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Payers": [
|
||||||
|
{
|
||||||
|
"Id": 22222,
|
||||||
|
"Name": "FIRMA POLAND SP. Z O.O."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Services": [
|
||||||
|
{
|
||||||
|
"Id": 5857,
|
||||||
|
"Name": "Audiometr standardowy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 7976,
|
||||||
|
"Name": "Audiometr standardowy - audiometria nadprogowa"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class ReservationFilterResponse(cities: List[IdName], clinics: List[IdName], defaultPayer: Option[IdName],
|
||||||
|
doctors: List[IdName], languages: List[IdName], payers: List[IdName],
|
||||||
|
services: List[IdName]) extends SerializableJsonObject
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"ClinicId": 6,
|
||||||
|
"DoctorId": 38509,
|
||||||
|
"PayerData": {
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 8547100,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 0,
|
||||||
|
"ServaId": 6621
|
||||||
|
},
|
||||||
|
"RoomId": 159,
|
||||||
|
"ServiceId": 6621,
|
||||||
|
"StartDateTime": "2018-06-04T11:00:00+02:00",
|
||||||
|
"TemporaryReservationId": 250303839
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class ReservationRequest(clinicId: Long, doctorId: Long, payerData: PayerDetails, roomId: Long, serviceId: Long,
|
||||||
|
startDateTime: ZonedDateTime, temporaryReservationId: Long) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"PreparationInfo": {
|
||||||
|
"IsPreparationRequired": true
|
||||||
|
},
|
||||||
|
"ReservedVisitsLimitInfo": {
|
||||||
|
"CanReserve": true,
|
||||||
|
"HasPatientLimit": false,
|
||||||
|
"MaxReservedVisitsCount": null,
|
||||||
|
"Message": "",
|
||||||
|
"ReservedVisitsCount": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class ReservationResponse(preparationInfo: PreparationInfo, reservedVisitsLimitInfo: ReservedVisitsLimitInfo) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class PreparationInfo(isPreparationRequired: Boolean) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class ReservedVisitsLimitInfo(canReserve: Boolean, hasPatientLimit: Boolean, maxReservedVisitsCount: Option[Int],
|
||||||
|
message: String, reservedVisitsCount: Option[Int]) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "ReservedVisits": [
|
||||||
|
*{
|
||||||
|
*"CanBeCanceled": true,
|
||||||
|
*"Clinic": {
|
||||||
|
*"Id": 6,
|
||||||
|
*"Name": "LX Wrocław - Szewska 3A"
|
||||||
|
*},
|
||||||
|
*"DoctorName": "lek. stom. TARAS SHEVCZENKO",
|
||||||
|
*"Impediment": {
|
||||||
|
*"ImpedimentText": "",
|
||||||
|
*"IsImpediment": false
|
||||||
|
*},
|
||||||
|
*"IsAdditional": false,
|
||||||
|
*"IsPreparationRequired": false,
|
||||||
|
*"Links": [
|
||||||
|
*{
|
||||||
|
*"Href": "/PatientPortalMobileAPI/api/visits/preparations/6621",
|
||||||
|
*"Method": "GET",
|
||||||
|
*"Rel": "get_preparations"
|
||||||
|
*}
|
||||||
|
*],
|
||||||
|
*"ReservationId": 888888888,
|
||||||
|
*"Service": {
|
||||||
|
*"Id": 6621,
|
||||||
|
*"Name": "Umówienie wizyty u stomatologa"
|
||||||
|
*},
|
||||||
|
*"VisitDate": {
|
||||||
|
*"FormattedDate": "21rd May, Mon. at 3:00 pm",
|
||||||
|
*"StartDateTime": "2018-05-21T15:00:00+02:00"
|
||||||
|
*}
|
||||||
|
*}
|
||||||
|
*]
|
||||||
|
*}
|
||||||
|
*/
|
||||||
|
case class ReservedVisitsResponse(reservedVisits: List[ReservedVisit]) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class ReservedVisit(canBeCanceled: Boolean, clinic: IdName, doctorName: String,
|
||||||
|
reservationId: Long, service: IdName, visitDate: VisitDate) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
trait SerializableJsonObject
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"ClinicId": 6,
|
||||||
|
"DoctorId": 38275,
|
||||||
|
"PayerDetailsList": [
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 3333333,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 0,
|
||||||
|
"ServaId": 6666
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 8547135,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 1,
|
||||||
|
"ServaId": 6666
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReferralRequiredByService": false,
|
||||||
|
"RoomId": 543,
|
||||||
|
"ServiceId": 6666,
|
||||||
|
"StartDateTime": "2018-02-23T11:30:00+02:00"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class TemporaryReservationRequest(clinicId: Long, doctorId: Long, payerDetailsList: List[PayerDetails],
|
||||||
|
referralRequiredByService: Boolean, roomId: Long, serviceId: Long,
|
||||||
|
startDateTime: ZonedDateTime) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
case class TemporaryReservationResponse(hasReferralRequired: Boolean, id: Long,
|
||||||
|
informationMessages: List[String]) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"ClinicId": 6,
|
||||||
|
"DoctorId": 38275,
|
||||||
|
"PayerDetailsList": [
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 3333333,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 0,
|
||||||
|
"ServaId": 6666
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BrandId": 2,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 8547135,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 1,
|
||||||
|
"ServaId": 6666
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReferralRequiredByService": false,
|
||||||
|
"RoomId": 543,
|
||||||
|
"ServiceId": 6666,
|
||||||
|
"StartDateTime": "2018-02-23T11:30:00+02:00"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class ValuationsRequest(clinicId: Long, doctorId: Long, payerDetailsList: List[PayerDetails],
|
||||||
|
referralRequiredByService: Boolean, roomId: Long, serviceId: Long,
|
||||||
|
startDateTime: ZonedDateTime) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"OptionsQuestion": "Would you like to confirm your appointment booking?",
|
||||||
|
"VisitTermVariants": [
|
||||||
|
{
|
||||||
|
"CanBeReserve": true,
|
||||||
|
"InfoMessage": "During the appointment, the physician will indicate the services to be provided and will inform you of the relevant fee, if any. The services will be provided in accordance with the scope of the agreement.",
|
||||||
|
"IsStomatology": true,
|
||||||
|
"OptionMessage": "I do not have the required referral",
|
||||||
|
"PaymentMessage": "",
|
||||||
|
"ReferralRequired": false,
|
||||||
|
"ValuationDetail": {
|
||||||
|
"PayerData": {
|
||||||
|
"BrandId": null,
|
||||||
|
"ContractId": 1111111,
|
||||||
|
"PayerId": 22222,
|
||||||
|
"PayerName": "FIRMA POLAND SP. Z O.O.",
|
||||||
|
"ProductElementId": 8547100,
|
||||||
|
"ProductId": 44444,
|
||||||
|
"ProductInContractId": 555555,
|
||||||
|
"ServaAppId": 0,
|
||||||
|
"ServaId": 6621
|
||||||
|
},
|
||||||
|
"Price": 0.0,
|
||||||
|
"ValuationType": 1
|
||||||
|
},
|
||||||
|
"WarningMessage": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class ValuationsResponse(optionsQuestion: Option[String], visitTermVariants: List[VisitTermVariant]) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class VisitTermVariant(canBeReserve: Boolean, infoMessage: String, isStomatology: Boolean, optionMessage: String, paymentMessage: String,
|
||||||
|
referralRequired: Boolean, valuationDetail: ValuationDetail, warningMessage: String) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class ValuationDetail(payerData: PayerDetails, price: BigDecimal, valuationType: Int) extends SerializableJsonObject
|
||||||
28
api/src/main/scala/com/lbs/api/json/model/VisitDate.scala
Normal file
28
api/src/main/scala/com/lbs/api/json/model/VisitDate.scala
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
case class VisitDate(formattedDate: String, startDateTime: ZonedDateTime) extends SerializableJsonObject
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"AreMoreVisits": false,
|
||||||
|
"HistoricVisits": [
|
||||||
|
{
|
||||||
|
"ClinicName": "LX Wrocław - Szewska 3A",
|
||||||
|
"DoctorName": "lek. stom. TARAS SHEVCZENKO",
|
||||||
|
"HasRecommendations": false,
|
||||||
|
"HasReferrals": false,
|
||||||
|
"IsAdditional": false,
|
||||||
|
"Links": [
|
||||||
|
{
|
||||||
|
"Href": "/PatientPortalMobileAPI/api/visits/recommendations/222222222",
|
||||||
|
"Method": "GET",
|
||||||
|
"Rel": "get_recommendations"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"QuestionToVisit": {
|
||||||
|
"IsAnswered": false,
|
||||||
|
"IsAsked": false,
|
||||||
|
"IsQuestionToVisitAvailable": false
|
||||||
|
},
|
||||||
|
"RateVisit": {
|
||||||
|
"IsRatingAvailable": false,
|
||||||
|
"IsVisitRated": false
|
||||||
|
},
|
||||||
|
"ReservationId": 222222222,
|
||||||
|
"Service": {
|
||||||
|
"Id": 6621,
|
||||||
|
"Name": "Umówienie wizyty u stomatologa"
|
||||||
|
},
|
||||||
|
"VisitDate": {
|
||||||
|
"FormattedDate": "17th Jan 2018, at 1:00 pm",
|
||||||
|
"StartDateTime": "2018-01-17T13:00:00+02:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ClinicName": "LX Wrocław - Szewska 3A",
|
||||||
|
"DoctorName": "lek. stom. TARAS SHEVCZENKO",
|
||||||
|
"HasRecommendations": false,
|
||||||
|
"HasReferrals": false,
|
||||||
|
"IsAdditional": false,
|
||||||
|
"Links": [
|
||||||
|
{
|
||||||
|
"Href": "/PatientPortalMobileAPI/api/visits/recommendations/999999999",
|
||||||
|
"Method": "GET",
|
||||||
|
"Rel": "get_recommendations"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"QuestionToVisit": {
|
||||||
|
"IsAnswered": false,
|
||||||
|
"IsAsked": false,
|
||||||
|
"IsQuestionToVisitAvailable": false
|
||||||
|
},
|
||||||
|
"RateVisit": {
|
||||||
|
"IsRatingAvailable": false,
|
||||||
|
"IsVisitRated": false
|
||||||
|
},
|
||||||
|
"ReservationId": 999999999,
|
||||||
|
"Service": {
|
||||||
|
"Id": 3589,
|
||||||
|
"Name": "Wypełnienie ubytku korony zęba na 2 powierzchniach"
|
||||||
|
},
|
||||||
|
"VisitDate": {
|
||||||
|
"FormattedDate": "17th Jan 2018, at 1:00 pm",
|
||||||
|
"StartDateTime": "2018-01-17T13:00:00+02:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case class VisitsHistoryResponse(areMoreVisits: Boolean, historicVisits: List[HistoricVisit]) extends SerializableJsonObject
|
||||||
|
|
||||||
|
case class HistoricVisit(clinicName: String, doctorName: String, reservationId: Long, service: IdName, visitDate: VisitDate)
|
||||||
66
api/src/main/scala/com/lbs/api/package.scala
Normal file
66
api/src/main/scala/com/lbs/api/package.scala
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs
|
||||||
|
|
||||||
|
import com.lbs.api.json.model.{AvailableTermsResponse, ReservationFilterResponse, ReservedVisitsResponse, VisitsHistoryResponse}
|
||||||
|
|
||||||
|
import scala.util.matching.Regex
|
||||||
|
|
||||||
|
package object api {
|
||||||
|
|
||||||
|
object ApiResponseMutators {
|
||||||
|
private val DoctorPrefixes: Regex = """\s*(dr\s*n.\s*med.|dr\s*hab.\s*n.\s*med|lek.\s*med.|lek.\s*stom.)\s*""".r
|
||||||
|
|
||||||
|
private def cleanupDoctorName(name: String) = DoctorPrefixes.replaceFirstIn(name, "")
|
||||||
|
|
||||||
|
trait ResponseMutator[T] {
|
||||||
|
def mutate(response: T): T
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class ResponseOps[T: ResponseMutator](response: Either[Throwable, T]) {
|
||||||
|
def mutate: Either[Throwable, T] = {
|
||||||
|
val mutator = implicitly[ResponseMutator[T]]
|
||||||
|
response.map(mutator.mutate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val ReservedVisitsResponseMutator: ResponseMutator[ReservedVisitsResponse] = (response: ReservedVisitsResponse) => {
|
||||||
|
response.copy(reservedVisits = response.reservedVisits.map(rv => rv.copy(doctorName = cleanupDoctorName(rv.doctorName))))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val VisitsHistoryResponseMutator: ResponseMutator[VisitsHistoryResponse] = (response: VisitsHistoryResponse) => {
|
||||||
|
response.copy(historicVisits = response.historicVisits.map(hv => hv.copy(doctorName = cleanupDoctorName(hv.doctorName))))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val ReservationFilterResponseMutator: ResponseMutator[ReservationFilterResponse] = (response: ReservationFilterResponse) => {
|
||||||
|
response.copy(doctors = response.doctors.map(d => d.copy(name = cleanupDoctorName(d.name))))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val AvailableTermsResponseMutator: ResponseMutator[AvailableTermsResponse] = (response: AvailableTermsResponse) => {
|
||||||
|
response.copy(availableVisitsTermPresentation =
|
||||||
|
response.availableVisitsTermPresentation.map(atp => atp.copy(doctor = atp.doctor.copy(name = cleanupDoctorName(atp.doctor.name)))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
api/src/test/scala/com/lbs/api/json/model/CommonSpec.scala
Normal file
43
api/src/test/scala/com/lbs/api/json/model/CommonSpec.scala
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import org.scalatest.Matchers
|
||||||
|
|
||||||
|
trait CommonSpec {
|
||||||
|
_: Matchers =>
|
||||||
|
|
||||||
|
private type SimpleEntity = {val id: Long; val name: String}
|
||||||
|
|
||||||
|
protected def testSimpleEntity(simpleEntity: SimpleEntity, expectedId: Long, expectedName: String): Unit = {
|
||||||
|
simpleEntity.id should be(expectedId)
|
||||||
|
simpleEntity.name should be(expectedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def testSimpleEntities(simpleEntities: List[SimpleEntity], expectedSize: Int, expectedId: Long, expectedName: String): Unit = {
|
||||||
|
simpleEntities.size should be(expectedSize)
|
||||||
|
val simpleEntity = simpleEntities.head
|
||||||
|
testSimpleEntity(simpleEntity, expectedId, expectedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class LoginResponseSpec extends FunSuiteLike with Matchers {
|
||||||
|
test("deserialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "access_token": "RmC6qccJMJ1uVhqJZ-6sBYdfT_LznEoGuH2di0",
|
||||||
|
| "expires_in": 599,
|
||||||
|
| "refresh_token": "7854cd0b-8545-483e-88d7-d07eda90995d",
|
||||||
|
| "token_type": "bearer"
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val response = json.as[LoginResponse]
|
||||||
|
|
||||||
|
response.accessToken should be("RmC6qccJMJ1uVhqJZ-6sBYdfT_LznEoGuH2di0")
|
||||||
|
response.expiresIn should be(599)
|
||||||
|
response.refreshToken should be("7854cd0b-8545-483e-88d7-d07eda90995d")
|
||||||
|
response.tokenType should be("bearer")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class ReservationFilterResponseSpec extends FunSuiteLike with Matchers with CommonSpec {
|
||||||
|
test("deserialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "Cities": [
|
||||||
|
| {
|
||||||
|
| "Id": 5,
|
||||||
|
| "Name": "Wrocław"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "Clinics": [
|
||||||
|
| {
|
||||||
|
| "Id": 1405,
|
||||||
|
| "Name": "Legnicka 40"
|
||||||
|
| },
|
||||||
|
| {
|
||||||
|
| "Id": 7,
|
||||||
|
| "Name": "Kwidzyńska 6"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "DefaultPayer": {
|
||||||
|
| "Id": 22222,
|
||||||
|
| "Name": "FIRMA"
|
||||||
|
| },
|
||||||
|
| "Doctors": [
|
||||||
|
| {
|
||||||
|
| "Id": 38275,
|
||||||
|
| "Name": "ANNA ABRAMCZYK"
|
||||||
|
| },
|
||||||
|
| {
|
||||||
|
| "Id": 15565,
|
||||||
|
| "Name": "ANDRZEJ ANDEWSKI"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "Languages": [
|
||||||
|
| {
|
||||||
|
| "Id": 11,
|
||||||
|
| "Name": "english"
|
||||||
|
| },
|
||||||
|
| {
|
||||||
|
| "Id": 10,
|
||||||
|
| "Name": "polish"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "Payers": [
|
||||||
|
| {
|
||||||
|
| "Id": 22222,
|
||||||
|
| "Name": "FIRMA"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "Services": [
|
||||||
|
| {
|
||||||
|
| "Id": 5857,
|
||||||
|
| "Name": "Audiometr standardowy"
|
||||||
|
| },
|
||||||
|
| {
|
||||||
|
| "Id": 7976,
|
||||||
|
| "Name": "Audiometr standardowy - audiometria nadprogowa"
|
||||||
|
| }
|
||||||
|
| ]
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val response = json.as[ReservationFilterResponse]
|
||||||
|
|
||||||
|
testSimpleEntities(response.cities, 1, 5L, "Wrocław")
|
||||||
|
testSimpleEntities(response.clinics, 2, 1405L, "Legnicka 40")
|
||||||
|
response.defaultPayer should be (_: Some[IdName])
|
||||||
|
testSimpleEntity(response.defaultPayer.get, 22222L, "FIRMA")
|
||||||
|
testSimpleEntities(response.doctors, 2, 38275L, "ANNA ABRAMCZYK")
|
||||||
|
testSimpleEntities(response.languages, 2, 11L, "english")
|
||||||
|
testSimpleEntities(response.payers, 1, 22222L, "FIRMA")
|
||||||
|
testSimpleEntities(response.services, 2, 5857L, "Audiometr standardowy")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class ReservedVisitsResponseSpec extends FunSuiteLike with Matchers with CommonSpec {
|
||||||
|
test("deserialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "ReservedVisits": [
|
||||||
|
| {
|
||||||
|
| "CanBeCanceled": true,
|
||||||
|
| "Clinic": {
|
||||||
|
| "Id": 6,
|
||||||
|
| "Name": "Szewska 3A"
|
||||||
|
| },
|
||||||
|
| "DoctorName": "TARAS SHEVCZENKO",
|
||||||
|
| "Impediment": {
|
||||||
|
| "ImpedimentText": "",
|
||||||
|
| "IsImpediment": false
|
||||||
|
| },
|
||||||
|
| "IsAdditional": false,
|
||||||
|
| "IsPreparationRequired": false,
|
||||||
|
| "Links": [
|
||||||
|
| {
|
||||||
|
| "Href": "/PatientPortalMobileAPI/api/visits/preparations/6621",
|
||||||
|
| "Method": "GET",
|
||||||
|
| "Rel": "get_preparations"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "ReservationId": 888888888,
|
||||||
|
| "Service": {
|
||||||
|
| "Id": 6621,
|
||||||
|
| "Name": "stomatolog"
|
||||||
|
| },
|
||||||
|
| "VisitDate": {
|
||||||
|
| "FormattedDate": "21rd May, Mon. at 3:00 pm",
|
||||||
|
| "StartDateTime": "2018-05-21T15:00:00+02:00"
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
| ]
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val response = json.as[ReservedVisitsResponse]
|
||||||
|
|
||||||
|
response.reservedVisits.size should be(1)
|
||||||
|
val reservedVisit = response.reservedVisits.head
|
||||||
|
reservedVisit.canBeCanceled should be(true)
|
||||||
|
testSimpleEntity(reservedVisit.clinic, 6L, "Szewska 3A")
|
||||||
|
reservedVisit.doctorName should be("TARAS SHEVCZENKO")
|
||||||
|
reservedVisit.reservationId should be(888888888L)
|
||||||
|
testSimpleEntity(reservedVisit.service, 6621L, "stomatolog")
|
||||||
|
reservedVisit.visitDate.formattedDate should be("21rd May, Mon. at 3:00 pm")
|
||||||
|
reservedVisit.visitDate.startDateTime should be(ZonedDateTime.parse("2018-05-21T15:00:00+02:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class TemporaryReservationRequestSpec extends FunSuiteLike with Matchers with CommonSpec {
|
||||||
|
test("serialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "ClinicId": 6,
|
||||||
|
| "DoctorId": 38275,
|
||||||
|
| "PayerDetailsList": [
|
||||||
|
| {
|
||||||
|
| "BrandId": 2,
|
||||||
|
| "ContractId": 1111111,
|
||||||
|
| "PayerId": 22222,
|
||||||
|
| "PayerName": "FIRMA",
|
||||||
|
| "ProductElementId": 3333333,
|
||||||
|
| "ProductId": 44444,
|
||||||
|
| "ProductInContractId": 555555,
|
||||||
|
| "ServaAppId": 0,
|
||||||
|
| "ServaId": 6666
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "ReferralRequiredByService": false,
|
||||||
|
| "RoomId": 543,
|
||||||
|
| "ServiceId": 6666,
|
||||||
|
| "StartDateTime": "2018-02-23T11:30:00+02:00"
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val request = TemporaryReservationRequest(clinicId = 6L, doctorId = 38275L, payerDetailsList = List(
|
||||||
|
PayerDetails(brandId = Some(2L), contractId = 1111111L, payerId = 22222L, payerName = "FIRMA",
|
||||||
|
productElementId = 3333333L, productId = 44444L, productInContractId = 555555L, servaAppId = 0L, servaId = 6666L)
|
||||||
|
), referralRequiredByService = false, roomId = 543L, serviceId = 6666L,
|
||||||
|
startDateTime = ZonedDateTime.parse("2018-02-23T11:30:00+02:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME))
|
||||||
|
|
||||||
|
val requestJson = request.asJson
|
||||||
|
val requestActual = requestJson.as[TemporaryReservationRequest]
|
||||||
|
val requestExpected = json.as[TemporaryReservationRequest]
|
||||||
|
|
||||||
|
requestActual should be (requestExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class ValuationsRequestSpec extends FunSuiteLike with Matchers with CommonSpec {
|
||||||
|
test("serialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "ClinicId": 6,
|
||||||
|
| "DoctorId": 38275,
|
||||||
|
| "PayerDetailsList": [
|
||||||
|
| {
|
||||||
|
| "BrandId": 2,
|
||||||
|
| "ContractId": 1111111,
|
||||||
|
| "PayerId": 22222,
|
||||||
|
| "PayerName": "FIRMA",
|
||||||
|
| "ProductElementId": 3333333,
|
||||||
|
| "ProductId": 44444,
|
||||||
|
| "ProductInContractId": 555555,
|
||||||
|
| "ServaAppId": 0,
|
||||||
|
| "ServaId": 6666
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "ReferralRequiredByService": false,
|
||||||
|
| "RoomId": 543,
|
||||||
|
| "ServiceId": 6666,
|
||||||
|
| "StartDateTime": "2018-02-23T11:30:00+02:00"
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val request = ValuationsRequest(clinicId = 6L, doctorId = 38275L, payerDetailsList = List(
|
||||||
|
PayerDetails(brandId = Some(2L), contractId = 1111111L, payerId = 22222L, payerName = "FIRMA",
|
||||||
|
productElementId = 3333333L, productId = 44444L, productInContractId = 555555L, servaAppId = 0L, servaId = 6666L)
|
||||||
|
), referralRequiredByService = false, roomId = 543L, serviceId = 6666L,
|
||||||
|
startDateTime = ZonedDateTime.parse("2018-02-23T11:30:00+02:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME))
|
||||||
|
|
||||||
|
val requestJson = request.asJson
|
||||||
|
val requestActual = requestJson.as[ValuationsRequest]
|
||||||
|
val requestExpected = json.as[ValuationsRequest]
|
||||||
|
|
||||||
|
requestActual should be (requestExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.api.json.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
import com.lbs.api.json.JsonSerializer.extensions._
|
||||||
|
import org.scalatest.{FunSuiteLike, Matchers}
|
||||||
|
|
||||||
|
class VisitsHistoryResponseSpec extends FunSuiteLike with Matchers with CommonSpec {
|
||||||
|
test("deserialization") {
|
||||||
|
val json =
|
||||||
|
"""
|
||||||
|
|{
|
||||||
|
| "AreMoreVisits": false,
|
||||||
|
| "HistoricVisits": [
|
||||||
|
| {
|
||||||
|
| "ClinicName": "Szewska 3A",
|
||||||
|
| "DoctorName": "TARAS SHEVCZENKO",
|
||||||
|
| "HasRecommendations": false,
|
||||||
|
| "HasReferrals": false,
|
||||||
|
| "IsAdditional": false,
|
||||||
|
| "Links": [
|
||||||
|
| {
|
||||||
|
| "Href": "/PatientPortalMobileAPI/api/visits/recommendations/222222222",
|
||||||
|
| "Method": "GET",
|
||||||
|
| "Rel": "get_recommendations"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "QuestionToVisit": {
|
||||||
|
| "IsAnswered": false,
|
||||||
|
| "IsAsked": false,
|
||||||
|
| "IsQuestionToVisitAvailable": false
|
||||||
|
| },
|
||||||
|
| "RateVisit": {
|
||||||
|
| "IsRatingAvailable": false,
|
||||||
|
| "IsVisitRated": false
|
||||||
|
| },
|
||||||
|
| "ReservationId": 222222222,
|
||||||
|
| "Service": {
|
||||||
|
| "Id": 6621,
|
||||||
|
| "Name": "stomatolog"
|
||||||
|
| },
|
||||||
|
| "VisitDate": {
|
||||||
|
| "FormattedDate": "17th Jan 2018, at 1:00 pm",
|
||||||
|
| "StartDateTime": "2018-01-17T13:00:00+02:00"
|
||||||
|
| }
|
||||||
|
| },
|
||||||
|
| {
|
||||||
|
| "ClinicName": "LX Wrocław - Szewska 3A",
|
||||||
|
| "DoctorName": "lek. stom. TARAS SHEVCZENKO",
|
||||||
|
| "HasRecommendations": false,
|
||||||
|
| "HasReferrals": false,
|
||||||
|
| "IsAdditional": false,
|
||||||
|
| "Links": [
|
||||||
|
| {
|
||||||
|
| "Href": "/PatientPortalMobileAPI/api/visits/recommendations/999999999",
|
||||||
|
| "Method": "GET",
|
||||||
|
| "Rel": "get_recommendations"
|
||||||
|
| }
|
||||||
|
| ],
|
||||||
|
| "QuestionToVisit": {
|
||||||
|
| "IsAnswered": false,
|
||||||
|
| "IsAsked": false,
|
||||||
|
| "IsQuestionToVisitAvailable": false
|
||||||
|
| },
|
||||||
|
| "RateVisit": {
|
||||||
|
| "IsRatingAvailable": false,
|
||||||
|
| "IsVisitRated": false
|
||||||
|
| },
|
||||||
|
| "ReservationId": 999999999,
|
||||||
|
| "Service": {
|
||||||
|
| "Id": 3589,
|
||||||
|
| "Name": "Wypełnienie ubytku korony zęba na 2 powierzchniach"
|
||||||
|
| },
|
||||||
|
| "VisitDate": {
|
||||||
|
| "FormattedDate": "17th Jan 2018, at 1:00 pm",
|
||||||
|
| "StartDateTime": "2018-01-17T13:00:00+02:00"
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
| ]
|
||||||
|
|}
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
val response = json.as[VisitsHistoryResponse]
|
||||||
|
|
||||||
|
response.areMoreVisits should be(false)
|
||||||
|
response.historicVisits.size should be(2)
|
||||||
|
val historicVisit = response.historicVisits.head
|
||||||
|
historicVisit.clinicName should be("Szewska 3A")
|
||||||
|
historicVisit.doctorName should be("TARAS SHEVCZENKO")
|
||||||
|
historicVisit.reservationId should be(222222222L)
|
||||||
|
testSimpleEntity(historicVisit.service, 6621L, "stomatolog")
|
||||||
|
historicVisit.visitDate.formattedDate should be("17th Jan 2018, at 1:00 pm")
|
||||||
|
historicVisit.visitDate.startDateTime should be(ZonedDateTime.parse("2018-01-17T13:00:00+02:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME))
|
||||||
|
}
|
||||||
|
}
|
||||||
5
bot/build.gradle
Normal file
5
bot/build.gradle
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies {
|
||||||
|
compile project(':common')
|
||||||
|
|
||||||
|
compile group: "info.mukel", name: "telegrambot4s_2.12", version: "3.0.14"
|
||||||
|
}
|
||||||
49
bot/src/main/scala/com/lbs/bot/Bot.scala
Normal file
49
bot/src/main/scala/com/lbs/bot/Bot.scala
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot
|
||||||
|
|
||||||
|
import com.lbs.bot.model._
|
||||||
|
import com.lbs.bot.telegram.TelegramBot
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
|
||||||
|
class Bot(telegram: TelegramBot /* other bots */) extends Logger {
|
||||||
|
def sendMessage(source: MessageSource, text: String): Unit =
|
||||||
|
resolveAdapter(source).sendMessage(source.chatId, text)
|
||||||
|
|
||||||
|
def sendMessage(source: MessageSource, text: String, inlineKeyboard: Option[InlineKeyboard] = None): Unit =
|
||||||
|
resolveAdapter(source).sendMessage(source.chatId, text, inlineKeyboard)
|
||||||
|
|
||||||
|
def sendEditMessage(source: MessageSource, messageId: String, inlineKeyboard: Option[InlineKeyboard]): Unit =
|
||||||
|
resolveAdapter(source).sendEditMessage(source.chatId, messageId, inlineKeyboard)
|
||||||
|
|
||||||
|
def sendEditMessage(source: MessageSource, messageId: String, text: String, inlineKeyboard: Option[InlineKeyboard] = None): Unit =
|
||||||
|
resolveAdapter(source).sendEditMessage(source.chatId, messageId, text, inlineKeyboard)
|
||||||
|
|
||||||
|
private def resolveAdapter(source: MessageSource): PollBot[_] =
|
||||||
|
source.sourceSystem match {
|
||||||
|
case TelegramMessageSourceSystem => telegram
|
||||||
|
case sourceSystem =>
|
||||||
|
sys.error(s"Unsupported source system $sourceSystem")
|
||||||
|
}
|
||||||
|
}
|
||||||
38
bot/src/main/scala/com/lbs/bot/PollBot.scala
Normal file
38
bot/src/main/scala/com/lbs/bot/PollBot.scala
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot
|
||||||
|
|
||||||
|
import com.lbs.bot.model.{Event, InlineKeyboard}
|
||||||
|
|
||||||
|
trait PollBot[In <: Event] {
|
||||||
|
def sendMessage(chatId: String, text: String): Unit
|
||||||
|
|
||||||
|
def sendMessage(chatId: String, text: String, buttons: Option[InlineKeyboard] = None): Unit
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: String, messageId: String, buttons: Option[InlineKeyboard]): Unit
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: String, messageId: String, text: String, buttons: Option[InlineKeyboard] = None): Unit
|
||||||
|
|
||||||
|
protected def onReceive(command: In): Unit
|
||||||
|
}
|
||||||
30
bot/src/main/scala/com/lbs/bot/WebhookBot.scala
Normal file
30
bot/src/main/scala/com/lbs/bot/WebhookBot.scala
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot
|
||||||
|
|
||||||
|
import com.lbs.bot.model.Event
|
||||||
|
|
||||||
|
trait WebhookBot[In <: Event] extends PollBot[In] {
|
||||||
|
def processPayload(payload: String, signature: Option[String]): Unit
|
||||||
|
}
|
||||||
38
bot/src/main/scala/com/lbs/bot/model/Button.scala
Normal file
38
bot/src/main/scala/com/lbs/bot/model/Button.scala
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
object Button {
|
||||||
|
def apply(label: String, id: Long) = new TaggedButton(label, id.toString)
|
||||||
|
|
||||||
|
def apply(label: String, id: String) = new TaggedButton(label, id)
|
||||||
|
|
||||||
|
def apply(label: String) = new LabeledButton(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Button
|
||||||
|
|
||||||
|
class TaggedButton(val label: String, val tag: String) extends Button
|
||||||
|
|
||||||
|
class LabeledButton(val label: String) extends Button
|
||||||
28
bot/src/main/scala/com/lbs/bot/model/Command.scala
Normal file
28
bot/src/main/scala/com/lbs/bot/model/Command.scala
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
case class Message(messageId: String, text: Option[String] = None)
|
||||||
|
|
||||||
|
case class Command(source: MessageSource, message: Message, callbackData: Option[String] = None)
|
||||||
26
bot/src/main/scala/com/lbs/bot/model/Event.scala
Normal file
26
bot/src/main/scala/com/lbs/bot/model/Event.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
trait Event
|
||||||
26
bot/src/main/scala/com/lbs/bot/model/InlineKeyboard.scala
Normal file
26
bot/src/main/scala/com/lbs/bot/model/InlineKeyboard.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
case class InlineKeyboard(buttons: Seq[Seq[Button]])
|
||||||
26
bot/src/main/scala/com/lbs/bot/model/MessageSource.scala
Normal file
26
bot/src/main/scala/com/lbs/bot/model/MessageSource.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
case class MessageSource(sourceSystem: MessageSourceSystem, chatId: String)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.model
|
||||||
|
|
||||||
|
trait MessageSourceSystem {
|
||||||
|
def id: Long
|
||||||
|
|
||||||
|
def name: String
|
||||||
|
|
||||||
|
override def toString: String = name
|
||||||
|
}
|
||||||
|
|
||||||
|
object MessageSourceSystem {
|
||||||
|
val MessageSourceSystems: Seq[MessageSourceSystem] = Seq(
|
||||||
|
TelegramMessageSourceSystem,
|
||||||
|
FacebookMessageSourceSystem
|
||||||
|
)
|
||||||
|
|
||||||
|
private val MessageSourceSystemsMap = MessageSourceSystems.map(e => e.id -> e).toMap
|
||||||
|
|
||||||
|
def apply(id: Long): MessageSourceSystem = {
|
||||||
|
MessageSourceSystemsMap.getOrElse(id, sys.error(s"Unsupported source system $id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TelegramMessageSourceSystem extends MessageSourceSystem {
|
||||||
|
override def id: Long = 1
|
||||||
|
|
||||||
|
override def name: String = "Telegram"
|
||||||
|
}
|
||||||
|
|
||||||
|
object FacebookMessageSourceSystem extends MessageSourceSystem {
|
||||||
|
override def id: Long = 2
|
||||||
|
|
||||||
|
override def name: String = "Facebook"
|
||||||
|
}
|
||||||
36
bot/src/main/scala/com/lbs/bot/package.scala
Normal file
36
bot/src/main/scala/com/lbs/bot/package.scala
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs
|
||||||
|
|
||||||
|
import com.lbs.bot.model.{Button, InlineKeyboard}
|
||||||
|
|
||||||
|
package object bot {
|
||||||
|
def createInlineKeyboard(buttons: Seq[Button], columns: Int = 2): Option[InlineKeyboard] = {
|
||||||
|
Option(buttons).filterNot(_.isEmpty).map(b => InlineKeyboard(b.grouped(columns).toSeq))
|
||||||
|
}
|
||||||
|
|
||||||
|
def createInlineKeyboard(buttons: Seq[Seq[Button]]): Option[InlineKeyboard] = {
|
||||||
|
Option(buttons).filterNot(_.isEmpty).map(InlineKeyboard)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
bot/src/main/scala/com/lbs/bot/telegram/TelegramBot.scala
Normal file
51
bot/src/main/scala/com/lbs/bot/telegram/TelegramBot.scala
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.telegram
|
||||||
|
|
||||||
|
import com.lbs.bot.PollBot
|
||||||
|
import com.lbs.bot.model._
|
||||||
|
import com.lbs.bot.telegram.TelegramModelConverters._
|
||||||
|
import info.mukel.telegrambot4s.models.InlineKeyboardMarkup
|
||||||
|
|
||||||
|
class TelegramBot(onCommand: Command => Unit, botToken: String) extends PollBot[TelegramEvent] {
|
||||||
|
|
||||||
|
private val telegramBot = new TelegramClient(onReceive, botToken)
|
||||||
|
telegramBot.run()
|
||||||
|
|
||||||
|
def sendMessage(chatId: String, text: String): Unit =
|
||||||
|
telegramBot.sendMessage(chatId.toLong, text)
|
||||||
|
|
||||||
|
def sendMessage(chatId: String, text: String, buttons: Option[InlineKeyboard] = None): Unit =
|
||||||
|
telegramBot.sendMessage(chatId.toLong, text, replyMarkup = buttons.map(_.mapTo[InlineKeyboardMarkup]))
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: String, messageId: String, buttons: Option[InlineKeyboard]): Unit =
|
||||||
|
telegramBot.sendEditMessage(chatId.toLong, messageId.toInt, replyMarkup = buttons.map(_.mapTo[InlineKeyboardMarkup]))
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: String, messageId: String, text: String, buttons: Option[InlineKeyboard] = None): Unit =
|
||||||
|
telegramBot.sendEditMessage(chatId.toLong, messageId.toInt, text, replyMarkup = buttons.map(_.mapTo[InlineKeyboardMarkup]))
|
||||||
|
|
||||||
|
override protected def onReceive(command: TelegramEvent): Unit = {
|
||||||
|
onCommand(command.mapTo[Command])
|
||||||
|
}
|
||||||
|
}
|
||||||
66
bot/src/main/scala/com/lbs/bot/telegram/TelegramClient.scala
Normal file
66
bot/src/main/scala/com/lbs/bot/telegram/TelegramClient.scala
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.telegram
|
||||||
|
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import info.mukel.telegrambot4s.api.declarative.{Callbacks, Commands}
|
||||||
|
import info.mukel.telegrambot4s.api.{Polling, TelegramBot => TelegramBotBase}
|
||||||
|
import info.mukel.telegrambot4s.methods.{EditMessageReplyMarkup, EditMessageText, ParseMode, SendMessage}
|
||||||
|
import info.mukel.telegrambot4s.models._
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
class TelegramClient(onReceive: TelegramEvent => Unit, botToken: String) extends TelegramBotBase with Polling with Commands with Callbacks with Logger {
|
||||||
|
|
||||||
|
override def token: String = botToken
|
||||||
|
|
||||||
|
def sendMessage(chatId: Long, text: String): Future[Message] =
|
||||||
|
request(SendMessage(chatId, text, parseMode = Some(ParseMode.HTML)))
|
||||||
|
|
||||||
|
def sendMessage(chatId: Long, text: String, replyMarkup: Option[InlineKeyboardMarkup] = None): Future[Message] =
|
||||||
|
request(SendMessage(chatId, text, parseMode = Some(ParseMode.HTML), replyMarkup = replyMarkup))
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: Long, messageId: Int, replyMarkup: Option[InlineKeyboardMarkup]): Future[Either[Boolean, Message]] =
|
||||||
|
request(EditMessageReplyMarkup(Some(chatId), Some(messageId), replyMarkup = replyMarkup))
|
||||||
|
|
||||||
|
def sendEditMessage(chatId: Long, messageId: Int, text: String, replyMarkup: Option[InlineKeyboardMarkup] = None): Future[Either[Boolean, Message]] =
|
||||||
|
request(EditMessageText(Some(chatId), Some(messageId), text = text, parseMode = Some(ParseMode.HTML), replyMarkup = replyMarkup))
|
||||||
|
|
||||||
|
|
||||||
|
override def receiveMessage(msg: Message): Unit = {
|
||||||
|
LOG.debug(s"Received telegram message: $msg")
|
||||||
|
onReceive(TelegramEvent(msg, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackWithTag(TagPrefix) { implicit cbq =>
|
||||||
|
LOG.debug(s"Received telegram callback: $cbq")
|
||||||
|
ackCallback()
|
||||||
|
for {
|
||||||
|
data <- cbq.data.map(_.stripPrefix(TagPrefix))
|
||||||
|
msg <- cbq.message
|
||||||
|
} {
|
||||||
|
onReceive(TelegramEvent(msg, Some(data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
bot/src/main/scala/com/lbs/bot/telegram/TelegramEvent.scala
Normal file
29
bot/src/main/scala/com/lbs/bot/telegram/TelegramEvent.scala
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot.telegram
|
||||||
|
|
||||||
|
import com.lbs.bot.model.Event
|
||||||
|
import info.mukel.telegrambot4s.models.Message
|
||||||
|
|
||||||
|
case class TelegramEvent(msg: Message, callbackData: Option[String]) extends Event
|
||||||
77
bot/src/main/scala/com/lbs/bot/telegram/package.scala
Normal file
77
bot/src/main/scala/com/lbs/bot/telegram/package.scala
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.bot
|
||||||
|
|
||||||
|
import com.lbs.bot.model._
|
||||||
|
import com.lbs.common.ModelConverters
|
||||||
|
import info.mukel.telegrambot4s.models
|
||||||
|
import info.mukel.telegrambot4s.models.{InlineKeyboardButton, InlineKeyboardMarkup}
|
||||||
|
|
||||||
|
package object telegram {
|
||||||
|
|
||||||
|
protected[bot] val TagPrefix = "callback"
|
||||||
|
|
||||||
|
object TelegramModelConverters extends ModelConverters {
|
||||||
|
implicit val TelegramCommandToCommandConverter:
|
||||||
|
ObjectConverter[TelegramEvent, Command] =
|
||||||
|
new ObjectConverter[TelegramEvent, Command] {
|
||||||
|
override def convert[Z <: TelegramEvent](data: Z): Command = {
|
||||||
|
Command(
|
||||||
|
source = MessageSource(TelegramMessageSourceSystem, data.msg.chat.id.toString),
|
||||||
|
message = Message(data.msg.messageId.toString, data.msg.text),
|
||||||
|
callbackData = data.callbackData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val TelegramMessageToMessageConverter:
|
||||||
|
ObjectConverter[models.Message, Message] =
|
||||||
|
new ObjectConverter[models.Message, Message] {
|
||||||
|
override def convert[Z <: models.Message](data: Z): Message = {
|
||||||
|
Message(data.messageId.toString, data.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val InlineKeyboardToInlineKeyboardMarkup:
|
||||||
|
ObjectConverter[InlineKeyboard, InlineKeyboardMarkup] =
|
||||||
|
new ObjectConverter[InlineKeyboard, InlineKeyboardMarkup] {
|
||||||
|
override def convert[Z <: InlineKeyboard](inlineKeyboard: Z): InlineKeyboardMarkup = {
|
||||||
|
val buttons = inlineKeyboard.buttons.map { row =>
|
||||||
|
row.map(createInlineKeyboardButton)
|
||||||
|
}
|
||||||
|
InlineKeyboardMarkup(buttons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def createInlineKeyboardButton(button: Button) = {
|
||||||
|
button match {
|
||||||
|
case b: TaggedButton => InlineKeyboardButton.callbackData(b.label, tag(b.tag))
|
||||||
|
case b: LabeledButton => InlineKeyboardButton.callbackData(b.label, b.label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def tag(name: String): String = TagPrefix + name
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
build.gradle
Normal file
47
build.gradle
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
group = 'com.lbs'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
scalaVersion = "2.12.6"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'idea'
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
maven { url 'https://repo.spring.io/libs-milestone' }
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'scala'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile group: 'org.scala-lang', name: "scala-library", version: scalaVersion
|
||||||
|
compile('org.scalaz:scalaz-core_2.12:7.2.23')
|
||||||
|
|
||||||
|
testCompile('org.scalatest:scalatest_2.12:3.0.4')
|
||||||
|
testCompile('org.mockito:mockito-core:2.13.0')
|
||||||
|
testCompile('org.pegdown:pegdown:1.6.0')
|
||||||
|
}
|
||||||
|
|
||||||
|
task scalaTest(dependsOn: ['testClasses'], type: JavaExec) {
|
||||||
|
main = 'org.scalatest.tools.Runner'
|
||||||
|
args = ['-R', "build/classes/test",
|
||||||
|
'-u', "build/test-results/$name",
|
||||||
|
'-h', "build/resports/scalaTests/$name",
|
||||||
|
'-o'
|
||||||
|
]
|
||||||
|
classpath = sourceSets.test.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
test.dependsOn scalaTest
|
||||||
|
}
|
||||||
5
common/build.gradle
Normal file
5
common/build.gradle
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies {
|
||||||
|
compile group: "org.slf4j", name: "slf4j-api", version: "1.7.25"
|
||||||
|
compile group: "ch.qos.logback", name: "logback-classic", version: "1.2.3"
|
||||||
|
compile group: "ch.qos.logback", name: "logback-core", version: "1.2.3"
|
||||||
|
}
|
||||||
38
common/src/main/scala/com/lbs/common/Implicits.scala
Normal file
38
common/src/main/scala/com/lbs/common/Implicits.scala
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.common
|
||||||
|
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
|
object Implicits {
|
||||||
|
implicit def optionalToOption[T](optional: Optional[T]): Option[T] = {
|
||||||
|
Option(optional.orElse(null.asInstanceOf[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit def optionToOptional[T](option: Option[T]): Optional[T] = {
|
||||||
|
Optional.of(option.getOrElse(null.asInstanceOf[T]))
|
||||||
|
}
|
||||||
|
}
|
||||||
66
common/src/main/scala/com/lbs/common/Logger.scala
Normal file
66
common/src/main/scala/com/lbs/common/Logger.scala
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.common
|
||||||
|
|
||||||
|
import org.slf4j
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
trait Logger {
|
||||||
|
private val log: slf4j.Logger = LoggerFactory.getLogger(this.getClass)
|
||||||
|
|
||||||
|
protected val LOG = new LoggerWrapper
|
||||||
|
|
||||||
|
class LoggerWrapper {
|
||||||
|
def debug(msg: => String): Unit = {
|
||||||
|
if (log.isDebugEnabled)
|
||||||
|
log.debug(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
def warn(msg: => String): Unit = {
|
||||||
|
if (log.isWarnEnabled)
|
||||||
|
log.warn(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
def warn(msg: => String, throwable: Throwable): Unit = {
|
||||||
|
if (log.isWarnEnabled)
|
||||||
|
log.warn(msg, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
def error(msg: => String): Unit = {
|
||||||
|
if (log.isErrorEnabled)
|
||||||
|
log.error(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
def error(msg: => String, throwable: Throwable): Unit = {
|
||||||
|
if (log.isErrorEnabled)
|
||||||
|
log.error(msg, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
def info(msg: => String): Unit = {
|
||||||
|
if (log.isInfoEnabled)
|
||||||
|
log.info(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
common/src/main/scala/com/lbs/common/ModelConverters.scala
Normal file
47
common/src/main/scala/com/lbs/common/ModelConverters.scala
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.common
|
||||||
|
|
||||||
|
import scala.collection.generic.CanBuildFrom
|
||||||
|
import scala.language.{higherKinds, implicitConversions}
|
||||||
|
|
||||||
|
trait ModelConverters {
|
||||||
|
|
||||||
|
trait CollectionConverter[-In, Out] {
|
||||||
|
def convert[Z <: In, Col[X] <: Iterable[X]](col: Col[Z])(implicit bf: CanBuildFrom[Col[Z], Out, Col[Out]]): Col[Out]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ObjectConverter[-In, Out] {
|
||||||
|
def convert[Z <: In](any: Z): Out
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class CollectionOps[From, Col[X] <: Iterable[X]](col: Col[From]) {
|
||||||
|
def mapTo[To](implicit converter: CollectionConverter[From, To], bf: CanBuildFrom[Col[From], To, Col[To]]): Col[To] = converter.convert(col)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class ObjectOps[From](anyRef: From) {
|
||||||
|
def mapTo[To](implicit converter: ObjectConverter[From, To]): To = converter.convert(anyRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
common/src/main/scala/com/lbs/common/ParametrizedLock.scala
Normal file
32
common/src/main/scala/com/lbs/common/ParametrizedLock.scala
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.common
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class ParametrizedLock[K] {
|
||||||
|
private val locks = new ConcurrentHashMap[K, AnyRef]
|
||||||
|
|
||||||
|
def obtainLock(key: K): AnyRef = locks.computeIfAbsent(key, k => new AnyRef)
|
||||||
|
}
|
||||||
41
common/src/main/scala/com/lbs/common/Scheduler.scala
Normal file
41
common/src/main/scala/com/lbs/common/Scheduler.scala
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.common
|
||||||
|
|
||||||
|
import java.util.concurrent.{Executors, ScheduledFuture}
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
class Scheduler(poolSize: Int) {
|
||||||
|
private val scheduledThreadPool = Executors.newScheduledThreadPool(poolSize)
|
||||||
|
|
||||||
|
def schedule(fn: => Unit, period: FiniteDuration): ScheduledFuture[_] = {
|
||||||
|
scheduledThreadPool.scheduleAtFixedRate(() => fn, period.length, period.length, period.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
def schedule(fn: => Unit, delay: FiniteDuration, period: FiniteDuration): ScheduledFuture[_] = {
|
||||||
|
require(delay.unit == period.unit, s"Delay units must be the same as for period ${period.unit}")
|
||||||
|
scheduledThreadPool.scheduleAtFixedRate(() => fn, delay.length, period.length, period.unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
gradle.properties
Normal file
5
gradle.properties
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
org.gradle.daemon=true
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.configureondemand=true
|
||||||
|
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Thu Feb 01 18:20:57 CET 2018
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||||
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save ( ) {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
31
server/build.gradle
Normal file
31
server/build.gradle
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
springBootVersion = '2.0.2.RELEASE'
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'scala'
|
||||||
|
apply plugin: 'org.springframework.boot'
|
||||||
|
apply plugin: 'io.spring.dependency-management'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':api')
|
||||||
|
compile project(':bot')
|
||||||
|
compile project(':common')
|
||||||
|
|
||||||
|
compile('org.springframework.boot:spring-boot-starter')
|
||||||
|
compile('org.springframework.boot:spring-boot-starter-data-jpa')
|
||||||
|
|
||||||
|
compile('org.jasypt:jasypt:1.9.2')
|
||||||
|
compile('org.postgresql:postgresql:42.2.1.jre7')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
3
server/src/main/resources/application.conf
Normal file
3
server/src/main/resources/application.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
akka {
|
||||||
|
loglevel = "WARNING"
|
||||||
|
}
|
||||||
31
server/src/main/resources/application.yml
Normal file
31
server/src/main/resources/application.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: "jdbc:postgresql://127.0.0.1:5432/lbs"
|
||||||
|
username: "lbs"
|
||||||
|
password: "lsb123"
|
||||||
|
jpa:
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
temp:
|
||||||
|
use_jdbc_metadata_defaults: "false"
|
||||||
|
database-platform: "org.hibernate.dialect.PostgreSQL9Dialect"
|
||||||
|
generate-ddl: "true"
|
||||||
|
# hibernate:
|
||||||
|
# ddl-auto: "create-update"
|
||||||
|
# ddl-auto: "create-drop"
|
||||||
|
|
||||||
|
banner:
|
||||||
|
location: "classpath:/banner.txt"
|
||||||
|
|
||||||
|
logging:
|
||||||
|
file: logs/app.log
|
||||||
|
level:
|
||||||
|
com.lbs: DEBUG
|
||||||
|
# org.hibernate.SQL: DEBUG
|
||||||
|
# org.hibernate.type.descriptor.sql.BasicBinder: TRACE
|
||||||
|
pattern:
|
||||||
|
file: "%d{yyyy-MM-dd HH:mm:ss} %logger{25} - %msg%n"
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss} %logger{25} - %msg%n"
|
||||||
|
|
||||||
|
security.secret: ${SECURITY_SECRET:random_secret_hfjdsk72euhdsbcgg6}
|
||||||
|
telegram.token: ${TELEGRAM_TOKEN}
|
||||||
6
server/src/main/resources/banner.txt
Normal file
6
server/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
,--. ,-----. ,---.
|
||||||
|
| | | |) /_ ' .-'
|
||||||
|
| | | .-. \ `. `-.
|
||||||
|
| '--. | '--' / .-' |
|
||||||
|
`-----' `------' `-----'
|
||||||
|
|
||||||
35
server/src/main/scala/com/lbs/server/Boot.scala
Normal file
35
server/src/main/scala/com/lbs/server/Boot.scala
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
class Boot
|
||||||
|
|
||||||
|
object Boot extends App {
|
||||||
|
SpringApplication.run(classOf[Boot], args: _*)
|
||||||
|
}
|
||||||
148
server/src/main/scala/com/lbs/server/BootConfig.scala
Normal file
148
server/src/main/scala/com/lbs/server/BootConfig.scala
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, ActorSystem}
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.MessageSource
|
||||||
|
import com.lbs.bot.telegram.TelegramBot
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor._
|
||||||
|
import com.lbs.server.lang.Localization
|
||||||
|
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
|
||||||
|
import org.jasypt.util.text.{StrongTextEncryptor, TextEncryptor}
|
||||||
|
import org.springframework.beans.factory.annotation.{Autowired, Value}
|
||||||
|
import org.springframework.context.annotation.{Bean, Configuration}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class BootConfig {
|
||||||
|
@Value("${security.secret}")
|
||||||
|
private var secret: String = _
|
||||||
|
|
||||||
|
@Value("${telegram.token}")
|
||||||
|
private var telegramBotToken: String = _
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var apiService: ApiService = _
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var dataService: DataService = _
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var monitoringService: MonitoringService = _
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var localization: Localization = _
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def actorSystem = ActorSystem()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def textEncryptor: TextEncryptor = {
|
||||||
|
val encryptor = new StrongTextEncryptor
|
||||||
|
encryptor.setPassword(secret)
|
||||||
|
encryptor
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def authActorFactory: MessageSource => ActorRef = source => actorSystem.actorOf(Auth.props(source,
|
||||||
|
dataService, unauthorizedHelpActorFactory, loginActorFactory, chatActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def loginActorFactory: (MessageSource, ActorRef) => ActorRef = (source, originator) => actorSystem.actorOf(Login.props(source, bot,
|
||||||
|
dataService, apiService, textEncryptor, localization, originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def bookingActorFactory: UserId => ActorRef = userId => actorSystem.actorOf(Book.props(userId, bot, apiService, dataService,
|
||||||
|
monitoringService, localization, datePickerFactory, staticDataActorFactory, termsPagerActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def unauthorizedHelpActorFactory: MessageSource => ActorRef = source => actorSystem.actorOf(UnauthorizedHelp.props(source, bot))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def helpActorFactory: UserId => ActorRef = userId => actorSystem.actorOf(Help.props(userId, bot, localization))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def monitoringsActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(Monitorings.props(userId, bot, monitoringService, localization, monitoringsPagerActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def historyActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(History.props(userId, bot, apiService, localization, historyPagerActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def visitsActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(Visits.props(userId, bot, apiService, localization, visitsPagerActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def bugActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(Bug.props(userId, bot, dataService, bugPagerActorFactory, localization))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def settingsActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(Settings.props(userId, bot, dataService, localization))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def chatActorFactory: UserId => ActorRef =
|
||||||
|
userId => actorSystem.actorOf(Chat.props(userId, dataService, monitoringService, bookingActorFactory, helpActorFactory,
|
||||||
|
monitoringsActorFactory, historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def datePickerFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(DatePicker.props(userId, bot, localization, originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def staticDataActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(StaticData.props(userId, bot, localization, originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def termsPagerActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(Pagers(userId, bot, localization).termsPagerProps(originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def visitsPagerActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(Pagers(userId, bot, localization).visitsPagerProps(originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def bugPagerActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(Pagers(userId, bot, localization).bugPagerProps(originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def historyPagerActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(Pagers(userId, bot, localization).historyPagerProps(originator))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def monitoringsPagerActorFactory: (UserId, ActorRef) => ActorRef = (userId, originator) =>
|
||||||
|
actorSystem.actorOf(Pagers(userId, bot, localization).monitoringsPagerProps(originator))
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def router: ActorRef = actorSystem.actorOf(Router.props(authActorFactory))
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def telegram: TelegramBot = new TelegramBot(router ! _, telegramBotToken)
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
def bot: Bot = new Bot(telegram)
|
||||||
|
}
|
||||||
104
server/src/main/scala/com/lbs/server/actor/Auth.scala
Normal file
104
server/src/main/scala/com/lbs/server/actor/Auth.scala
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.bot.model.{Command, MessageSource}
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.{LoggedIn, UserId}
|
||||||
|
import com.lbs.server.service.DataService
|
||||||
|
import com.lbs.server.util.MessageExtractors._
|
||||||
|
|
||||||
|
class Auth(val source: MessageSource, dataService: DataService, unauthorizedHelpActorFactory: MessageSource => ActorRef,
|
||||||
|
loginActorFactory: (MessageSource, ActorRef) => ActorRef, chatActorFactory: UserId => ActorRef) extends Actor with Logger {
|
||||||
|
|
||||||
|
private val loginActor = loginActorFactory(source, self)
|
||||||
|
private val unauthorizedHelpActor: ActorRef = unauthorizedHelpActorFactory(source)
|
||||||
|
|
||||||
|
private var userId: Option[UserId] = getUserId
|
||||||
|
private var chatActor: ActorRef = _
|
||||||
|
|
||||||
|
override def receive: Receive = {
|
||||||
|
case cmd@Command(_, Text("/help"), _) if userId.isEmpty =>
|
||||||
|
unauthorizedHelpActor ! cmd
|
||||||
|
case cmd@Command(_, Text("/start"), _) if userId.isEmpty =>
|
||||||
|
unauthorizedHelpActor ! cmd
|
||||||
|
case cmd@Command(_, Text("/login"), _) =>
|
||||||
|
userId = None
|
||||||
|
loginActor ! Init
|
||||||
|
loginActor ! cmd
|
||||||
|
case cmd: Command if userId.isEmpty =>
|
||||||
|
loginActor ! cmd
|
||||||
|
case cmd: Command if userId.nonEmpty =>
|
||||||
|
chatActor = getChatActor(userId.get)
|
||||||
|
chatActor ! cmd
|
||||||
|
case LoggedIn(forwardCommand, id) =>
|
||||||
|
val uId = UserId(id, source)
|
||||||
|
val cmd = forwardCommand.cmd
|
||||||
|
userId = Some(uId)
|
||||||
|
chatActor = getChatActor(uId, reinit = true)
|
||||||
|
if (!cmd.message.text.contains("/login"))
|
||||||
|
chatActor ! cmd
|
||||||
|
case cmd: Command =>
|
||||||
|
chatActor ! cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getChatActor(userId: UserId, reinit: Boolean = false): ActorRef = {
|
||||||
|
if (chatActor == null) {
|
||||||
|
chatActorFactory(userId)
|
||||||
|
} else {
|
||||||
|
if (reinit) {
|
||||||
|
chatActor ! PoisonPill
|
||||||
|
chatActorFactory(userId)
|
||||||
|
} else chatActor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getUserId: Option[UserId] = {
|
||||||
|
val userIdMaybe = dataService.findUserIdBySource(source)
|
||||||
|
userIdMaybe.map(id => UserId(id, source))
|
||||||
|
}
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
loginActor ! PoisonPill
|
||||||
|
unauthorizedHelpActor ! PoisonPill
|
||||||
|
if (chatActor != null) chatActor ! PoisonPill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Auth {
|
||||||
|
def props(source: MessageSource, dataService: DataService, unauthorizedHelpActorFactory: MessageSource => ActorRef,
|
||||||
|
loginActorFactory: (MessageSource, ActorRef) => ActorRef, chatActorFactory: UserId => ActorRef): Props =
|
||||||
|
Props(classOf[Auth], source, dataService, unauthorizedHelpActorFactory, loginActorFactory, chatActorFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
342
server/src/main/scala/com/lbs/server/actor/Book.scala
Normal file
342
server/src/main/scala/com/lbs/server/actor/Book.scala
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.api.json.model._
|
||||||
|
import com.lbs.bot._
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.server.actor.Book._
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.DatePicker.{DateFromMode, DateToMode}
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.StaticData.StaticDataConfig
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.repository.model.Monitoring
|
||||||
|
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
|
||||||
|
import com.lbs.server.util.ServerModelConverters._
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: DataService, monitoringService: MonitoringService,
|
||||||
|
val localization: Localization, datePickerActorFactory: (UserId, ActorRef) => ActorRef, staticDataActorFactory: (UserId, ActorRef) => ActorRef,
|
||||||
|
termsPagerActorFactory: (UserId, ActorRef) => ActorRef) extends SafeFSM[FSMState, FSMData] with StaticDataForBooking with Localizable {
|
||||||
|
|
||||||
|
private val datePicker = datePickerActorFactory(userId, self)
|
||||||
|
protected val staticData = staticDataActorFactory(userId, self)
|
||||||
|
private val termsPager = termsPagerActorFactory(userId, self)
|
||||||
|
|
||||||
|
startWith(RequestCity, BookingData())
|
||||||
|
|
||||||
|
requestStaticData(RequestCity, AwaitCity, cityConfig) { bd: BookingData =>
|
||||||
|
withFunctions(
|
||||||
|
latestOptions = dataService.getLatestCities(userId.userId),
|
||||||
|
staticOptions = apiService.getAllCities(userId.userId),
|
||||||
|
applyId = id => bd.copy(cityId = id))
|
||||||
|
}(requestNext = RequestClinic)
|
||||||
|
|
||||||
|
requestStaticData(RequestClinic, AwaitClinic, clinicConfig) { bd: BookingData =>
|
||||||
|
withFunctions(
|
||||||
|
latestOptions = dataService.getLatestClinicsByCityId(userId.userId, bd.cityId.id),
|
||||||
|
staticOptions = apiService.getAllClinics(userId.userId, bd.cityId.id),
|
||||||
|
applyId = id => bd.copy(clinicId = id))
|
||||||
|
}(requestNext = RequestService)
|
||||||
|
|
||||||
|
requestStaticData(RequestService, AwaitService, serviceConfig) { bd: BookingData =>
|
||||||
|
withFunctions(
|
||||||
|
latestOptions = dataService.getLatestServicesByCityIdAndClinicId(userId.userId, bd.cityId.id, bd.clinicId.optionalId),
|
||||||
|
staticOptions = apiService.getAllServices(userId.userId, bd.cityId.id, bd.clinicId.optionalId),
|
||||||
|
applyId = id => bd.copy(serviceId = id))
|
||||||
|
}(requestNext = RequestDoctor)
|
||||||
|
|
||||||
|
requestStaticData(RequestDoctor, AwaitDoctor, doctorConfig) { bd: BookingData =>
|
||||||
|
withFunctions(
|
||||||
|
latestOptions = dataService.getLatestDoctorsByCityIdAndClinicIdAndServiceId(userId.userId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id),
|
||||||
|
staticOptions = apiService.getAllDoctors(userId.userId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id),
|
||||||
|
applyId = id => bd.copy(doctorId = id))
|
||||||
|
}(requestNext = RequestDateFrom)
|
||||||
|
|
||||||
|
whenSafe(RequestDateFrom) {
|
||||||
|
case Event(_, bookingData: BookingData) =>
|
||||||
|
datePicker ! DateFromMode
|
||||||
|
datePicker ! bookingData.dateFrom
|
||||||
|
goto(AwaitDateFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDateFrom) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
datePicker ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(date: ZonedDateTime, bookingData: BookingData) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestDateTo) using bookingData.copy(dateFrom = date)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestDateTo) {
|
||||||
|
case Event(_, bookingData: BookingData) =>
|
||||||
|
datePicker ! DateToMode
|
||||||
|
datePicker ! bookingData.dateFrom.plusDays(1)
|
||||||
|
goto(AwaitDateTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDateTo) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
datePicker ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(date: ZonedDateTime, bookingData: BookingData) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestDayTime) using bookingData.copy(dateTo = date)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestDayTime) {
|
||||||
|
case Event(Next, _: BookingData) =>
|
||||||
|
bot.sendMessage(userId.source, lang.chooseTimeOfDay,
|
||||||
|
inlineKeyboard = createInlineKeyboard(lang.timeOfDay.map { case (id, label) => Button(label, id.toString) }.toSeq, columns = 1))
|
||||||
|
goto(AwaitDayTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDayTime) {
|
||||||
|
case Event(Command(_, msg, Some(timeIdStr)), bookingData: BookingData) =>
|
||||||
|
invokeNext()
|
||||||
|
val timeId = timeIdStr.toInt
|
||||||
|
bot.sendEditMessage(userId.source, msg.messageId, lang.preferredTimeIs(timeId))
|
||||||
|
goto(RequestAction) using bookingData.copy(timeOfDay = timeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestAction) {
|
||||||
|
case Event(Next, bookingData: BookingData) =>
|
||||||
|
dataService.storeAppointment(userId.userId, bookingData)
|
||||||
|
bot.sendMessage(userId.source,
|
||||||
|
lang.bookingSummary(bookingData),
|
||||||
|
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.findTerms, Tags.FindTerms), Button(lang.modifyDate, Tags.ModifyDate))))
|
||||||
|
goto(AwaitAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitAction) {
|
||||||
|
case Event(Command(_, _, Some(Tags.FindTerms)), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestTerm)
|
||||||
|
case Event(Command(_, _, Some(Tags.ModifyDate)), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestDateFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestTerm) {
|
||||||
|
case Event(Next, bookingData: BookingData) =>
|
||||||
|
val availableTerms = apiService.getAvailableTerms(userId.userId, bookingData.cityId.id,
|
||||||
|
bookingData.clinicId.optionalId, bookingData.serviceId.id, bookingData.doctorId.optionalId,
|
||||||
|
bookingData.dateFrom, Some(bookingData.dateTo), timeOfDay = bookingData.timeOfDay)
|
||||||
|
termsPager ! availableTerms
|
||||||
|
goto(AwaitTerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitTerm) {
|
||||||
|
case Event(Command(_, _, Some(Tags.ModifyDate)), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestDateFrom)
|
||||||
|
case Event(Command(_, _, Some(Tags.CreateMonitoring)), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(AskMonitoringOptions)
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
termsPager ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(term: AvailableVisitsTermPresentation, _) =>
|
||||||
|
self ! term
|
||||||
|
goto(RequestReservation)
|
||||||
|
case Event(Pager.NoItemsFound, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.noTermsFound, inlineKeyboard =
|
||||||
|
createInlineKeyboard(Seq(Button(lang.modifyDate, Tags.ModifyDate), Button(lang.createMonitoring, Tags.CreateMonitoring))))
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestReservation) {
|
||||||
|
case Event(term: AvailableVisitsTermPresentation, bookingData: BookingData) =>
|
||||||
|
val response = apiService.temporaryReservation(userId.userId, term.mapTo[TemporaryReservationRequest], term.mapTo[ValuationsRequest])
|
||||||
|
response match {
|
||||||
|
case Left(ex) =>
|
||||||
|
bot.sendMessage(userId.source, ex.getMessage)
|
||||||
|
invokeNext()
|
||||||
|
stay()
|
||||||
|
case Right((temporaryReservation, valuations)) =>
|
||||||
|
bot.sendMessage(userId.source, lang.confirmAppointment(term, valuations),
|
||||||
|
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.cancel, Tags.Cancel), Button(lang.book, Tags.Book))))
|
||||||
|
goto(AwaitReservation) using bookingData.copy(term = Some(term), temporaryReservationId = Some(temporaryReservation.id), valuations = Some(valuations))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitReservation) {
|
||||||
|
case Event(Command(_, _, Some(Tags.Cancel)), bookingData: BookingData) =>
|
||||||
|
apiService.deleteTemporaryReservation(userId.userId, bookingData.temporaryReservationId.get)
|
||||||
|
stay()
|
||||||
|
case Event(Command(_, _, Some(Tags.Book)), bookingData: BookingData) =>
|
||||||
|
val reservationRequestMaybe = for {
|
||||||
|
tmpReservationId <- bookingData.temporaryReservationId
|
||||||
|
valuations <- bookingData.valuations
|
||||||
|
visitTermVariant <- valuations.visitTermVariants.headOption
|
||||||
|
term <- bookingData.term
|
||||||
|
} yield (tmpReservationId, visitTermVariant, term).mapTo[ReservationRequest]
|
||||||
|
|
||||||
|
reservationRequestMaybe match {
|
||||||
|
case Some(reservationRequest) =>
|
||||||
|
apiService.reservation(userId.userId, reservationRequest) match {
|
||||||
|
case Left(ex) =>
|
||||||
|
bot.sendMessage(userId.source, ex.getMessage)
|
||||||
|
invokeNext()
|
||||||
|
stay()
|
||||||
|
case Right(success) =>
|
||||||
|
log.debug(s"Successfully confirmed: $success")
|
||||||
|
bot.sendMessage(userId.source, lang.appointmentIsConfirmed)
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AskMonitoringOptions) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.chooseTypeOfMonitoring,
|
||||||
|
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.bookByApplication, Tags.BookByApplication), Button(lang.bookManually, Tags.BookManually)), columns = 1))
|
||||||
|
stay()
|
||||||
|
case Event(Command(_, _, Some(autobookStr)), bookingData: BookingData) =>
|
||||||
|
val autobook = autobookStr.toBoolean
|
||||||
|
invokeNext()
|
||||||
|
goto(CreateMonitoring) using bookingData.copy(autobook = autobook)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(CreateMonitoring) {
|
||||||
|
case Event(Next, bookingData: BookingData) =>
|
||||||
|
LOG.debug(s"Creating monitoring for $bookingData")
|
||||||
|
Try(monitoringService.createMonitoring((userId -> bookingData).mapTo[Monitoring])) match {
|
||||||
|
case Success(_) => bot.sendMessage(userId.source, lang.monitoringHasBeenCreated)
|
||||||
|
case Failure(ex) =>
|
||||||
|
LOG.error("Unable to create monitoring", ex)
|
||||||
|
bot.sendMessage(userId.source, lang.unableToCreateMonitoring)
|
||||||
|
}
|
||||||
|
goto(RequestCity) using BookingData()
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
reinit()
|
||||||
|
case e: Event =>
|
||||||
|
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def cityConfig = StaticDataConfig(lang.city, "Wrocław", isAnyAllowed = false)
|
||||||
|
|
||||||
|
private def clinicConfig = StaticDataConfig(lang.clinic, "Swobodna 1", isAnyAllowed = true)
|
||||||
|
|
||||||
|
private def serviceConfig = StaticDataConfig(lang.service, "Stomatolog", isAnyAllowed = false)
|
||||||
|
|
||||||
|
private def doctorConfig = StaticDataConfig(lang.doctor, "Bartniak", isAnyAllowed = true)
|
||||||
|
|
||||||
|
private def reinit() = {
|
||||||
|
invokeNext()
|
||||||
|
datePicker ! Init
|
||||||
|
staticData ! Init
|
||||||
|
termsPager ! Init
|
||||||
|
goto(RequestCity) using BookingData()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
datePicker ! PoisonPill
|
||||||
|
staticData ! PoisonPill
|
||||||
|
termsPager ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Book {
|
||||||
|
|
||||||
|
def props(userId: UserId, bot: Bot, apiService: ApiService, dataService: DataService, monitoringService: MonitoringService,
|
||||||
|
localization: Localization, datePickerActorFactory: (UserId, ActorRef) => ActorRef,
|
||||||
|
staticDataActorFactory: (UserId, ActorRef) => ActorRef, termsPagerActorFactory: (UserId, ActorRef) => ActorRef): Props =
|
||||||
|
Props(classOf[Book], userId, bot, apiService, dataService, monitoringService, localization, datePickerActorFactory,
|
||||||
|
staticDataActorFactory, termsPagerActorFactory)
|
||||||
|
|
||||||
|
object RequestCity extends FSMState
|
||||||
|
|
||||||
|
object AwaitCity extends FSMState
|
||||||
|
|
||||||
|
object RequestClinic extends FSMState
|
||||||
|
|
||||||
|
object AwaitClinic extends FSMState
|
||||||
|
|
||||||
|
object RequestService extends FSMState
|
||||||
|
|
||||||
|
object AwaitService extends FSMState
|
||||||
|
|
||||||
|
object RequestDoctor extends FSMState
|
||||||
|
|
||||||
|
object AwaitDoctor extends FSMState
|
||||||
|
|
||||||
|
object CreateMonitoring extends FSMState
|
||||||
|
|
||||||
|
object AskMonitoringOptions extends FSMState
|
||||||
|
|
||||||
|
object RequestDateFrom extends FSMState
|
||||||
|
|
||||||
|
object AwaitDateFrom extends FSMState
|
||||||
|
|
||||||
|
object RequestDateTo extends FSMState
|
||||||
|
|
||||||
|
object AwaitDateTo extends FSMState
|
||||||
|
|
||||||
|
object RequestDayTime extends FSMState
|
||||||
|
|
||||||
|
object AwaitDayTime extends FSMState
|
||||||
|
|
||||||
|
object RequestAction extends FSMState
|
||||||
|
|
||||||
|
object AwaitAction extends FSMState
|
||||||
|
|
||||||
|
object RequestTerm extends FSMState
|
||||||
|
|
||||||
|
object AwaitTerm extends FSMState
|
||||||
|
|
||||||
|
object RequestReservation extends FSMState
|
||||||
|
|
||||||
|
object AwaitReservation extends FSMState
|
||||||
|
|
||||||
|
case class BookingData(cityId: IdName = null, clinicId: IdName = null,
|
||||||
|
serviceId: IdName = null, doctorId: IdName = null, dateFrom: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
dateTo: ZonedDateTime = ZonedDateTime.now().plusDays(1L), timeOfDay: Int = 0, autobook: Boolean = false, term: Option[AvailableVisitsTermPresentation] = None,
|
||||||
|
temporaryReservationId: Option[Long] = None, valuations: Option[ValuationsResponse] = None) extends FSMData
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val Cancel = "cancel"
|
||||||
|
val Book = "book"
|
||||||
|
val FindTerms = "find_terms"
|
||||||
|
val ModifyDate = "modify_date"
|
||||||
|
val CreateMonitoring = "create_monitoring"
|
||||||
|
val BookManually = "false"
|
||||||
|
val BookByApplication = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
135
server/src/main/scala/com/lbs/server/actor/Bug.scala
Normal file
135
server/src/main/scala/com/lbs/server/actor/Bug.scala
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.server.actor.Bug._
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.repository.model
|
||||||
|
import com.lbs.server.service.DataService
|
||||||
|
import com.lbs.server.util.MessageExtractors
|
||||||
|
|
||||||
|
class Bug(val userId: UserId, bot: Bot, dataService: DataService, bugPagerActorFactory: (UserId, ActorRef) => ActorRef,
|
||||||
|
val localization: Localization) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||||
|
|
||||||
|
private val bugPager = bugPagerActorFactory(userId, self)
|
||||||
|
|
||||||
|
startWith(RequestAction, null)
|
||||||
|
|
||||||
|
whenSafe(RequestAction) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.bugAction, inlineKeyboard =
|
||||||
|
createInlineKeyboard(Seq(Button(lang.createNewBug, Tags.SubmitNew), Button(lang.showSubmittedBugs, Tags.ListSubmitted))))
|
||||||
|
goto(AwaitAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitAction) {
|
||||||
|
case Event(Command(_, _, Some(Tags.SubmitNew)), _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.enterIssueDetails)
|
||||||
|
goto(AwaitBugDescription)
|
||||||
|
case Event(Command(_, _, Some(Tags.ListSubmitted)), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestData) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
val bugs = dataService.getBugs(userId.userId)
|
||||||
|
bugPager ! Init
|
||||||
|
bugPager ! Right[Throwable, Seq[model.Bug]](bugs)
|
||||||
|
goto(AwaitPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitPage) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
bugPager ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(Pager.NoItemsFound, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.noSubmittedIssuesFound)
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitBugDescription) {
|
||||||
|
case Event(Command(_, MessageExtractors.Text(details), _), _) =>
|
||||||
|
val bugId = dataService.submitBug(userId.userId, userId.source.sourceSystem.id, details)
|
||||||
|
bot.sendMessage(userId.source, lang.bugHasBeenCreated(bugId.getOrElse(-1L)))
|
||||||
|
goto(RequestAction) using null
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
invokeNext()
|
||||||
|
bugPager ! Init
|
||||||
|
goto(RequestAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
bugPager ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Bug {
|
||||||
|
def props(userId: UserId, bot: Bot, dataService: DataService, bugPagerActorFactory: (UserId, ActorRef) => ActorRef, localization: Localization): Props =
|
||||||
|
Props(classOf[Bug], userId, bot, dataService, bugPagerActorFactory, localization)
|
||||||
|
|
||||||
|
object RequestBugDetails extends FSMState
|
||||||
|
|
||||||
|
object AwaitBugDescription extends FSMState
|
||||||
|
|
||||||
|
object RequestAction extends FSMState
|
||||||
|
|
||||||
|
object AwaitAction extends FSMState
|
||||||
|
|
||||||
|
object RequestData extends FSMState
|
||||||
|
|
||||||
|
object AwaitPage extends FSMState
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val SubmitNew = "submit"
|
||||||
|
val ListSubmitted = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
196
server/src/main/scala/com/lbs/server/actor/Chat.scala
Normal file
196
server/src/main/scala/com/lbs/server/actor/Chat.scala
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.bot.model.Command
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import com.lbs.server.actor.Chat._
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.service.{DataService, MonitoringService}
|
||||||
|
import com.lbs.server.util.MessageExtractors._
|
||||||
|
|
||||||
|
import scala.util.matching.Regex
|
||||||
|
|
||||||
|
class Chat(val userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: UserId => ActorRef, helpActorFactory: UserId => ActorRef,
|
||||||
|
monitoringsActorFactory: UserId => ActorRef, historyActorFactory: UserId => ActorRef,
|
||||||
|
visitsActorFactory: UserId => ActorRef, settingsActorFactory: UserId => ActorRef,
|
||||||
|
bugActorFactory: UserId => ActorRef) extends SafeFSM[FSMState, FSMData] with Logger {
|
||||||
|
|
||||||
|
private val bookingActor = bookingActorFactory(userId)
|
||||||
|
private val helpActor = helpActorFactory(userId)
|
||||||
|
private val monitoringsActor = monitoringsActorFactory(userId)
|
||||||
|
private val historyActor = historyActorFactory(userId)
|
||||||
|
private val visitsActor = visitsActorFactory(userId)
|
||||||
|
private val settingsActor = settingsActorFactory(userId)
|
||||||
|
private val bugActor = bugActorFactory(userId)
|
||||||
|
|
||||||
|
startWith(HelpChat, null)
|
||||||
|
|
||||||
|
when(HelpChat, helpActor) {
|
||||||
|
case Event(cmd@Command(_, Text("/help"), _), _) =>
|
||||||
|
helpActor ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(cmd@Command(_, Text("/start"), _), _) =>
|
||||||
|
helpActor ! cmd
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(BookChat, bookingActor) {
|
||||||
|
case Event(Command(_, Text("/book"), _), _) =>
|
||||||
|
bookingActor ! Init
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(HistoryChat, historyActor) {
|
||||||
|
case Event(Command(_, Text("/history"), _), _) =>
|
||||||
|
historyActor ! Init
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(VisitsChat, visitsActor) {
|
||||||
|
case Event(Command(_, Text("/visits"), _), _) =>
|
||||||
|
visitsActor ! Init
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(BugChat, bugActor) {
|
||||||
|
case Event(Command(_, Text("/bug"), _), _) =>
|
||||||
|
bugActor ! Init
|
||||||
|
goto(BugChat)
|
||||||
|
}
|
||||||
|
|
||||||
|
when(MonitoringsChat, monitoringsActor) {
|
||||||
|
case Event(Command(_, Text("/monitorings"), _), _) =>
|
||||||
|
monitoringsActor ! Init
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(SettingsChat, settingsActor) {
|
||||||
|
case Event(Command(_, Text("/settings"), _), _) =>
|
||||||
|
settingsActor ! Init
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def when(state: FSMState, actor: ActorRef)(mainStateFunction: StateFunction): Unit = {
|
||||||
|
whenSafe(state) {
|
||||||
|
case event: Event =>
|
||||||
|
if (mainStateFunction.isDefinedAt(event)) mainStateFunction(event)
|
||||||
|
else {
|
||||||
|
val secondaryStateFunction = secondaryState(actor)
|
||||||
|
if (secondaryStateFunction.isDefinedAt(event)) secondaryStateFunction(event)
|
||||||
|
else eventHandler(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def secondaryState(actor: ActorRef): StateFunction = {
|
||||||
|
case Event(cmd@Command(_, Text("/bug"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(BugChat)
|
||||||
|
case Event(cmd@Command(_, Text("/help"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(HelpChat)
|
||||||
|
case Event(cmd@Command(_, Text("/start"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(HelpChat)
|
||||||
|
case Event(cmd@Command(_, Text("/book"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(BookChat)
|
||||||
|
case Event(cmd@Command(_, Text("/monitorings"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(MonitoringsChat)
|
||||||
|
case Event(cmd@Command(_, Text("/history"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(HistoryChat)
|
||||||
|
case Event(cmd@Command(_, Text("/visits"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(VisitsChat)
|
||||||
|
case Event(cmd@Command(_, Text("/settings"), _), _) =>
|
||||||
|
self ! cmd
|
||||||
|
goto(SettingsChat)
|
||||||
|
case Event(cmd@Command(_, Text(MonitoringId(monitoringIdStr, scheduleIdStr, timeStr)), _), _) =>
|
||||||
|
val monitoringId = monitoringIdStr.toLong
|
||||||
|
val scheduleId = scheduleIdStr.toLong
|
||||||
|
val time = timeStr.toLong
|
||||||
|
monitoringService.bookAppointmentByScheduleId(userId.userId, monitoringId, scheduleId, time)
|
||||||
|
stay()
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
actor ! cmd
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case e: Event =>
|
||||||
|
LOG.debug(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
bookingActor ! PoisonPill
|
||||||
|
helpActor ! PoisonPill
|
||||||
|
monitoringsActor ! PoisonPill
|
||||||
|
historyActor ! PoisonPill
|
||||||
|
visitsActor ! PoisonPill
|
||||||
|
settingsActor ! PoisonPill
|
||||||
|
bugActor ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Chat {
|
||||||
|
def props(userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: UserId => ActorRef, helpActorFactory: UserId => ActorRef,
|
||||||
|
monitoringsActorFactory: UserId => ActorRef, historyActorFactory: UserId => ActorRef,
|
||||||
|
visitsActorFactory: UserId => ActorRef, settingsActorFactory: UserId => ActorRef, bugActorFactory: UserId => ActorRef): Props =
|
||||||
|
Props(classOf[Chat], userId, dataService, monitoringService, bookingActorFactory, helpActorFactory, monitoringsActorFactory,
|
||||||
|
historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory)
|
||||||
|
|
||||||
|
object HelpChat extends FSMState
|
||||||
|
|
||||||
|
object BookChat extends FSMState
|
||||||
|
|
||||||
|
object MonitoringsChat extends FSMState
|
||||||
|
|
||||||
|
object HistoryChat extends FSMState
|
||||||
|
|
||||||
|
object VisitsChat extends FSMState
|
||||||
|
|
||||||
|
object SettingsChat extends FSMState
|
||||||
|
|
||||||
|
object BugChat extends FSMState
|
||||||
|
|
||||||
|
object Init
|
||||||
|
|
||||||
|
val MonitoringId: Regex = s"/reserve_(\\d+)_(\\d+)_(\\d+)".r
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
155
server/src/main/scala/com/lbs/server/actor/DatePicker.scala
Normal file
155
server/src/main/scala/com/lbs/server/actor/DatePicker.scala
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import java.time.format.TextStyle
|
||||||
|
import java.time.{LocalTime, ZonedDateTime}
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.DatePicker._
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date picker Inline Keyboard
|
||||||
|
*
|
||||||
|
* ⬆ ⬆ ⬆
|
||||||
|
* dd MM yyyy
|
||||||
|
* ⬇ ⬇ ⬇
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DatePicker(val userId: UserId, val bot: Bot, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, ZonedDateTime] with Localizable {
|
||||||
|
|
||||||
|
startWith(AwaitMode, null)
|
||||||
|
|
||||||
|
private var mode: Mode = DateFromMode
|
||||||
|
|
||||||
|
whenSafe(AwaitMode) {
|
||||||
|
case Event(newMode: Mode, _) =>
|
||||||
|
mode = newMode
|
||||||
|
goto(RequestDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestDate) {
|
||||||
|
case Event(initialDate: ZonedDateTime, _) =>
|
||||||
|
val message = mode match {
|
||||||
|
case DateFromMode => lang.chooseDateFrom
|
||||||
|
case DateToMode => lang.chooseDateTo
|
||||||
|
}
|
||||||
|
bot.sendMessage(userId.source, message, inlineKeyboard = dateButtons(initialDate))
|
||||||
|
goto(AwaitDate) using initialDate
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDate) {
|
||||||
|
case Event(Command(_, msg, Some(Tags.Done)), finalDate: ZonedDateTime) =>
|
||||||
|
|
||||||
|
val (message, updatedDate) = mode match {
|
||||||
|
case DateFromMode =>
|
||||||
|
val startOfTheDay = finalDate.`with`(LocalTime.MIN)
|
||||||
|
val dateFrom = if (startOfTheDay.isBefore(ZonedDateTime.now())) finalDate else startOfTheDay
|
||||||
|
lang.dateFromIs(dateFrom) -> dateFrom
|
||||||
|
case DateToMode =>
|
||||||
|
val dateTo = finalDate.`with`(LocalTime.MAX).minusHours(2)
|
||||||
|
lang.dateToIs(dateTo) -> dateTo
|
||||||
|
}
|
||||||
|
bot.sendEditMessage(userId.source, msg.messageId, message)
|
||||||
|
originator ! updatedDate
|
||||||
|
goto(AwaitMode) using null
|
||||||
|
|
||||||
|
case Event(Command(_, msg, Some(tag)), date: ZonedDateTime) =>
|
||||||
|
val modifiedDate = modifyDate(date, tag)
|
||||||
|
bot.sendEditMessage(userId.source, msg.messageId, inlineKeyboard = dateButtons(modifiedDate))
|
||||||
|
stay() using modifiedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
goto(AwaitMode) using null
|
||||||
|
case e: Event =>
|
||||||
|
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
private def modifyDate(date: ZonedDateTime, tag: String) = {
|
||||||
|
val dateModifier = tag match {
|
||||||
|
case Tags.DayUp => date.plusDays _
|
||||||
|
case Tags.MonthUp => date.plusMonths _
|
||||||
|
case Tags.YearUp => date.plusYears _
|
||||||
|
case Tags.DayDown => date.minusDays _
|
||||||
|
case Tags.MonthDown => date.minusMonths _
|
||||||
|
case Tags.YearDown => date.minusYears _
|
||||||
|
}
|
||||||
|
dateModifier(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def dateButtons(date: ZonedDateTime) = {
|
||||||
|
val day = date.getDayOfMonth.toString
|
||||||
|
val dayOfWeek = date.getDayOfWeek.getDisplayName(TextStyle.SHORT, lang.locale)
|
||||||
|
val month = date.getMonth.getDisplayName(TextStyle.SHORT, lang.locale)
|
||||||
|
val year = date.getYear.toString
|
||||||
|
|
||||||
|
createInlineKeyboard(Seq(
|
||||||
|
Seq(Button("⬆", Tags.DayUp), Button("⬆", Tags.MonthUp), Button("⬆", Tags.YearUp)),
|
||||||
|
Seq(Button(s"$day ($dayOfWeek)"), Button(month), Button(year)),
|
||||||
|
Seq(Button("⬇", Tags.DayDown), Button("⬇", Tags.MonthDown), Button("⬇", Tags.YearDown)),
|
||||||
|
Seq(Button("Done", Tags.Done))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DatePicker {
|
||||||
|
def props(userId: UserId, bot: Bot, localization: Localization, originator: ActorRef): Props =
|
||||||
|
Props(classOf[DatePicker], userId, bot, localization, originator)
|
||||||
|
|
||||||
|
object RequestDate extends FSMState
|
||||||
|
|
||||||
|
object AwaitDate extends FSMState
|
||||||
|
|
||||||
|
object AwaitMode extends FSMState
|
||||||
|
|
||||||
|
trait Mode
|
||||||
|
|
||||||
|
object DateFromMode extends Mode
|
||||||
|
|
||||||
|
object DateToMode extends Mode
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val DayUp = "day_up"
|
||||||
|
val MonthUp = "month_up"
|
||||||
|
val YearUp = "year_up"
|
||||||
|
val DayDown = "day_down"
|
||||||
|
val MonthDown = "month_down"
|
||||||
|
val YearDown = "year_down"
|
||||||
|
val Done = "done"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
26
server/src/main/scala/com/lbs/server/actor/FSMData.scala
Normal file
26
server/src/main/scala/com/lbs/server/actor/FSMData.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
trait FSMData
|
||||||
26
server/src/main/scala/com/lbs/server/actor/FSMState.scala
Normal file
26
server/src/main/scala/com/lbs/server/actor/FSMState.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
trait FSMState
|
||||||
50
server/src/main/scala/com/lbs/server/actor/Help.scala
Normal file
50
server/src/main/scala/com/lbs/server/actor/Help.scala
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.Command
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
|
||||||
|
class Help(val userId: UserId, bot: Bot, val localization: Localization) extends Actor with Localizable {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case _: Command =>
|
||||||
|
bot.sendMessage(userId.source, lang.help)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Help {
|
||||||
|
def props(userId: UserId, bot: Bot, localization: Localization): Props = Props(classOf[Help], userId, bot, localization)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
97
server/src/main/scala/com/lbs/server/actor/History.scala
Normal file
97
server/src/main/scala/com/lbs/server/actor/History.scala
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.api.json.model.HistoricVisit
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.Command
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.History.{AwaitPage, RequestData}
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.service.ApiService
|
||||||
|
|
||||||
|
class History(val userId: UserId, bot: Bot, apiService: ApiService, val localization: Localization, historyPagerActorFactory: (UserId, ActorRef) => ActorRef) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||||
|
|
||||||
|
private val historyPager = historyPagerActorFactory(userId, self)
|
||||||
|
|
||||||
|
startWith(RequestData, null)
|
||||||
|
|
||||||
|
whenSafe(RequestData) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
val visits = apiService.visitsHistory(userId.userId)
|
||||||
|
historyPager ! visits
|
||||||
|
goto(AwaitPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitPage) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
historyPager ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(Pager.NoItemsFound, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.visitsHistoryIsEmpty)
|
||||||
|
goto(RequestData)
|
||||||
|
case Event(_: HistoricVisit, _) =>
|
||||||
|
goto(RequestData) using null
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
invokeNext()
|
||||||
|
historyPager ! Init
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
historyPager ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object History {
|
||||||
|
def props(userId: UserId, bot: Bot, apiService: ApiService, localization: Localization, historyPagerActorFactory: (UserId, ActorRef) => ActorRef): Props =
|
||||||
|
Props(classOf[History], userId, bot, apiService, localization, historyPagerActorFactory)
|
||||||
|
|
||||||
|
object RequestData extends FSMState
|
||||||
|
|
||||||
|
object AwaitPage extends FSMState
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
126
server/src/main/scala/com/lbs/server/actor/Login.scala
Normal file
126
server/src/main/scala/com/lbs/server/actor/Login.scala
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.{Command, MessageSource}
|
||||||
|
import com.lbs.bot.telegram.TelegramBot
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login._
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.service.{ApiService, DataService}
|
||||||
|
import com.lbs.server.util.MessageExtractors
|
||||||
|
import org.jasypt.util.text.TextEncryptor
|
||||||
|
|
||||||
|
class Login(source: MessageSource, bot: Bot, dataService: DataService, apiService: ApiService, textEncryptor: TextEncryptor, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||||
|
|
||||||
|
protected var userId: UserId = _
|
||||||
|
|
||||||
|
startWith(LogIn, LoginData())
|
||||||
|
|
||||||
|
private var forwardCommand: ForwardCommand = _
|
||||||
|
|
||||||
|
whenSafe(LogIn) {
|
||||||
|
case Event(cmd: Command, LoginData(None, None)) =>
|
||||||
|
forwardCommand = ForwardCommand(cmd)
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestUsername)
|
||||||
|
case Event(_, LoginData(Some(username), Some(password))) =>
|
||||||
|
val loginResult = apiService.login(username, password)
|
||||||
|
loginResult match {
|
||||||
|
case Left(error) =>
|
||||||
|
bot.sendMessage(source, error.getMessage)
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestUsername) using LoginData()
|
||||||
|
case Right(loggedIn) =>
|
||||||
|
val credentials = dataService.saveCredentials(source, username, password)
|
||||||
|
userId = UserId(credentials.userId, source)
|
||||||
|
apiService.addSession(credentials.userId, loggedIn.accessToken, loggedIn.tokenType)
|
||||||
|
bot.sendMessage(source, lang.loginAndPasswordAreOk)
|
||||||
|
originator ! LoggedIn(forwardCommand, credentials.userId)
|
||||||
|
stay() using null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestUsername) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
bot.sendMessage(source, lang.provideUsername)
|
||||||
|
goto(AwaitUsername)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitUsername) {
|
||||||
|
case Event(Command(_, MessageExtractors.TextOpt(username), _), loginData: LoginData) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestPassword) using loginData.copy(username = username)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestPassword) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
bot.sendMessage(source, lang.providePassword)
|
||||||
|
goto(AwaitPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitPassword) {
|
||||||
|
case Event(Command(_, MessageExtractors.TextOpt(password), _), loginData: LoginData) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(LogIn) using loginData.copy(password = password.map(textEncryptor.encrypt))
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
goto(LogIn) using LoginData()
|
||||||
|
case e: Event =>
|
||||||
|
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object Login {
|
||||||
|
|
||||||
|
def props(source: MessageSource, bot: Bot, dataService: DataService, apiService: ApiService, textEncryptor: TextEncryptor, localization: Localization, originator: ActorRef): Props =
|
||||||
|
Props(classOf[Login], source, bot, dataService, apiService, textEncryptor, localization, originator)
|
||||||
|
|
||||||
|
object LogIn extends FSMState
|
||||||
|
|
||||||
|
object RequestUsername extends FSMState
|
||||||
|
|
||||||
|
object AwaitUsername extends FSMState
|
||||||
|
|
||||||
|
object RequestPassword extends FSMState
|
||||||
|
|
||||||
|
object AwaitPassword extends FSMState
|
||||||
|
|
||||||
|
case class LoginData(username: Option[String] = None, password: Option[String] = None) extends FSMData
|
||||||
|
|
||||||
|
case class ForwardCommand(cmd: Command)
|
||||||
|
|
||||||
|
case class UserId(userId: Long, source: MessageSource)
|
||||||
|
|
||||||
|
case class LoggedIn(forwardCommand: ForwardCommand, userId: Long)
|
||||||
|
|
||||||
|
}
|
||||||
113
server/src/main/scala/com/lbs/server/actor/Monitorings.scala
Normal file
113
server/src/main/scala/com/lbs/server/actor/Monitorings.scala
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.bot._
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.Monitorings.{AwaitDecision, AwaitPage, RequestData, Tags}
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.repository.model.Monitoring
|
||||||
|
import com.lbs.server.service.MonitoringService
|
||||||
|
|
||||||
|
class Monitorings(val userId: UserId, bot: Bot, monitoringService: MonitoringService, val localization: Localization, monitoringsPagerActorFactory: (UserId, ActorRef) => ActorRef) extends SafeFSM[FSMState, Monitoring] with Localizable {
|
||||||
|
|
||||||
|
private val monitoringsPager = monitoringsPagerActorFactory(userId, self)
|
||||||
|
|
||||||
|
startWith(RequestData, null)
|
||||||
|
|
||||||
|
whenSafe(RequestData) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
val monitorings = monitoringService.getActiveMonitorings(userId.userId)
|
||||||
|
monitoringsPager ! Right[Throwable, Seq[Monitoring]](monitorings)
|
||||||
|
goto(AwaitPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitPage) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
monitoringsPager ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(Pager.NoItemsFound, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.noActiveMonitorings)
|
||||||
|
goto(RequestData)
|
||||||
|
case Event(monitoring: Monitoring, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.deactivateMonitoring(monitoring), inlineKeyboard =
|
||||||
|
createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
|
||||||
|
goto(AwaitDecision) using monitoring
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDecision) {
|
||||||
|
case Event(Command(_, _, Some(Tags.No)), _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.monitoringWasNotDeactivated)
|
||||||
|
goto(RequestData)
|
||||||
|
case Event(Command(_, _, Some(Tags.Yes)), monitoring: Monitoring) =>
|
||||||
|
monitoringService.deactivateMonitoring(monitoring.recordId)
|
||||||
|
bot.sendMessage(userId.source, lang.deactivated)
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
invokeNext()
|
||||||
|
monitoringsPager ! Init
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
monitoringsPager ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Monitorings {
|
||||||
|
def props(userId: UserId, bot: Bot, monitoringService: MonitoringService, localization: Localization, monitoringsPagerActorFactory: (UserId, ActorRef) => ActorRef): Props =
|
||||||
|
Props(classOf[Monitorings], userId, bot, monitoringService, localization, monitoringsPagerActorFactory)
|
||||||
|
|
||||||
|
object RequestData extends FSMState
|
||||||
|
|
||||||
|
object AwaitPage extends FSMState
|
||||||
|
|
||||||
|
object AwaitDecision extends FSMState
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val Yes = "yes"
|
||||||
|
val No = "no"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
132
server/src/main/scala/com/lbs/server/actor/Pager.scala
Normal file
132
server/src/main/scala/com/lbs/server/actor/Pager.scala
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.Pager.{Tags, _}
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.util.MessageExtractors
|
||||||
|
|
||||||
|
class Pager[Data](val userId: UserId, bot: Bot, makeMessage: (Data, Int, Int) => String,
|
||||||
|
makeHeader: (Int, Int) => String, selectionPrefix: Option[String],
|
||||||
|
val localization: Localization, originator: ActorRef)
|
||||||
|
extends SafeFSM[FSMState, FSMData] with Localizable with Logger {
|
||||||
|
|
||||||
|
private case class Page(page: Int, pages: Seq[Seq[Data]]) extends FSMData
|
||||||
|
|
||||||
|
private val Selection = s"/${selectionPrefix.getOrElse("")}_(\\d+)_(\\d+)".r
|
||||||
|
|
||||||
|
startWith(PrepareData, null)
|
||||||
|
|
||||||
|
whenSafe(PrepareData) {
|
||||||
|
case Event(Left(error: Throwable), _) =>
|
||||||
|
bot.sendMessage(userId.source, error.getMessage)
|
||||||
|
invokeNext()
|
||||||
|
goto(PrepareData)
|
||||||
|
case Event(Right(items: Seq[Data]), _) if items.isEmpty =>
|
||||||
|
originator ! NoItemsFound
|
||||||
|
goto(PrepareData) using null
|
||||||
|
case Event(Right(items: Seq[Data]), _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestData) using Page(0, items.grouped(Pager.PageSize).toList)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestData) {
|
||||||
|
case Event(Next, page: Page) =>
|
||||||
|
sendPage(page.page, page.pages)
|
||||||
|
goto(AwaitData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitData) {
|
||||||
|
case Event(Command(_, msg, Some(Tags.Next)), termsData: Page) =>
|
||||||
|
val page = termsData.page + 1
|
||||||
|
sendPage(page, termsData.pages, Some(msg.messageId))
|
||||||
|
stay() using termsData.copy(page = page)
|
||||||
|
case Event(Command(_, msg, Some(Tags.Previous)), termsData: Page) =>
|
||||||
|
val page = termsData.page - 1
|
||||||
|
sendPage(page, termsData.pages, Some(msg.messageId))
|
||||||
|
stay() using termsData.copy(page = page)
|
||||||
|
case Event(Command(_, MessageExtractors.Text(Selection(pageStr, indexStr)), _), termsData: Page) if selectionPrefix.nonEmpty =>
|
||||||
|
val page = pageStr.toInt
|
||||||
|
val index = indexStr.toInt
|
||||||
|
originator ! termsData.pages(page)(index)
|
||||||
|
goto(PrepareData) using null
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendPage(page: Int, data: Seq[Seq[Data]], messageId: Option[String] = None): Unit = {
|
||||||
|
val pages = data.length
|
||||||
|
val message = makeHeader(page, data.length) + "\n\n" + data(page).zipWithIndex.map { case (d, index) => makeMessage(d, page, index) }.mkString
|
||||||
|
|
||||||
|
val previousButton = if (page > 0) Some(Button(lang.previous, Tags.Previous)) else None
|
||||||
|
val nextButton = if (page >= 0 && page < pages - 1) Some(Button(lang.next, Tags.Next)) else None
|
||||||
|
val buttons = previousButton.toSeq ++ nextButton.toSeq
|
||||||
|
|
||||||
|
messageId match {
|
||||||
|
case Some(id) =>
|
||||||
|
bot.sendEditMessage(userId.source, id, message, inlineKeyboard = createInlineKeyboard(buttons))
|
||||||
|
case None =>
|
||||||
|
bot.sendMessage(userId.source, message, inlineKeyboard = createInlineKeyboard(buttons))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
goto(PrepareData) using null
|
||||||
|
case e: Event =>
|
||||||
|
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
object Pager {
|
||||||
|
|
||||||
|
def props[Data](userId: UserId, bot: Bot,
|
||||||
|
makeMessage: (Data, Int, Int) => String, makeHeader: (Int, Int) => String, dataPrefix: Option[String], localization: Localization, originator: ActorRef): Props =
|
||||||
|
Props(classOf[Pager[Data]], userId, bot, makeMessage, makeHeader, dataPrefix, localization, originator)
|
||||||
|
|
||||||
|
val PageSize = 5
|
||||||
|
|
||||||
|
object PrepareData extends FSMState
|
||||||
|
|
||||||
|
object RequestData extends FSMState
|
||||||
|
|
||||||
|
object AwaitData extends FSMState
|
||||||
|
|
||||||
|
object NoItemsFound
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val Previous = "previous"
|
||||||
|
val Next = "next"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
70
server/src/main/scala/com/lbs/server/actor/Pagers.scala
Normal file
70
server/src/main/scala/com/lbs/server/actor/Pagers.scala
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
import com.lbs.api.json.model.{AvailableVisitsTermPresentation, HistoricVisit, ReservedVisit}
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.repository.model
|
||||||
|
import com.lbs.server.repository.model.Monitoring
|
||||||
|
|
||||||
|
class Pagers(val userId: UserId, bot: Bot, val localization: Localization) extends Localizable {
|
||||||
|
|
||||||
|
def termsPagerProps(originator: ActorRef): Props =
|
||||||
|
Pager.props[AvailableVisitsTermPresentation](userId, bot,
|
||||||
|
(term: AvailableVisitsTermPresentation, page: Int, index: Int) => lang.termEntry(term, page, index),
|
||||||
|
(page: Int, pages: Int) => lang.termsHeader(page, pages),
|
||||||
|
Some("book"), localization, originator)
|
||||||
|
|
||||||
|
def historyPagerProps(originator: ActorRef): Props =
|
||||||
|
Pager.props[HistoricVisit](userId, bot,
|
||||||
|
(visit: HistoricVisit, page: Int, index: Int) => lang.historyEntry(visit, page, index),
|
||||||
|
(page: Int, pages: Int) => lang.historyHeader(page, pages),
|
||||||
|
Some("repeat"), localization, originator)
|
||||||
|
|
||||||
|
def visitsPagerProps(originator: ActorRef): Props =
|
||||||
|
Pager.props[ReservedVisit](userId, bot,
|
||||||
|
(visit: ReservedVisit, page: Int, index: Int) => lang.upcomingVisitEntry(visit, page, index),
|
||||||
|
(page: Int, pages: Int) => lang.upcomingVisitsHeader(page, pages),
|
||||||
|
Some("cancel"), localization, originator)
|
||||||
|
|
||||||
|
def bugPagerProps(originator: ActorRef): Props =
|
||||||
|
Pager.props[model.Bug](userId, bot,
|
||||||
|
(bug: model.Bug, page: Int, index: Int) => lang.bugEntry(bug, page, index),
|
||||||
|
(page: Int, pages: Int) => lang.bugsHeader(page, pages),
|
||||||
|
None, localization, originator)
|
||||||
|
|
||||||
|
def monitoringsPagerProps(originator: ActorRef): Props =
|
||||||
|
Pager.props[Monitoring](userId, bot,
|
||||||
|
(monitoring: Monitoring, page: Int, index: Int) => lang.monitoringEntry(monitoring, page, index),
|
||||||
|
(page: Int, pages: Int) => lang.monitoringsHeader(page, pages),
|
||||||
|
Some("cancel"), localization, originator)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object Pagers {
|
||||||
|
def apply(userId: UserId, bot: Bot, localization: Localization) = new Pagers(userId, bot, localization)
|
||||||
|
}
|
||||||
85
server/src/main/scala/com/lbs/server/actor/Router.scala
Normal file
85
server/src/main/scala/com/lbs/server/actor/Router.scala
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, PoisonPill, Props}
|
||||||
|
import com.lbs.bot.model.{Command, MessageSource}
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
import com.lbs.server.actor.Router.DestroyChat
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.concurrent.ExecutionContextExecutor
|
||||||
|
import scala.concurrent.duration.DurationLong
|
||||||
|
|
||||||
|
class Router(authActorFactory: MessageSource => ActorRef) extends Actor with Logger {
|
||||||
|
|
||||||
|
private val chats = mutable.Map.empty[MessageSource, ActorRef]
|
||||||
|
|
||||||
|
private val timers = mutable.Map.empty[MessageSource, Cancellable]
|
||||||
|
|
||||||
|
private val idleTimeout = 1.hour
|
||||||
|
|
||||||
|
private implicit val dispatcher: ExecutionContextExecutor = context.system.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = {
|
||||||
|
case cmd@Command(source, _, _) =>
|
||||||
|
scheduleIdleChatDestroyer(source)
|
||||||
|
val chat = chats.get(source) match {
|
||||||
|
case Some(actor) => actor
|
||||||
|
case None =>
|
||||||
|
val actor = authActorFactory(source)
|
||||||
|
chats += source -> actor
|
||||||
|
actor
|
||||||
|
}
|
||||||
|
chat ! cmd
|
||||||
|
case DestroyChat(source) =>
|
||||||
|
destroyChat(source)
|
||||||
|
case what => LOG.info(s"Unknown message: $what")
|
||||||
|
}
|
||||||
|
|
||||||
|
private def destroyChat(source: MessageSource): Unit = {
|
||||||
|
LOG.info(s"Destroying chat for $source due to $idleTimeout inactivity")
|
||||||
|
timers.remove(source)
|
||||||
|
chats.remove(source).foreach(_ ! PoisonPill)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def scheduleIdleChatDestroyer(source: MessageSource): Unit = {
|
||||||
|
timers.remove(source).foreach(_.cancel())
|
||||||
|
val cancellable = context.system.scheduler.scheduleOnce(idleTimeout) {
|
||||||
|
self ! DestroyChat(source)
|
||||||
|
}
|
||||||
|
timers += source -> cancellable
|
||||||
|
}
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
chats.foreach(_._2 ! PoisonPill)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Router {
|
||||||
|
def props(authActorFactory: MessageSource => ActorRef) = Props(classOf[Router], authActorFactory)
|
||||||
|
|
||||||
|
case class DestroyChat(source: MessageSource)
|
||||||
|
|
||||||
|
}
|
||||||
57
server/src/main/scala/com/lbs/server/actor/SafeFSM.scala
Normal file
57
server/src/main/scala/com/lbs/server/actor/SafeFSM.scala
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.FSM
|
||||||
|
import com.lbs.common.Logger
|
||||||
|
|
||||||
|
trait SafeFSM[S, D] extends FSM[S, D] with Logger {
|
||||||
|
|
||||||
|
protected val defaultEventHandler: StateFunction = {
|
||||||
|
case e: Event =>
|
||||||
|
LOG.warn(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected var eventHandler: StateFunction = defaultEventHandler
|
||||||
|
|
||||||
|
protected def whenSafe(state: S)(stateFunction: StateFunction): Unit = {
|
||||||
|
when(state) {
|
||||||
|
case event: Event =>
|
||||||
|
try {
|
||||||
|
if (stateFunction.isDefinedAt(event)) stateFunction(event)
|
||||||
|
else eventHandler(event)
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
LOG.error(s"Exception occurred while processing event $event", e)
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def whenUnhandledSafe(stateFunction: StateFunction): Unit = {
|
||||||
|
whenUnhandled(stateFunction)
|
||||||
|
eventHandler = stateFunction orElse defaultEventHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
103
server/src/main/scala/com/lbs/server/actor/Settings.scala
Normal file
103
server/src/main/scala/com/lbs/server/actor/Settings.scala
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.Props
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.Settings._
|
||||||
|
import com.lbs.server.lang.{Lang, Localizable, Localization}
|
||||||
|
import com.lbs.server.service.DataService
|
||||||
|
|
||||||
|
class Settings(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization) extends SafeFSM[FSMState, FSMData] with Localizable {
|
||||||
|
|
||||||
|
startWith(RequestAction, null)
|
||||||
|
|
||||||
|
whenSafe(RequestAction) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.settingsHeader, inlineKeyboard =
|
||||||
|
createInlineKeyboard(Seq(Button(lang.language, Tags.Language))))
|
||||||
|
goto(AwaitAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitAction) {
|
||||||
|
case Event(Command(_, _, Some(Tags.Language)), _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.chooseLanguage,
|
||||||
|
inlineKeyboard = createInlineKeyboard(Lang.Langs.map(l => Button(l.label, l.id)), columns = 1))
|
||||||
|
goto(AwaitLanguage)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitLanguage) {
|
||||||
|
case Event(Command(_, _, Some(langIdStr)), _) =>
|
||||||
|
val langId = langIdStr.toInt
|
||||||
|
localization.updateLanguage(userId.userId, Lang(langId))
|
||||||
|
bot.sendMessage(userId.source, lang.languageUpdated)
|
||||||
|
goto(RequestAction) using null
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
object Settings {
|
||||||
|
def props(userId: UserId, bot: Bot, dataService: DataService, localization: Localization): Props =
|
||||||
|
Props(classOf[Settings], userId, bot, dataService, localization)
|
||||||
|
|
||||||
|
object AwaitLanguage extends FSMState
|
||||||
|
|
||||||
|
object RequestAction extends FSMState
|
||||||
|
|
||||||
|
object AwaitAction extends FSMState
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val Language = "language"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
133
server/src/main/scala/com/lbs/server/actor/StaticData.scala
Normal file
133
server/src/main/scala/com/lbs/server/actor/StaticData.scala
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
import com.lbs.api.json.model.IdName
|
||||||
|
import com.lbs.bot.model.{Button, Command, TaggedButton}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.StaticData._
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
|
||||||
|
class StaticData(val userId: UserId, bot: Bot, val localization: Localization, originator: ActorRef) extends SafeFSM[FSMState, IdName] with Localizable {
|
||||||
|
|
||||||
|
private def anySelectOption: List[TaggedButton] = if (config.isAnyAllowed) List(Button(lang.any, -1L)) else List()
|
||||||
|
|
||||||
|
startWith(AwaitConfig, null)
|
||||||
|
|
||||||
|
private var config: StaticDataConfig = _
|
||||||
|
|
||||||
|
private var callbackTags: List[TaggedButton] = List()
|
||||||
|
|
||||||
|
whenSafe(AwaitConfig) {
|
||||||
|
case Event(newConfig: StaticDataConfig, _) =>
|
||||||
|
config = newConfig
|
||||||
|
invokeNext()
|
||||||
|
goto(RequestStaticData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(RequestStaticData) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
originator ! LatestOptions
|
||||||
|
stay()
|
||||||
|
case Event(LatestOptions(options), _) if options.isEmpty =>
|
||||||
|
callbackTags = anySelectOption
|
||||||
|
bot.sendMessage(userId.source, lang.pleaseEnterStaticDataNameOrAny(config),
|
||||||
|
inlineKeyboard = createInlineKeyboard(callbackTags))
|
||||||
|
goto(AwaitStaticData)
|
||||||
|
case Event(LatestOptions(options), _) if options.nonEmpty =>
|
||||||
|
callbackTags = anySelectOption ++ options.map(data => Button(data.name, data.id))
|
||||||
|
bot.sendMessage(userId.source, lang.pleaseEnterStaticDataNameOrPrevious(config),
|
||||||
|
inlineKeyboard = createInlineKeyboard(callbackTags, columns = 1))
|
||||||
|
goto(AwaitStaticData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitStaticData) {
|
||||||
|
case Event(Command(_, msg, Some(tag)), _) =>
|
||||||
|
val id = tag.toLong
|
||||||
|
val label = callbackTags.find(_.tag == tag).map(_.label).getOrElse(sys.error("Unable to get callback tag label"))
|
||||||
|
bot.sendEditMessage(userId.source, msg.messageId, lang.staticDataIs(config, label))
|
||||||
|
originator ! IdName(id, label)
|
||||||
|
goto(AwaitConfig)
|
||||||
|
|
||||||
|
case Event(Command(_, msg, _), _) =>
|
||||||
|
val searchText = msg.text.get.toLowerCase
|
||||||
|
originator ! FindOptions(searchText)
|
||||||
|
stay()
|
||||||
|
|
||||||
|
case Event(FoundOptions(Right(options)), _) if options.nonEmpty =>
|
||||||
|
callbackTags = anySelectOption ::: options.map(c => Button(c.name, c.id))
|
||||||
|
bot.sendMessage(userId.source, lang.pleaseChooseStaticDataNameOrAny(config),
|
||||||
|
inlineKeyboard = createInlineKeyboard(callbackTags, columns = 1))
|
||||||
|
stay()
|
||||||
|
|
||||||
|
case Event(FoundOptions(Right(options)), _) if options.isEmpty =>
|
||||||
|
callbackTags = anySelectOption
|
||||||
|
bot.sendMessage(userId.source, lang.staticNotFound(config), inlineKeyboard = createInlineKeyboard(callbackTags))
|
||||||
|
stay()
|
||||||
|
|
||||||
|
case Event(FoundOptions(Left(ex)), _) =>
|
||||||
|
bot.sendMessage(userId.source, ex.getMessage)
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
goto(AwaitConfig) using null
|
||||||
|
case e: Event =>
|
||||||
|
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
|
||||||
|
stay()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
object StaticData {
|
||||||
|
def props(userId: UserId, bot: Bot, localization: Localization, originator: ActorRef): Props =
|
||||||
|
Props(classOf[StaticData], userId, bot, localization, originator)
|
||||||
|
|
||||||
|
object AwaitConfig extends FSMState
|
||||||
|
|
||||||
|
object RequestStaticData extends FSMState
|
||||||
|
|
||||||
|
object AwaitStaticData extends FSMState
|
||||||
|
|
||||||
|
case class StaticDataConfig(name: String, example: String, isAnyAllowed: Boolean)
|
||||||
|
|
||||||
|
object LatestOptions
|
||||||
|
|
||||||
|
case class LatestOptions(options: Seq[IdName])
|
||||||
|
|
||||||
|
case class FindOptions(searchText: String)
|
||||||
|
|
||||||
|
case class FoundOptions(option: Either[Throwable, List[IdName]])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import com.lbs.api.json.model.IdName
|
||||||
|
import com.lbs.bot.model.Command
|
||||||
|
import com.lbs.server.actor.Book.BookingData
|
||||||
|
import com.lbs.server.actor.StaticData.{FindOptions, FoundOptions, LatestOptions, StaticDataConfig}
|
||||||
|
|
||||||
|
trait StaticDataForBooking extends SafeFSM[FSMState, FSMData] {
|
||||||
|
|
||||||
|
protected def staticData: ActorRef
|
||||||
|
|
||||||
|
protected def withFunctions(latestOptions: => Seq[IdName], staticOptions: => Either[Throwable, List[IdName]], applyId: IdName => BookingData): FSMState => StateFunction = {
|
||||||
|
nextState: FSMState => {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
staticData ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(LatestOptions, _) =>
|
||||||
|
staticData ! LatestOptions(latestOptions)
|
||||||
|
stay()
|
||||||
|
case Event(FindOptions(searchText), _) =>
|
||||||
|
staticData ! FoundOptions(filterOptions(staticOptions, searchText))
|
||||||
|
stay()
|
||||||
|
case Event(id: IdName, _) =>
|
||||||
|
invokeNext()
|
||||||
|
goto(nextState) using applyId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def requestStaticData(requestState: FSMState, awaitState: FSMState, staticDataConfig: => StaticDataConfig)(functions: BookingData => FSMState => StateFunction)(requestNext: FSMState): Unit = {
|
||||||
|
whenSafe(requestState) {
|
||||||
|
case Event(_, _) =>
|
||||||
|
staticData ! staticDataConfig
|
||||||
|
goto(awaitState)
|
||||||
|
}
|
||||||
|
whenSafe(awaitState) {
|
||||||
|
case event@Event(_, bookingData: BookingData) =>
|
||||||
|
val fn = functions(bookingData)(requestNext)
|
||||||
|
if (fn.isDefinedAt(event)) fn(event) else eventHandler(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def filterOptions(options: Either[Throwable, List[IdName]], searchText: String) = {
|
||||||
|
options.map(opt => opt.filter(c => c.name.toLowerCase.contains(searchText)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.{Command, MessageSource}
|
||||||
|
import com.lbs.server.lang.En
|
||||||
|
|
||||||
|
class UnauthorizedHelp(source: MessageSource, bot: Bot) extends Actor {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case _: Command =>
|
||||||
|
bot.sendMessage(source, En.help)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object UnauthorizedHelp {
|
||||||
|
def props(source: MessageSource, bot: Bot): Props = Props(classOf[UnauthorizedHelp], source, bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
119
server/src/main/scala/com/lbs/server/actor/Visits.scala
Normal file
119
server/src/main/scala/com/lbs/server/actor/Visits.scala
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.actor
|
||||||
|
|
||||||
|
import akka.actor.{ActorRef, PoisonPill, Props}
|
||||||
|
import com.lbs.api.json.model.ReservedVisit
|
||||||
|
import com.lbs.bot.model.{Button, Command}
|
||||||
|
import com.lbs.bot.{Bot, _}
|
||||||
|
import com.lbs.server.actor.Chat.Init
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
import com.lbs.server.actor.Visits.{AwaitDecision, AwaitPage, RequestData, Tags}
|
||||||
|
import com.lbs.server.lang.{Localizable, Localization}
|
||||||
|
import com.lbs.server.service.ApiService
|
||||||
|
|
||||||
|
class Visits(val userId: UserId, bot: Bot, apiService: ApiService, val localization: Localization,
|
||||||
|
visitsPagerActorFactory: (UserId, ActorRef) => ActorRef) extends SafeFSM[FSMState, ReservedVisit] with Localizable {
|
||||||
|
|
||||||
|
private val reservedVisitsPager = visitsPagerActorFactory(userId, self)
|
||||||
|
|
||||||
|
startWith(RequestData, null)
|
||||||
|
|
||||||
|
whenSafe(RequestData) {
|
||||||
|
case Event(Next, _) =>
|
||||||
|
val visits = apiService.reservedVisits(userId.userId)
|
||||||
|
reservedVisitsPager ! visits
|
||||||
|
goto(AwaitPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitPage) {
|
||||||
|
case Event(cmd: Command, _) =>
|
||||||
|
reservedVisitsPager ! cmd
|
||||||
|
stay()
|
||||||
|
case Event(Pager.NoItemsFound, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.noUpcomingVisits)
|
||||||
|
goto(RequestData)
|
||||||
|
case Event(visit: ReservedVisit, _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.areYouSureToCancelAppointment(visit),
|
||||||
|
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
|
||||||
|
goto(AwaitDecision) using visit
|
||||||
|
}
|
||||||
|
|
||||||
|
whenSafe(AwaitDecision) {
|
||||||
|
case Event(Command(_, _, Some(Tags.No)), _) =>
|
||||||
|
bot.sendMessage(userId.source, lang.appointmentWasNotCancelled)
|
||||||
|
goto(RequestData)
|
||||||
|
case Event(Command(_, _, Some(Tags.Yes)), visit: ReservedVisit) =>
|
||||||
|
apiService.deleteReservation(userId.userId, visit.reservationId) match {
|
||||||
|
case Left(ex) => bot.sendMessage(userId.source, lang.unableToCancelUpcomingVisit(ex.getMessage))
|
||||||
|
case Right(r) => bot.sendMessage(userId.source, lang.appointmentHasBeenCancelled)
|
||||||
|
}
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
whenUnhandledSafe {
|
||||||
|
case Event(Init, _) =>
|
||||||
|
invokeNext()
|
||||||
|
reservedVisitsPager ! Init
|
||||||
|
goto(RequestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
reservedVisitsPager ! PoisonPill
|
||||||
|
super.postStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Visits {
|
||||||
|
def props(userId: UserId, bot: Bot, apiService: ApiService, localization: Localization,
|
||||||
|
visitsPagerActorFactory: (UserId, ActorRef) => ActorRef): Props =
|
||||||
|
Props(classOf[Visits], userId, bot, apiService, localization, visitsPagerActorFactory)
|
||||||
|
|
||||||
|
object RequestData extends FSMState
|
||||||
|
|
||||||
|
object AwaitPage extends FSMState
|
||||||
|
|
||||||
|
object AwaitDecision extends FSMState
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
val Yes = "yes"
|
||||||
|
val No = "no"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
34
server/src/main/scala/com/lbs/server/actor/package.scala
Normal file
34
server/src/main/scala/com/lbs/server/actor/package.scala
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
|
||||||
|
package object actor {
|
||||||
|
def invokeNext()(implicit self: ActorRef): Unit = {
|
||||||
|
self ! Next
|
||||||
|
}
|
||||||
|
|
||||||
|
object Next
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.exception
|
||||||
|
|
||||||
|
case class UserNotFoundException(chatId: Long) extends Exception(s"Luxmed username for char with id $chatId")
|
||||||
351
server/src/main/scala/com/lbs/server/lang/En.scala
Normal file
351
server/src/main/scala/com/lbs/server/lang/En.scala
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.lang
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
import com.lbs.api.json.model.{AvailableVisitsTermPresentation, HistoricVisit, ReservedVisit, ValuationsResponse}
|
||||||
|
import com.lbs.server.actor.Book
|
||||||
|
import com.lbs.server.actor.StaticData.StaticDataConfig
|
||||||
|
import com.lbs.server.repository.model.{Bug, Monitoring}
|
||||||
|
import com.lbs.server.util.DateTimeUtil.{formatDate, formatDateTime, minutesSinceBeginOf2018}
|
||||||
|
|
||||||
|
object En extends Lang {
|
||||||
|
|
||||||
|
override def id: Int = 0
|
||||||
|
|
||||||
|
override def locale: Locale = Locale.ENGLISH
|
||||||
|
|
||||||
|
override def label: String = "🇺🇸English"
|
||||||
|
|
||||||
|
override protected def withPages(message: String, page: Int, pages: Int): String = {
|
||||||
|
if (pages > 1) s"$message. Page <b>${page + 1}</b> of <b>$pages</b>"
|
||||||
|
else message
|
||||||
|
}
|
||||||
|
|
||||||
|
override def unableToCancelUpcomingVisit(reason: String): String =
|
||||||
|
s"⚠ Unable to cancel upcoming visit! Reason: $reason"
|
||||||
|
|
||||||
|
override def appointmentHasBeenCancelled: String =
|
||||||
|
s"👍 Your appointment has been cancelled!"
|
||||||
|
|
||||||
|
override def yes: String = "Yes"
|
||||||
|
|
||||||
|
override def no: String = "No"
|
||||||
|
|
||||||
|
override def noUpcomingVisits: String =
|
||||||
|
"ℹ No upcoming visits found"
|
||||||
|
|
||||||
|
override def areYouSureToCancelAppointment(visit: ReservedVisit): String =
|
||||||
|
s"""<b>➡</b> Are you sure want to cancel appointment?
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinic.name}
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def chooseDateFrom: String = "<b>➡</b> Please choose date from"
|
||||||
|
|
||||||
|
override def chooseDateTo: String = "<b>➡</b> Please choose date to"
|
||||||
|
|
||||||
|
override def findTerms: String = "🔍 Find terms"
|
||||||
|
|
||||||
|
override def modifyDate: String = "📅 Modify date"
|
||||||
|
|
||||||
|
override def bookingSummary(bookingData: Book.BookingData): String =
|
||||||
|
s"🦄 Ok! We are going to book a service <b>${bookingData.serviceId.name}</b>" +
|
||||||
|
s" with a doctor chosen <b>${bookingData.doctorId.name}</b>" +
|
||||||
|
s" in <b>${bookingData.clinicId.name}</b> clinic" +
|
||||||
|
s" of the <b>${bookingData.cityId.name}</b> city." +
|
||||||
|
s"\nDesired dates: <b>${formatDate(bookingData.dateFrom, locale)}</b> -> <b>${formatDate(bookingData.dateTo, locale)}</b>" +
|
||||||
|
s"\nTime: <b>${timeOfDay(bookingData.timeOfDay)}</b>" +
|
||||||
|
s"\n\n<b>➡</b> Now choose your action"
|
||||||
|
|
||||||
|
override def noTermsFound: String =
|
||||||
|
s"""ℹ No available terms found
|
||||||
|
|
|
||||||
|
|What do you want to do next?""".stripMargin
|
||||||
|
|
||||||
|
override def createMonitoring: String = "👀 Create monitoring"
|
||||||
|
|
||||||
|
override def cancel: String = "Cancel"
|
||||||
|
|
||||||
|
override def book: String = "Book"
|
||||||
|
|
||||||
|
override def confirmAppointment(term: AvailableVisitsTermPresentation, valuations: ValuationsResponse): String =
|
||||||
|
|
||||||
|
s"""<b>➡</b> ${valuations.optionsQuestion.getOrElse("Would you like to confirm your appointment booking?")}
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|
|
||||||
|
|ℹ${valuations.visitTermVariants.head.infoMessage}""".stripMargin
|
||||||
|
|
||||||
|
override def appointmentIsConfirmed: String = "👍 Your appointment has been confirmed!"
|
||||||
|
|
||||||
|
override def monitoringHasBeenCreated: String = "👍 Monitoring has been created! List of active /monitorings"
|
||||||
|
|
||||||
|
override def unableToCreateMonitoring: String = s"👎 Unable to create monitoring. Please create a /bug"
|
||||||
|
|
||||||
|
override def chooseTypeOfMonitoring: String = "<b>➡</b> Please choose type of monitoring you want"
|
||||||
|
|
||||||
|
override def bookByApplication: String = "👾 Book by application"
|
||||||
|
|
||||||
|
override def bookManually: String = "👤 Book manually"
|
||||||
|
|
||||||
|
override def city: String = "city"
|
||||||
|
|
||||||
|
override def clinic: String = "clinic"
|
||||||
|
|
||||||
|
override def service: String = "service"
|
||||||
|
|
||||||
|
override def doctor: String = "doctor"
|
||||||
|
|
||||||
|
override def previous: String = "Previous"
|
||||||
|
|
||||||
|
override def next: String = "Next"
|
||||||
|
|
||||||
|
override def noActiveMonitorings: String = "ℹ You don't have active monitorings. Create new one /book"
|
||||||
|
|
||||||
|
override def deactivateMonitoring(monitoring: Monitoring): String =
|
||||||
|
s"""<b>➡</b> Are you sure want to deactivate monitoring?
|
||||||
|
|
|
||||||
|
|📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}""".stripMargin
|
||||||
|
|
||||||
|
override def deactivated: String = "👍 Deactivated! List of active /monitorings"
|
||||||
|
|
||||||
|
override def any: String = "Any"
|
||||||
|
|
||||||
|
override def pressAny: String = s"or press <b>$any</b> button"
|
||||||
|
|
||||||
|
override def pleaseEnterStaticDataNameOrAny(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(
|
||||||
|
s"""<b>➡</b> Please enter a ${config.name} name
|
||||||
|
|For example: <b>${config.example}</b>""".stripMargin,
|
||||||
|
config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def pleaseEnterStaticDataNameOrPrevious(config: StaticDataConfig): String =
|
||||||
|
s"""<b>➡</b> Please enter a ${config.name} name
|
||||||
|
|For example: <b>${config.example}</b>
|
||||||
|
|
|
||||||
|
|or choose a ${config.name} from previous searches""".stripMargin
|
||||||
|
|
||||||
|
override def staticDataIs(config: StaticDataConfig, label: String): String =
|
||||||
|
s"<b>✅</b> ${capitalizeFirstLetter(config.name)} is <b>$label</b>"
|
||||||
|
|
||||||
|
override def pleaseChooseStaticDataNameOrAny(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(s"<b>➡</b> Please choose a ${config.name}", config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def staticNotFound(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(
|
||||||
|
s"""<b>➡</b> Nothing was found 😔
|
||||||
|
|Please enter a ${config.name} name again""", config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def loginAndPasswordAreOk: String =
|
||||||
|
s"""✅ Congrats! Login and password are OK!
|
||||||
|
|Now you can change the language /settings
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
override def provideUsername: String =
|
||||||
|
s"""ℹ You must be logged in using your <b>Luxmed</b> credentials
|
||||||
|
|
|
||||||
|
|<b>➡</b> Please provide username""".stripMargin
|
||||||
|
|
||||||
|
override def providePassword: String = "<b>➡</b> Please provide password"
|
||||||
|
|
||||||
|
override def visitsHistoryIsEmpty: String = "ℹ No visits in your history"
|
||||||
|
|
||||||
|
override def help: String =
|
||||||
|
s"""ℹ This is non official bot for Portal Pacienta LUX MED.
|
||||||
|
|With its help you can book a visit to the doctor, create term monitorings, view upcoming visits and visit history
|
||||||
|
|
|
||||||
|
|<b>➡</b> Supported commands
|
||||||
|
|/login - enter Luxmed credentials
|
||||||
|
|/book - make an appointment
|
||||||
|
|/monitorings - available terms monitoring
|
||||||
|
|/history - visits history
|
||||||
|
|/visits - upcoming visits
|
||||||
|
|/settings - change language
|
||||||
|
|/bug - submit an issue""".stripMargin
|
||||||
|
|
||||||
|
override def dateFromIs(dateFrom: ZonedDateTime): String = s"📅 Date from is ${formatDate(dateFrom, locale)}"
|
||||||
|
|
||||||
|
override def dateToIs(dateTo: ZonedDateTime): String = s"📅 Date to is ${formatDate(dateTo, locale)}"
|
||||||
|
|
||||||
|
override def termEntry(term: AvailableVisitsTermPresentation, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|<b>➡</b> /book_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def termsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Available terms", page, pages)
|
||||||
|
|
||||||
|
override def historyEntry(visit: HistoricVisit, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinicName}
|
||||||
|
|<b>➡</b> /repeat_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def historyHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Conducted visits", page, pages)
|
||||||
|
|
||||||
|
override def upcomingVisitEntry(visit: ReservedVisit, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinic.name}
|
||||||
|
|<b>➡</b> /cancel_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def upcomingVisitsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Reserved visits", page, pages)
|
||||||
|
|
||||||
|
override def bugEntry(bug: Bug, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(bug.submitted, locale)}</b>
|
||||||
|
|Description: ${bug.details}
|
||||||
|
|State: <b>${if (bug.resolved) "✅ Resolved" else "🚫 Unresolved"}</b>
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def bugsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Submitted issues", page, pages)
|
||||||
|
|
||||||
|
override def monitoringEntry(monitoring: Monitoring, page: Int, index: Int): String =
|
||||||
|
s"""📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|Type: ${if (monitoring.autobook) "Auto" else "Manual"}
|
||||||
|
|<b>➡</b> /cancel_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def monitoringsHeader(page: Int, pages: Int): String =
|
||||||
|
s"<b>➡</b> Active monitorings."
|
||||||
|
|
||||||
|
override def invalidLoginOrPassword: String =
|
||||||
|
"""❗ You have entered invalid login or password or changed it via site.
|
||||||
|
|Your monitorings were removed. Please /login again and create new monitorings.
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
override def availableTermEntry(term: AvailableVisitsTermPresentation, monitoring: Monitoring, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|/reserve_${monitoring.recordId}_${term.scheduleId}_${minutesSinceBeginOf2018(term.visitDate.startDateTime)}
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def availableTermsHeader(size: Int): String =
|
||||||
|
s"""✅ <b>$size</b> terms were found by monitoring. We showed you the closest 5.
|
||||||
|
|<b>➡</b> Please choose one to reserve""".stripMargin
|
||||||
|
|
||||||
|
override def nothingWasFoundByMonitoring(monitoring: Monitoring): String =
|
||||||
|
s"""❗ Nothing was found by your monitoring. Monitoring has been <b>disabled</b> as outdated.
|
||||||
|
|
|
||||||
|
|📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|
|
||||||
|
|<b>➡</b> Create new monitoring /book""".stripMargin
|
||||||
|
|
||||||
|
override def appointmentIsBooked(term: AvailableVisitsTermPresentation, monitoring: Monitoring): String =
|
||||||
|
s"""👍 We just booked appointment for you!
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}""".stripMargin
|
||||||
|
|
||||||
|
override def maximumMonitoringsLimitExceeded: String = "Maximum monitorings per user is 5"
|
||||||
|
|
||||||
|
override def monitoringOfTheSameTypeExists: String = "You already have active monitoring for the same service /monitorings"
|
||||||
|
|
||||||
|
override def termIsOutdated: String =
|
||||||
|
s"""❗️ Looks like the term is already booked by someone else
|
||||||
|
|Please try another one or create a new monitoring /book""".stripMargin
|
||||||
|
|
||||||
|
override def loginHasChangedOrWrong: String =
|
||||||
|
"""❗ You have entered invalid <b>login</b> or <b>password</b> or changed it via site.
|
||||||
|
|Please /login again and create a new monitoring /book.
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
override def settingsHeader: String = "<b>➡</b> Please choose an action"
|
||||||
|
|
||||||
|
override def language: String = "Change language"
|
||||||
|
|
||||||
|
override def chooseLanguage: String = "<b>➡</b> Please choose a language"
|
||||||
|
|
||||||
|
override def languageUpdated: String = "👍 Language was successfully changed!"
|
||||||
|
|
||||||
|
override def appointmentWasNotCancelled: String = "👍 Appointment was not cancelled"
|
||||||
|
|
||||||
|
override def monitoringWasNotDeactivated: String = "👍 Monitoring was not deactivated"
|
||||||
|
|
||||||
|
override def bugAction: String = "<b>➡</b> Please choose an action"
|
||||||
|
|
||||||
|
override def createNewBug: String = "🐞 Submit new"
|
||||||
|
|
||||||
|
override def showSubmittedBugs: String = "👀 Show submitted"
|
||||||
|
|
||||||
|
override def enterIssueDetails: String = "<b>➡</b> Please provide issue details"
|
||||||
|
|
||||||
|
override def noSubmittedIssuesFound: String = "ℹ No submitted issues found"
|
||||||
|
|
||||||
|
override def bugHasBeenCreated(bugId: Long): String = s"✅ Thank you for submitting bug <b>#$bugId</b>!"
|
||||||
|
|
||||||
|
override def chooseTimeOfDay: String = "<b>➡</b> Please choose preferred time of day"
|
||||||
|
|
||||||
|
override def afterFive: String = "After 17:00"
|
||||||
|
|
||||||
|
override def nineToFive: String = "From 09:00 to 17:00"
|
||||||
|
|
||||||
|
override def beforeNine: String = "Before 09:00"
|
||||||
|
|
||||||
|
override def allDay: String = "All day"
|
||||||
|
|
||||||
|
override def preferredTimeIs(time: Int): String = s"⏱ Preferred time is ${timeOfDay(time)}"
|
||||||
|
}
|
||||||
236
server/src/main/scala/com/lbs/server/lang/Lang.scala
Normal file
236
server/src/main/scala/com/lbs/server/lang/Lang.scala
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.lang
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
import com.lbs.api.json.model.{AvailableVisitsTermPresentation, HistoricVisit, ReservedVisit, ValuationsResponse}
|
||||||
|
import com.lbs.server.actor.Book.BookingData
|
||||||
|
import com.lbs.server.actor.StaticData.StaticDataConfig
|
||||||
|
import com.lbs.server.repository.model
|
||||||
|
import com.lbs.server.repository.model.Monitoring
|
||||||
|
|
||||||
|
object Lang {
|
||||||
|
|
||||||
|
val Langs: Seq[Lang] = Seq(En, Ua)
|
||||||
|
|
||||||
|
private val LangsMap = Seq(En, Ua).map(e => e.id -> e).toMap
|
||||||
|
|
||||||
|
def apply(id: Int): Lang = {
|
||||||
|
LangsMap.getOrElse(id, sys.error(s"Unknown language id $id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Lang {
|
||||||
|
def id: Int
|
||||||
|
|
||||||
|
def locale: Locale
|
||||||
|
|
||||||
|
def label: String
|
||||||
|
|
||||||
|
val timeOfDay = Map(
|
||||||
|
0 -> allDay,
|
||||||
|
1 -> beforeNine,
|
||||||
|
2 -> nineToFive,
|
||||||
|
3 -> afterFive
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def capitalizeFirstLetter(str: String): String = {
|
||||||
|
val fistCapitalLetter = str.head.toTitleCase
|
||||||
|
fistCapitalLetter + str.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def withPages(message: String, page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def unableToCancelUpcomingVisit(reason: String): String
|
||||||
|
|
||||||
|
def appointmentHasBeenCancelled: String
|
||||||
|
|
||||||
|
def yes: String
|
||||||
|
|
||||||
|
def no: String
|
||||||
|
|
||||||
|
def noUpcomingVisits: String
|
||||||
|
|
||||||
|
def areYouSureToCancelAppointment(visit: ReservedVisit): String
|
||||||
|
|
||||||
|
def chooseDateFrom: String
|
||||||
|
|
||||||
|
def chooseDateTo: String
|
||||||
|
|
||||||
|
def findTerms: String
|
||||||
|
|
||||||
|
def modifyDate: String
|
||||||
|
|
||||||
|
def bookingSummary(bookingData: BookingData): String
|
||||||
|
|
||||||
|
def noTermsFound: String
|
||||||
|
|
||||||
|
def createMonitoring: String
|
||||||
|
|
||||||
|
def cancel: String
|
||||||
|
|
||||||
|
def book: String
|
||||||
|
|
||||||
|
def confirmAppointment(term: AvailableVisitsTermPresentation, valuations: ValuationsResponse): String
|
||||||
|
|
||||||
|
def appointmentIsConfirmed: String
|
||||||
|
|
||||||
|
def monitoringHasBeenCreated: String
|
||||||
|
|
||||||
|
def unableToCreateMonitoring: String
|
||||||
|
|
||||||
|
def chooseTypeOfMonitoring: String
|
||||||
|
|
||||||
|
def bookByApplication: String
|
||||||
|
|
||||||
|
def bookManually: String
|
||||||
|
|
||||||
|
def city: String
|
||||||
|
|
||||||
|
def clinic: String
|
||||||
|
|
||||||
|
def service: String
|
||||||
|
|
||||||
|
def doctor: String
|
||||||
|
|
||||||
|
def previous: String
|
||||||
|
|
||||||
|
def next: String
|
||||||
|
|
||||||
|
def noActiveMonitorings: String
|
||||||
|
|
||||||
|
def deactivateMonitoring(monitoring: Monitoring): String
|
||||||
|
|
||||||
|
def deactivated: String
|
||||||
|
|
||||||
|
def any: String
|
||||||
|
|
||||||
|
def pressAny: String
|
||||||
|
|
||||||
|
protected def withAnyVariant(message: String, isAnyAllowed: Boolean): String = {
|
||||||
|
if (isAnyAllowed)
|
||||||
|
message + "\n\n" + pressAny
|
||||||
|
else message
|
||||||
|
}
|
||||||
|
|
||||||
|
def pleaseEnterStaticDataNameOrAny(config: StaticDataConfig): String
|
||||||
|
|
||||||
|
def pleaseEnterStaticDataNameOrPrevious(config: StaticDataConfig): String
|
||||||
|
|
||||||
|
def staticDataIs(config: StaticDataConfig, label: String): String
|
||||||
|
|
||||||
|
def pleaseChooseStaticDataNameOrAny(config: StaticDataConfig): String
|
||||||
|
|
||||||
|
def staticNotFound(config: StaticDataConfig): String
|
||||||
|
|
||||||
|
def loginAndPasswordAreOk: String
|
||||||
|
|
||||||
|
def provideUsername: String
|
||||||
|
|
||||||
|
def providePassword: String
|
||||||
|
|
||||||
|
def visitsHistoryIsEmpty: String
|
||||||
|
|
||||||
|
def help: String
|
||||||
|
|
||||||
|
def dateFromIs(dateFrom: ZonedDateTime): String
|
||||||
|
|
||||||
|
def dateToIs(dateTo: ZonedDateTime): String
|
||||||
|
|
||||||
|
def termEntry(term: AvailableVisitsTermPresentation, page: Int, index: Int): String
|
||||||
|
|
||||||
|
def termsHeader(page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def historyEntry(visit: HistoricVisit, page: Int, index: Int): String
|
||||||
|
|
||||||
|
def historyHeader(page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def upcomingVisitEntry(visit: ReservedVisit, page: Int, index: Int): String
|
||||||
|
|
||||||
|
def upcomingVisitsHeader(page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def bugEntry(bug: model.Bug, page: Int, index: Int): String
|
||||||
|
|
||||||
|
def bugsHeader(page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def monitoringEntry(monitoring: Monitoring, page: Int, index: Int): String
|
||||||
|
|
||||||
|
def monitoringsHeader(page: Int, pages: Int): String
|
||||||
|
|
||||||
|
def invalidLoginOrPassword: String
|
||||||
|
|
||||||
|
def availableTermEntry(term: AvailableVisitsTermPresentation, monitoring: Monitoring, index: Int): String
|
||||||
|
|
||||||
|
def availableTermsHeader(size: Int): String
|
||||||
|
|
||||||
|
def nothingWasFoundByMonitoring(monitoring: Monitoring): String
|
||||||
|
|
||||||
|
def appointmentIsBooked(term: AvailableVisitsTermPresentation, monitoring: Monitoring): String
|
||||||
|
|
||||||
|
def maximumMonitoringsLimitExceeded: String
|
||||||
|
|
||||||
|
def monitoringOfTheSameTypeExists: String
|
||||||
|
|
||||||
|
def termIsOutdated: String
|
||||||
|
|
||||||
|
def loginHasChangedOrWrong: String
|
||||||
|
|
||||||
|
def settingsHeader: String
|
||||||
|
|
||||||
|
def language: String
|
||||||
|
|
||||||
|
def chooseLanguage: String
|
||||||
|
|
||||||
|
def languageUpdated: String
|
||||||
|
|
||||||
|
def appointmentWasNotCancelled: String
|
||||||
|
|
||||||
|
def monitoringWasNotDeactivated: String
|
||||||
|
|
||||||
|
def createNewBug: String
|
||||||
|
|
||||||
|
def showSubmittedBugs: String
|
||||||
|
|
||||||
|
def bugAction: String
|
||||||
|
|
||||||
|
def bugHasBeenCreated(bugId: Long): String
|
||||||
|
|
||||||
|
def noSubmittedIssuesFound: String
|
||||||
|
|
||||||
|
def enterIssueDetails: String
|
||||||
|
|
||||||
|
def chooseTimeOfDay: String
|
||||||
|
|
||||||
|
def afterFive: String
|
||||||
|
|
||||||
|
def nineToFive: String
|
||||||
|
|
||||||
|
def beforeNine: String
|
||||||
|
|
||||||
|
def allDay: String
|
||||||
|
|
||||||
|
def preferredTimeIs(time: Int): String
|
||||||
|
}
|
||||||
34
server/src/main/scala/com/lbs/server/lang/Localizable.scala
Normal file
34
server/src/main/scala/com/lbs/server/lang/Localizable.scala
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.lang
|
||||||
|
|
||||||
|
import com.lbs.server.actor.Login.UserId
|
||||||
|
|
||||||
|
trait Localizable {
|
||||||
|
protected def userId: UserId
|
||||||
|
|
||||||
|
protected def localization: Localization
|
||||||
|
|
||||||
|
protected def lang: Lang = Option(userId).map(uId => localization.lang(uId.userId)).getOrElse(En)
|
||||||
|
}
|
||||||
63
server/src/main/scala/com/lbs/server/lang/Localization.scala
Normal file
63
server/src/main/scala/com/lbs/server/lang/Localization.scala
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.lang
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
import com.lbs.server.repository.model
|
||||||
|
import com.lbs.server.service.DataService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Localization {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var dataService: DataService = _
|
||||||
|
|
||||||
|
private val cachedLangs = new ConcurrentHashMap[Long, Lang]
|
||||||
|
|
||||||
|
def lang(userId: Long): Lang = {
|
||||||
|
cachedLangs.computeIfAbsent(userId, _ => {
|
||||||
|
val settings = dataService.findSettings(userId)
|
||||||
|
settings.map(s => Lang(s.lang)).getOrElse(En)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def invalidateLangsCache(): Unit = {
|
||||||
|
cachedLangs.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateLanguage(userId: Long, lang: Lang): Unit = {
|
||||||
|
cachedLangs.put(userId, lang)
|
||||||
|
val settings = dataService.findSettings(userId) match {
|
||||||
|
case Some(exists) =>
|
||||||
|
exists.setLang(lang.id)
|
||||||
|
exists
|
||||||
|
case None => model.Settings(userId, lang.id)
|
||||||
|
}
|
||||||
|
dataService.saveSettings(settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
350
server/src/main/scala/com/lbs/server/lang/Ua.scala
Normal file
350
server/src/main/scala/com/lbs/server/lang/Ua.scala
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.lang
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
import com.lbs.api.json.model.{AvailableVisitsTermPresentation, HistoricVisit, ReservedVisit, ValuationsResponse}
|
||||||
|
import com.lbs.server.actor.Book
|
||||||
|
import com.lbs.server.actor.StaticData.StaticDataConfig
|
||||||
|
import com.lbs.server.repository.model.{Bug, Monitoring}
|
||||||
|
import com.lbs.server.util.DateTimeUtil.{formatDate, formatDateTime, minutesSinceBeginOf2018}
|
||||||
|
|
||||||
|
object Ua extends Lang {
|
||||||
|
|
||||||
|
override def id: Int = 1
|
||||||
|
|
||||||
|
override def locale: Locale = new Locale("uk", "UA")
|
||||||
|
|
||||||
|
override def label: String = "🇺🇦Українська"
|
||||||
|
|
||||||
|
override protected def withPages(message: String, page: Int, pages: Int): String = {
|
||||||
|
if (pages > 1) s"$message. Сторінка <b>${page + 1}</b> з <b>$pages</b>"
|
||||||
|
else message
|
||||||
|
}
|
||||||
|
|
||||||
|
override def unableToCancelUpcomingVisit(reason: String): String =
|
||||||
|
s"⚠ Не вдається скасувати візит! Причина: $reason"
|
||||||
|
|
||||||
|
override def appointmentHasBeenCancelled: String =
|
||||||
|
s"👍 Ваш візит було скасовано!"
|
||||||
|
|
||||||
|
override def yes: String = "Так"
|
||||||
|
|
||||||
|
override def no: String = "Ні"
|
||||||
|
|
||||||
|
override def noUpcomingVisits: String =
|
||||||
|
"ℹ Не знайдено жодного майбутнього візиту"
|
||||||
|
|
||||||
|
override def areYouSureToCancelAppointment(visit: ReservedVisit): String =
|
||||||
|
s"""<b>➡</b> Ви впевнені, що хочете скасувати візит?
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinic.name}
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def chooseDateFrom: String = "<b>➡</b> Будь ласка, виберіть початкову дату"
|
||||||
|
|
||||||
|
override def chooseDateTo: String = "<b>➡</b> Будь ласка, виберіть кінцеву дату"
|
||||||
|
|
||||||
|
override def findTerms: String = "🔍 Знайти терміни"
|
||||||
|
|
||||||
|
override def modifyDate: String = "📅 Змінити дату"
|
||||||
|
|
||||||
|
override def bookingSummary(bookingData: Book.BookingData): String =
|
||||||
|
s"🦄 Супер! Ми збираємося зарезервувати послугу <b>${bookingData.serviceId.name}</b>" +
|
||||||
|
s" з обраним лікарем <b>${bookingData.doctorId.name}</b>" +
|
||||||
|
s" в <b>${bookingData.clinicId.name}</b> клініці" +
|
||||||
|
s" міста <b>${bookingData.cityId.name}</b>." +
|
||||||
|
s"\nБажані дати: <b>${formatDate(bookingData.dateFrom, locale)}</b> -> <b>${formatDate(bookingData.dateTo, locale)}</b>" +
|
||||||
|
s"\nЧас: <b>${timeOfDay(bookingData.timeOfDay)}</b>" +
|
||||||
|
s"\n\n<b>➡</b> Тепер оберіть наступну дію"
|
||||||
|
|
||||||
|
override def noTermsFound: String =
|
||||||
|
s"""ℹ Терміни відсутні
|
||||||
|
|
|
||||||
|
|Що ви хочете зробити далі?""".stripMargin
|
||||||
|
|
||||||
|
override def createMonitoring: String = "👀 Створити моніторінг"
|
||||||
|
|
||||||
|
override def cancel: String = "Відмінити"
|
||||||
|
|
||||||
|
override def book: String = "Зарезервувати"
|
||||||
|
|
||||||
|
override def confirmAppointment(term: AvailableVisitsTermPresentation, valuations: ValuationsResponse): String =
|
||||||
|
|
||||||
|
s"""<b>➡</b> ${valuations.optionsQuestion.getOrElse("Ви хотіли б підтвердити резервацію візиту?")}
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|
|
||||||
|
|ℹ${valuations.visitTermVariants.head.infoMessage}""".stripMargin
|
||||||
|
|
||||||
|
override def appointmentIsConfirmed: String = "👍 Ваш візит було підтверджено!"
|
||||||
|
|
||||||
|
override def monitoringHasBeenCreated: String = "👍 Моніторинг був створений! Список активних /monitorings"
|
||||||
|
|
||||||
|
override def unableToCreateMonitoring: String = s"👎 Не вдається створити моніторинг. Будь ласка, створіть /bug"
|
||||||
|
|
||||||
|
override def chooseTypeOfMonitoring: String = "<b>➡</b> Будь ласка, виберіть тип моніторингу"
|
||||||
|
|
||||||
|
override def bookByApplication: String = "👾 Автоматична резервація"
|
||||||
|
|
||||||
|
override def bookManually: String = "👤 Ручна резервація"
|
||||||
|
|
||||||
|
override def city: String = "місто"
|
||||||
|
|
||||||
|
override def clinic: String = "клініка"
|
||||||
|
|
||||||
|
override def service: String = "послуга"
|
||||||
|
|
||||||
|
override def doctor: String = "лікар"
|
||||||
|
|
||||||
|
override def previous: String = "Попередня"
|
||||||
|
|
||||||
|
override def next: String = "Наступна"
|
||||||
|
|
||||||
|
override def noActiveMonitorings: String = "ℹ У вас немає активних моніторингів. Створити новий /book"
|
||||||
|
|
||||||
|
override def deactivateMonitoring(monitoring: Monitoring): String =
|
||||||
|
s"""<b>➡</b> Ви впевнені, що хочете вимкнути моніторинг?
|
||||||
|
|
|
||||||
|
|📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}""".stripMargin
|
||||||
|
|
||||||
|
override def deactivated: String = "👍 Деактивовано! Список активних /monitorings"
|
||||||
|
|
||||||
|
override def any: String = "Будь-який"
|
||||||
|
|
||||||
|
override def pressAny: String = s"або натисніть кнопку <b>$any</b>"
|
||||||
|
|
||||||
|
override def pleaseEnterStaticDataNameOrAny(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(
|
||||||
|
s"""<b>➡</b> Будь ласка, введіть ${config.name}
|
||||||
|
|Наприклад: <b>${config.example}</b>""".stripMargin,
|
||||||
|
config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def pleaseEnterStaticDataNameOrPrevious(config: StaticDataConfig): String =
|
||||||
|
s"""<b>➡</b> Будь ласка, введіть ${config.name}
|
||||||
|
|Наприклад: <b>${config.example}</b>
|
||||||
|
|
|
||||||
|
|або виберіть ${config.name} з попередніх пошуків""".stripMargin
|
||||||
|
|
||||||
|
override def staticDataIs(config: StaticDataConfig, label: String): String =
|
||||||
|
s"<b>✅</b> ${capitalizeFirstLetter(config.name)} <b>$label</b>"
|
||||||
|
|
||||||
|
override def pleaseChooseStaticDataNameOrAny(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(s"<b>➡</b> Будь ласка, виберіть ${config.name}", config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def staticNotFound(config: StaticDataConfig): String =
|
||||||
|
withAnyVariant(
|
||||||
|
s"""<b>➡</b> Нічого не знайдено 😔
|
||||||
|
|Будь ласка, введіть ${config.name} знову""".stripMargin, config.isAnyAllowed)
|
||||||
|
|
||||||
|
override def loginAndPasswordAreOk: String =
|
||||||
|
s"""✅ Супер! Логін і пароль збережено
|
||||||
|
|Тепер ви можете змінити мову /settings""".stripMargin
|
||||||
|
|
||||||
|
override def provideUsername: String =
|
||||||
|
s"""ℹ Ви повинні увійти в систему, використовуючи облікові дані <b>Luxmed</b>
|
||||||
|
|
|
||||||
|
|<b>➡</b> Будь ласка, введіть ім'я користувача""".stripMargin
|
||||||
|
|
||||||
|
override def providePassword: String = "<b>➡</b> Будь ласка, введіть пароль"
|
||||||
|
|
||||||
|
override def visitsHistoryIsEmpty: String = "ℹ Немає візитів в вашій історії"
|
||||||
|
|
||||||
|
override def help: String =
|
||||||
|
s"""ℹ Це неофіційний бот для Порталу Пацієнта LUX MED.
|
||||||
|
|Завдяки йому ви можете зарезервувати візит до лікаря, створити моніторинг доступних термінів, переглянути історію та майбутні візити
|
||||||
|
|
|
||||||
|
|<b>➡</b> Підтримувані команди
|
||||||
|
|/login - ввести облікові дані Luxmed
|
||||||
|
|/book - призначити візит
|
||||||
|
|/monitorings - моніторінг доступних термінів
|
||||||
|
|/history - історія візитів
|
||||||
|
|/visits - майбутні візити
|
||||||
|
|/settings - змінити мову
|
||||||
|
|/bug - відправити баг""".stripMargin
|
||||||
|
|
||||||
|
override def dateFromIs(dateFrom: ZonedDateTime): String = s"📅 Початкова дата ${formatDate(dateFrom, locale)}"
|
||||||
|
|
||||||
|
override def dateToIs(dateTo: ZonedDateTime): String = s"📅 Кінцева дата ${formatDate(dateTo, locale)}"
|
||||||
|
|
||||||
|
override def termEntry(term: AvailableVisitsTermPresentation, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|<b>➡</b> /book_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def termsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Доступні терміни", page, pages)
|
||||||
|
|
||||||
|
override def historyEntry(visit: HistoricVisit, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinicName}
|
||||||
|
|<b>➡</b> /repeat_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def historyHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Завершені візити", page, pages)
|
||||||
|
|
||||||
|
override def upcomingVisitEntry(visit: ReservedVisit, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(visit.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${visit.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${visit.service.name}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${visit.clinic.name}
|
||||||
|
|<b>➡</b> /cancel_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def upcomingVisitsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Зарезервовані візити", page, pages)
|
||||||
|
|
||||||
|
override def bugEntry(bug: Bug, page: Int, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(bug.submitted, locale)}</b>
|
||||||
|
|Опис: ${bug.details}
|
||||||
|
|Статус: <b>${if (bug.resolved) "✅ Вирішено" else "🚫 Невирішено"}</b>
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def bugsHeader(page: Int, pages: Int): String =
|
||||||
|
withPages("<b>➡</b> Створені баги", page, pages)
|
||||||
|
|
||||||
|
override def monitoringEntry(monitoring: Monitoring, page: Int, index: Int): String =
|
||||||
|
s"""📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|Тип: ${if (monitoring.autobook) "Автоматичний" else "Ручний"}
|
||||||
|
|<b>➡</b> /cancel_${page}_$index
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def monitoringsHeader(page: Int, pages: Int): String =
|
||||||
|
s"<b>➡</b> Активні моніторінги"
|
||||||
|
|
||||||
|
override def invalidLoginOrPassword: String =
|
||||||
|
"""❗ Ви ввели невірний логін або пароль, або змінили його через сайт.
|
||||||
|
|Ваші моніторинги були видалені. Будь ласка, /login знову і створіть нові моніторинги.
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
override def availableTermEntry(term: AvailableVisitsTermPresentation, monitoring: Monitoring, index: Int): String =
|
||||||
|
s"""⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|/reserve_${monitoring.recordId}_${term.scheduleId}_${minutesSinceBeginOf2018(term.visitDate.startDateTime)}
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
override def availableTermsHeader(size: Int): String =
|
||||||
|
s"""✅ <b>$size</b> термінів було знайдено за допомогою моніторінгу. Ми показали вам найближчі 5.
|
||||||
|
|<b>➡</b> Будь ласка, оберіть один щоб заререзвувати""".stripMargin
|
||||||
|
|
||||||
|
override def nothingWasFoundByMonitoring(monitoring: Monitoring): String =
|
||||||
|
s"""❗ Нічого не знайдено за вашим моніторингом. Моніторинг був <b>вимкнений</b> як застарілий.
|
||||||
|
|
|
||||||
|
|📅 <b>${formatDate(monitoring.dateFrom, locale)}</b> -> <b>${formatDate(monitoring.dateTo, locale)}</b>
|
||||||
|
|⏱ <b>${timeOfDay(monitoring.timeOfDay)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${monitoring.doctorName}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${monitoring.clinicName}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}
|
||||||
|
|
|
||||||
|
|<b>➡</b> Створити новий моніторінг /book""".stripMargin
|
||||||
|
|
||||||
|
override def appointmentIsBooked(term: AvailableVisitsTermPresentation, monitoring: Monitoring): String =
|
||||||
|
s"""👍 Ми зерезевували візит для вас!
|
||||||
|
|
|
||||||
|
|⏱ <b>${formatDateTime(term.visitDate.startDateTime, locale)}</b>
|
||||||
|
|${capitalizeFirstLetter(doctor)}: ${term.doctor.name}
|
||||||
|
|${capitalizeFirstLetter(service)}: ${monitoring.serviceName}
|
||||||
|
|${capitalizeFirstLetter(clinic)}: ${term.clinic.name}
|
||||||
|
|${capitalizeFirstLetter(city)}: ${monitoring.cityName}""".stripMargin
|
||||||
|
|
||||||
|
override def maximumMonitoringsLimitExceeded: String = "Максимальна кількість моніторінгів 5"
|
||||||
|
|
||||||
|
override def monitoringOfTheSameTypeExists: String = "У вас вже є активний моніторинг на таку ж саму послугу /monitorings"
|
||||||
|
|
||||||
|
override def termIsOutdated: String =
|
||||||
|
s"""❗️ Схоже, що термін вже не є доступним
|
||||||
|
|Будь ласка, спробуйте інший або створіть новий моніторинг /book""".stripMargin
|
||||||
|
|
||||||
|
override def loginHasChangedOrWrong: String =
|
||||||
|
"""❗ Ви ввели невірний і <b>логін</b> або <b>пароль</b> або змінили його через сайт.
|
||||||
|
|Будь ласка, /login знову і створіть новий моніторинг/book.
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
|
override def settingsHeader: String = "<b>➡</b> Оберіть дію"
|
||||||
|
|
||||||
|
override def language: String = "Змінтини мову"
|
||||||
|
|
||||||
|
override def chooseLanguage: String = "<b>➡</b> Будь ласка, оберіть мову"
|
||||||
|
|
||||||
|
override def languageUpdated: String = "👍 Мову успішно змінено!"
|
||||||
|
|
||||||
|
override def appointmentWasNotCancelled: String = "👍 Візит не було скасовано"
|
||||||
|
|
||||||
|
override def monitoringWasNotDeactivated: String = "👍 Моніторінг не було деактивовано"
|
||||||
|
|
||||||
|
override def bugAction: String = "<b>➡</b> Оберіть дію"
|
||||||
|
|
||||||
|
override def createNewBug: String = "🐞 Створити новий"
|
||||||
|
|
||||||
|
override def showSubmittedBugs: String = "👀 Показати створені"
|
||||||
|
|
||||||
|
override def enterIssueDetails: String = "<b>➡</b> Будь ласка, введіть деталі проблеми"
|
||||||
|
|
||||||
|
override def noSubmittedIssuesFound: String = "ℹ Створених вами багів не знайдено"
|
||||||
|
|
||||||
|
override def bugHasBeenCreated(bugId: Long): String = s"✅ Дякуємо за відправлений баг <b>#$bugId</b>!"
|
||||||
|
|
||||||
|
override def chooseTimeOfDay: String = "<b>➡</b> Будь ласка, оберіть бажаний час"
|
||||||
|
|
||||||
|
override def afterFive: String = "Після 17:00"
|
||||||
|
|
||||||
|
override def nineToFive: String = "Від 09:00 до 17:00"
|
||||||
|
|
||||||
|
override def beforeNine: String = "До 09:00"
|
||||||
|
|
||||||
|
override def allDay: String = "Весь день"
|
||||||
|
|
||||||
|
override def preferredTimeIs(time: Int): String = s"⏱ Бажаний час ${timeOfDay(time)}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.repository
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import com.lbs.server.repository.model.{Bug, CityHistory, ClinicHistory, Credentials, DoctorHistory, JLong, Monitoring, ServiceHistory, Settings, Source}
|
||||||
|
import javax.persistence.EntityManager
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class DataRepository(@Autowired em: EntityManager) {
|
||||||
|
|
||||||
|
private val maxHistory = 2
|
||||||
|
|
||||||
|
def getCityHistory(userId: Long): Seq[CityHistory] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select city from CityHistory city where city.recordId in
|
||||||
|
| (select max(c.recordId) from CityHistory c where c.userId = :userId group by c.name order by MAX(c.time) desc)
|
||||||
|
| order by city.time desc""".stripMargin, classOf[CityHistory])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setMaxResults(maxHistory)
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def getClinicHistory(userId: Long, cityId: Long): Seq[ClinicHistory] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select clinic from ClinicHistory clinic where clinic.recordId in
|
||||||
|
| (select max(c.recordId) from ClinicHistory c where c.userId = :userId and c.cityId = :cityId group by c.name order by MAX(c.time) desc)
|
||||||
|
| order by clinic.time desc""".stripMargin, classOf[ClinicHistory])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setParameter("cityId", cityId)
|
||||||
|
.setMaxResults(maxHistory)
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def getServiceHistory(userId: Long, cityId: Long, clinicId: Option[Long]): Seq[ServiceHistory] = {
|
||||||
|
val query = em.createQuery(
|
||||||
|
s"""select service from ServiceHistory service where service.recordId in
|
||||||
|
| (select max(s.recordId) from ServiceHistory s where s.userId = :userId and s.cityId = :cityId
|
||||||
|
| and s.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")} group by s.name order by MAX(s.time) desc)
|
||||||
|
| order by service.time desc""".stripMargin, classOf[ServiceHistory])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setParameter("cityId", cityId)
|
||||||
|
.setMaxResults(maxHistory)
|
||||||
|
|
||||||
|
clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def getDoctorHistory(userId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Seq[DoctorHistory] = {
|
||||||
|
val query = em.createQuery(
|
||||||
|
s"""select doctor from DoctorHistory doctor where doctor.recordId in
|
||||||
|
| (select max(d.recordId) from DoctorHistory d where d.userId = :userId
|
||||||
|
| and d.cityId = :cityId and d.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")}
|
||||||
|
| and d.serviceId = :serviceId group by d.name order by MAX(d.time) desc)
|
||||||
|
| order by doctor.time desc""".stripMargin, classOf[DoctorHistory])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setParameter("cityId", cityId)
|
||||||
|
.setParameter("serviceId", serviceId)
|
||||||
|
.setMaxResults(maxHistory)
|
||||||
|
|
||||||
|
clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def findCredentials(userId: Long): Option[Credentials] = {
|
||||||
|
em.createQuery(
|
||||||
|
"select credentials from Credentials credentials where credentials.userId = :userId", classOf[Credentials])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBugs(userId: Long): Seq[Bug] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select bug from Bug bug where bug.userId = :userId order by bug.submitted desc""".stripMargin, classOf[Bug])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setMaxResults(50)
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def getActiveMonitorings: Seq[Monitoring] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select monitoring from Monitoring monitoring where monitoring.active = true""".stripMargin, classOf[Monitoring])
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def getActiveMonitoringsCount(userId: Long): JLong = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select count(monitoring) from Monitoring monitoring where monitoring.active = true
|
||||||
|
| and monitoring.userId = :userId""".stripMargin, classOf[JLong])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.getSingleResult
|
||||||
|
}
|
||||||
|
|
||||||
|
def getActiveMonitorings(userId: Long): Seq[Monitoring] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select monitoring from Monitoring monitoring where monitoring.active = true
|
||||||
|
| and monitoring.userId = :userId order by monitoring.dateTo asc""".stripMargin, classOf[Monitoring])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def findActiveMonitoring(userId: Long, cityId: Long, serviceId: Long): Option[Monitoring] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select monitoring from Monitoring monitoring where monitoring.active = true
|
||||||
|
| and monitoring.userId = :userId
|
||||||
|
| and monitoring.cityId = :cityId
|
||||||
|
| and monitoring.serviceId = :serviceId""".stripMargin, classOf[Monitoring])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setParameter("cityId", cityId)
|
||||||
|
.setParameter("serviceId", serviceId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def getActiveMonitoringsSince(since: ZonedDateTime): Seq[Monitoring] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select monitoring from Monitoring monitoring where monitoring.active = true
|
||||||
|
| and monitoring.created > :since""".stripMargin, classOf[Monitoring])
|
||||||
|
.setParameter("since", since)
|
||||||
|
.getResultList.asScala
|
||||||
|
}
|
||||||
|
|
||||||
|
def findMonitoring(userId: Long, monitoringId: Long): Option[Monitoring] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select monitoring from Monitoring monitoring where monitoring.userId = :userId
|
||||||
|
| and monitoring.recordId = :monitoringId""".stripMargin, classOf[Monitoring])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.setParameter("monitoringId", monitoringId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def findSettings(userId: Long): Option[Settings] = {
|
||||||
|
em.createQuery(
|
||||||
|
"select settings from Settings settings where settings.userId = :userId", classOf[Settings])
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def findUserId(chatId: String, sourceSystemId: Long): Option[JLong] = {
|
||||||
|
em.createQuery(
|
||||||
|
"select source.userId from Source source where source.chatId = :chatId" +
|
||||||
|
" and source.sourceSystemId = :sourceSystemId", classOf[JLong])
|
||||||
|
.setParameter("chatId", chatId)
|
||||||
|
.setParameter("sourceSystemId", sourceSystemId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def findCredentialsByUsername(username: String): Option[Credentials] = {
|
||||||
|
em.createQuery(
|
||||||
|
"select credentials from Credentials credentials where credentials.username = :username", classOf[Credentials])
|
||||||
|
.setParameter("username", username)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def findSource(chatId: String, sourceSystemId: Long, userId: Long): Option[Source] = {
|
||||||
|
em.createQuery(
|
||||||
|
"select source from Source source where source.chatId = :chatId" +
|
||||||
|
" and source.sourceSystemId = :sourceSystemId" +
|
||||||
|
" and userId = :userId", classOf[Source])
|
||||||
|
.setParameter("chatId", chatId)
|
||||||
|
.setParameter("sourceSystemId", sourceSystemId)
|
||||||
|
.setParameter("userId", userId)
|
||||||
|
.getResultList.asScala.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def saveEntity[T](entity: T): T = {
|
||||||
|
em.merge(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.repository.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import javax.persistence.{Access, AccessType, Column, Entity}
|
||||||
|
|
||||||
|
import scala.beans.BeanProperty
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Access(AccessType.FIELD)
|
||||||
|
class Bug extends RecordId {
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
var userId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "source_system_id", nullable = false)
|
||||||
|
var sourceSystemId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var details: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var resolved: Boolean = false
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var submitted: ZonedDateTime = ZonedDateTime.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
object Bug {
|
||||||
|
def apply(userId: Long, sourceSystemId: Long, details: String, resolved: Boolean = false, submitted: ZonedDateTime = ZonedDateTime.now()): Bug = {
|
||||||
|
val bug = new Bug
|
||||||
|
bug.userId = userId
|
||||||
|
bug.sourceSystemId = sourceSystemId
|
||||||
|
bug.details = details
|
||||||
|
bug.resolved = resolved
|
||||||
|
bug.submitted = submitted
|
||||||
|
bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.repository.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import javax.persistence.{Access, AccessType, Column, Entity}
|
||||||
|
|
||||||
|
import scala.beans.BeanProperty
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Access(AccessType.FIELD)
|
||||||
|
class CityHistory extends History with RecordId {
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var id: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var name: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
var userId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var time: ZonedDateTime = _
|
||||||
|
}
|
||||||
|
|
||||||
|
object CityHistory {
|
||||||
|
def apply(userId: Long, id: Long, name: String, time: ZonedDateTime): CityHistory = {
|
||||||
|
val city = new CityHistory
|
||||||
|
city.userId = userId
|
||||||
|
city.id = id
|
||||||
|
city.name = name
|
||||||
|
city.time = time
|
||||||
|
city
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Yevhen Zadyra
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.lbs.server.repository.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import javax.persistence.{Access, AccessType, Column, Entity}
|
||||||
|
|
||||||
|
import scala.beans.BeanProperty
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Access(AccessType.FIELD)
|
||||||
|
class ClinicHistory extends History with RecordId {
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var id: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var name: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
var userId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "city_id", nullable = false)
|
||||||
|
var cityId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var time: ZonedDateTime = _
|
||||||
|
}
|
||||||
|
|
||||||
|
object ClinicHistory {
|
||||||
|
def apply(userId: Long, id: Long, name: String, cityId: Long, time: ZonedDateTime): ClinicHistory = {
|
||||||
|
val clinic = new ClinicHistory
|
||||||
|
clinic.userId = userId
|
||||||
|
clinic.id = id
|
||||||
|
clinic.name = name
|
||||||
|
clinic.time = time
|
||||||
|
clinic.cityId = cityId
|
||||||
|
clinic
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user