9 Commits

92 changed files with 401 additions and 188 deletions

View File

@ -12,6 +12,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: extract version - name: extract version
id: version id: version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
@ -21,13 +22,24 @@ jobs:
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
filter: 'Release \d+\.\d+\.\d+' filter: 'Release \d+\.\d+\.\d+'
- name: setup JDK - name: setup JDK
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
distribution: temurin distribution: temurin
java-version: 18 java-version: 18
- name: install sbt
uses: eclipse-score/apt-install@main
with:
packages: wget
- run: |
wget https://scala.jfrog.io/artifactory/debian/sbt-1.11.7.deb
sudo dpkg -i sbt-1.11.7.deb
- name: create dist - name: create dist
run: make dist run: make dist
- name: release - name: release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:

View File

@ -13,10 +13,20 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: setup JDK - name: setup JDK
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
distribution: temurin distribution: temurin
java-version: 18 java-version: 18
- name: install sbt
uses: eclipse-score/apt-install@main
with:
packages: wget
- run: |
wget https://scala.jfrog.io/artifactory/debian/sbt-1.11.7.deb
sudo dpkg -i sbt-1.11.7.deb
- name: run tests - name: run tests
run: make tests run: make tests

View File

@ -41,7 +41,7 @@
<div class="container"> <div class="container">
<div id="toolbar"> <div id="toolbar">
<button id="add-btn" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-loot-dialog" hidden> <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-loot-dialog">
<i class="bi bi-plus"></i> add <i class="bi bi-plus"></i> add
</button> </button>
<button class="btn btn-secondary" onclick="reload()"> <button class="btn btn-secondary" onclick="reload()">
@ -147,7 +147,7 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button> <button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button>
<button type="button" class="btn btn-secondary" onclick="suggestLoot()">suggest</button> <button type="button" class="btn btn-secondary" onclick="suggestLoot()">suggest</button>
<button type="submit" class="btn btn-primary">add</button> <button id="add-btn" type="submit" class="btn btn-primary">add</button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -4,7 +4,7 @@ me.arcanis.ffxivbis {
include "item_data.json" include "item_data.json"
# xivapi base url, string, required # xivapi base url, string, required
xivapi-url = "https://xivapi.com" xivapi-url = "https://v2.xivapi.com"
# xivapi developer key, string, optional # xivapi developer key, string, optional
#xivapi-key = "abcdef" #xivapi-key = "abcdef"
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -15,6 +15,7 @@ import akka.util.Timeout
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.cors import ch.megard.akka.http.cors.scaladsl.CorsDirectives.cors
import com.typesafe.scalalogging.StrictLogging import com.typesafe.scalalogging.StrictLogging
import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint
import me.arcanis.ffxivbis.http.api.v2.RootApiV2Endpoint
import me.arcanis.ffxivbis.http.view.RootView import me.arcanis.ffxivbis.http.view.RootView
import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message}
@ -31,6 +32,7 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
private val auth = AuthorizationProvider(config, storage) private val auth = AuthorizationProvider(config, storage)
private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config) private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config)
private val rootApiV2Endpoint = new RootApiV2Endpoint(storage, auth)
private val rootView = new RootView(auth) private val rootView = new RootView(auth)
private val swagger = new Swagger(config) private val swagger = new Swagger(config)
@ -47,6 +49,7 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
pathPrefix("api") { pathPrefix("api") {
pathPrefix(Segment) { pathPrefix(Segment) {
case "v1" => rootApiV1Endpoint.routes case "v1" => rootApiV1Endpoint.routes
case "v2" => rootApiV2Endpoint.routes
case _ => reject case _ => reject
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,3 +1,11 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http package me.arcanis.ffxivbis.http
import scala.collection.immutable.HashSet import scala.collection.immutable.HashSet

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,3 +1,11 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v1.json package me.arcanis.ffxivbis.http.api.v1.json
import me.arcanis.ffxivbis.http.ValidatorHelper import me.arcanis.ffxivbis.http.ValidatorHelper

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v2
import me.arcanis.ffxivbis.http.api.v1.{HttpHandler => HttpHandlerV1}
import me.arcanis.ffxivbis.http.api.v2.json.JsonSupport
trait HttpHandler extends HttpHandlerV1 { this: JsonSupport => }

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v2
import akka.actor.typed.{ActorRef, Scheduler}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
import akka.util.Timeout
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.media.{ArraySchema, Content, Schema}
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.{Operation, Parameter}
import jakarta.ws.rs._
import me.arcanis.ffxivbis.http.api.v1.json.{ErrorModel, PieceModel, PlayerIdWithCountersModel}
import me.arcanis.ffxivbis.http.api.v2.json._
import me.arcanis.ffxivbis.http.helpers.LootHelper
import me.arcanis.ffxivbis.http.{Authorization, AuthorizationProvider}
import me.arcanis.ffxivbis.messages.Message
@Path("/api/v2")
class LootEndpoint(override val storage: ActorRef[Message], override val auth: AuthorizationProvider)(implicit
timeout: Timeout,
scheduler: Scheduler
) extends LootHelper
with Authorization
with JsonSupport
with HttpHandler {
def routes: Route = suggestLoot
@PUT
@Path("party/{partyId}/loot")
@Consumes(value = Array("application/json"))
@Produces(value = Array("application/json"))
@Operation(
summary = "suggest loot",
description = "Suggest loot pieces to party",
parameters = Array(
new Parameter(
name = "partyId",
in = ParameterIn.PATH,
description = "unique party ID",
example = "o3KicHQPW5b0JcOm5yI3"
),
),
requestBody = new RequestBody(
description = "piece description",
required = true,
content = Array(new Content(schema = new Schema(implementation = classOf[PieceModel])))
),
responses = Array(
new ApiResponse(
responseCode = "200",
description = "Players with counters ordered by priority to get this item",
content = Array(
new Content(
array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerIdWithCountersModel])),
)
)
),
new ApiResponse(
responseCode = "400",
description = "Invalid parameters were supplied",
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
),
new ApiResponse(
responseCode = "401",
description = "Supplied authorization is invalid",
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
),
new ApiResponse(
responseCode = "403",
description = "Access is forbidden",
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
),
new ApiResponse(
responseCode = "500",
description = "Internal server error",
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
),
),
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
tags = Array("loot"),
)
def suggestLoot: Route =
path("party" / Segment / "loot") { partyId =>
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
put {
entity(as[PiecesModel]) { piece =>
onSuccess(suggestPiece(partyId, piece.toPiece)) { response =>
complete(response.map(PlayerIdWithCountersModel.fromPlayerId))
}
}
}
}
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v2
import akka.actor.typed.{ActorRef, Scheduler}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.util.Timeout
import me.arcanis.ffxivbis.http.AuthorizationProvider
import me.arcanis.ffxivbis.http.api.v2.json.JsonSupport
import me.arcanis.ffxivbis.messages.Message
class RootApiV2Endpoint(
storage: ActorRef[Message],
auth: AuthorizationProvider,
)(implicit
timeout: Timeout,
scheduler: Scheduler
) extends JsonSupport
with HttpHandler {
private val lootEndpoint = new LootEndpoint(storage, auth)
def routes: Route =
handleExceptions(exceptionHandler) {
handleRejections(rejectionHandler) {
lootEndpoint.routes
}
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v2.json
import me.arcanis.ffxivbis.http.api.v1.json.{JsonSupport => JsonSupportV1}
import spray.json._
trait JsonSupport extends JsonSupportV1 {
implicit val piecesFormat: RootJsonFormat[PiecesModel] = jsonFormat1(PiecesModel.apply)
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021-2026 Evgeniy Alekseev.
*
* This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis).
*
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
*/
package me.arcanis.ffxivbis.http.api.v2.json
import io.swagger.v3.oas.annotations.media.Schema
import me.arcanis.ffxivbis.http.api.v1.json.PieceModel
import me.arcanis.ffxivbis.models.Piece
case class PiecesModel(
@Schema(description = "pieces list", required = true) pieces: Seq[PieceModel],
) {
def toPiece: Seq[Piece] = pieces.map(_.toPiece)
}
object PiecesModel {
def fromPiece(pieces: Seq[Piece]): PiecesModel = PiecesModel(pieces.map(PieceModel.fromPiece))
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -56,4 +56,11 @@ trait LootHelper {
scheduler: Scheduler scheduler: Scheduler
): Future[Seq[PlayerIdWithCounters]] = ): Future[Seq[PlayerIdWithCounters]] =
storage.ask(SuggestLoot(partyId, piece, _)).map(_.result) storage.ask(SuggestLoot(partyId, piece, _)).map(_.result)
def suggestPiece(partyId: String, pieces: Seq[Piece])(implicit
executionContext: ExecutionContext,
timeout: Timeout,
scheduler: Scheduler
): Future[Seq[PlayerIdWithCounters]] =
storage.ask(SuggestMultiLoot(partyId, pieces, _)).map(_.result)
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -69,6 +69,11 @@ object DatabaseMessage {
override val isReadOnly: Boolean = true override val isReadOnly: Boolean = true
} }
case class SuggestMultiLoot(partyId: String, pieces: Seq[Piece], replyTo: ActorRef[LootSelector.LootSelectorResult])
extends LootDatabaseMessage {
override val isReadOnly: Boolean = true
}
// party handler // party handler
trait PartyDatabaseMessage extends DatabaseMessage trait PartyDatabaseMessage extends DatabaseMessage

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -35,8 +35,8 @@ case class Party(partyDescription: PartyDescription, rules: Seq[String], players
this this
} }
def suggestLoot(piece: Piece): LootSelector.LootSelectorResult = def suggestLoot(pieces: Seq[Piece]): LootSelector.LootSelectorResult =
LootSelector(getPlayers, piece, rules) LootSelector(getPlayers, pieces, rules)
} }
object Party { object Party {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -10,9 +10,9 @@ package me.arcanis.ffxivbis.service
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerIdWithCounters} import me.arcanis.ffxivbis.models.{Piece, Player, PlayerIdWithCounters}
class LootSelector(players: Seq[Player], piece: Piece, orderBy: Seq[String]) { class LootSelector(players: Seq[Player], pieces: Seq[Piece], orderBy: Seq[String]) {
val counters: Seq[PlayerIdWithCounters] = players.map(_.withCounters(Some(piece))) val counters: Seq[PlayerIdWithCounters] = pieces.flatMap(piece => players.map(_.withCounters(Some(piece))))
def suggest: LootSelector.LootSelectorResult = def suggest: LootSelector.LootSelectorResult =
LootSelector.LootSelectorResult { LootSelector.LootSelectorResult {
@ -22,8 +22,8 @@ class LootSelector(players: Seq[Player], piece: Piece, orderBy: Seq[String]) {
object LootSelector { object LootSelector {
def apply(players: Seq[Player], piece: Piece, orderBy: Seq[String]): LootSelectorResult = def apply(players: Seq[Player], pieces: Seq[Piece], orderBy: Seq[String]): LootSelectorResult =
new LootSelector(players, piece, orderBy).suggest new LootSelector(players, pieces, orderBy).suggest
case class LootSelectorResult(result: Seq[PlayerIdWithCounters]) case class LootSelectorResult(result: Seq[PlayerIdWithCounters])
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -15,7 +15,6 @@ import spray.json._
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
import scala.jdk.CollectionConverters._ import scala.jdk.CollectionConverters._
import scala.util.Try import scala.util.Try
import scala.util.matching.Regex
trait XivApi extends RequestExecutor { trait XivApi extends RequestExecutor {
@ -41,106 +40,39 @@ trait XivApi extends RequestExecutor {
else remotePieceType(remote).map(_ ++ local) else remotePieceType(remote).map(_ ++ local)
} }
private def remotePieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType]] = { private def remotePieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType]] =
val uriForItems = Uri(xivapiUrl) Future
.withPath(Uri.Path / "item") .traverse(itemIds) { id =>
.withQuery( val uriForItem = Uri(xivapiUrl)
Uri.Query( .withPath(Uri.Path / "api" / "sheet" / "Item" / id.toString)
Map( .withQuery(
"columns" -> Seq("ID", "GameContentLinks").mkString(","), Uri.Query(
"ids" -> itemIds.mkString(","), Map(
"private_key" -> xivapiKey.getOrElse("") "fields" -> Seq("Lot").mkString(","),
) "private_key" -> xivapiKey.getOrElse("")
) )
)
sendRequest(uriForItems, XivApi.parseXivapiJsonToShop).flatMap { shops =>
val shopIds = shops.values.map(_._2).toSet
val columns = shops.values.map(pair => s"ItemCost${pair._1}").toSet
val uriForShops = Uri(xivapiUrl)
.withPath(Uri.Path / "specialshop")
.withQuery(
Uri.Query(
Map(
"columns" -> (columns + "ID").mkString(","),
"ids" -> shopIds.mkString(","),
"private_key" -> xivapiKey.getOrElse("")
) )
) )
)
sendRequest(uriForShops, XivApi.parseXivapiJsonToType(shops)) sendRequest(uriForItem, XivApi.parseXivapiJsonToLot).map(id -> _)
} }
} .map(_.toMap)
} }
object XivApi { object XivApi {
private val defaultShop = JsObject("IsUnique" -> JsNumber(1), "StackSize" -> JsNumber(999)) private def parseXivapiJsonToLot(js: JsObject)(implicit executionContext: ExecutionContext): Future[PieceType] =
private val itemRegexp = new Regex("""Item(Receive|Cost)(\d+)""", "type", "index")
private def parseXivapiJsonToShop(
js: JsObject
)(implicit executionContext: ExecutionContext): Future[Map[Long, (String, Long)]] = {
def extractTraderId(js: JsObject) =
js.fields
.get("Recipe")
.map(_ => "crafted" -> -1L) // you can craft this item
.orElse { // lets try shop items
js.fields("SpecialShop").asJsObject.fields.collectFirst {
case (shopName, JsArray(array)) if itemRegexp.matches(shopName) =>
val shopId = array.head match {
case JsNumber(id) => id.toLong
case other => throw deserializationError(s"Could not parse $other")
}
itemRegexp.findFirstMatchIn(shopName).get.group("index") -> shopId
}
}
.getOrElse(throw deserializationError(s"Could not parse $js"))
Future { Future {
js.fields("Results") match { js.fields("fields") match {
case array: JsArray => case JsObject(fields) =>
array.elements fields
.map(_.asJsObject.getFields("ID", "GameContentLinks") match { .get("Lot")
case Seq(JsNumber(id), shop: JsObject) => id.toLong -> extractTraderId(shop.asJsObject) .collect {
case other => throw deserializationError(s"Could not parse $other") case JsBoolean(true) => PieceType.Savage
}) case JsBoolean(false) => PieceType.Tome
.toMap
case other => throw deserializationError(s"Could not parse $other")
}
}
}
private def parseXivapiJsonToType(
shops: Map[Long, (String, Long)]
)(js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[Long, PieceType]] =
Future {
val shopMap = js.fields("Results") match {
case array: JsArray =>
array.elements.collect { case shop: JsObject =>
shop.fields("ID") match {
case JsNumber(id) => id.toLong -> shop
case other => throw deserializationError(s"Could not parse $other")
} }
}.toMap .getOrElse(throw deserializationError(s"Could not find lot field in $fields"))
case other => throw deserializationError(s"Could not parse $other") case other => throw deserializationError(s"Could not read fields as object from $other")
}
shops.map { case (itemId, (index, shopId)) =>
val pieceType =
if (index == "crafted" && shopId == -1L) PieceType.Crafted
else
Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject)
.getOrElse(defaultShop)
.getFields("IsUnique", "StackSize") match {
case Seq(JsNumber(isUnique), JsNumber(stackSize)) =>
if (isUnique == 1 || stackSize.toLong != 999) PieceType.Tome // either upgraded gear or tomes found
else PieceType.Savage
case other => throw deserializationError(s"Could not parse $other")
}
itemId -> pieceType
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).
@ -40,7 +40,14 @@ trait DatabaseLootHandler { this: Database =>
case SuggestLoot(partyId, piece, client) => case SuggestLoot(partyId, piece, client) =>
run { run {
getParty(partyId, withBiS = true, withLoot = true) getParty(partyId, withBiS = true, withLoot = true)
.map(_.suggestLoot(piece)) .map(_.suggestLoot(Seq(piece)))
}(client ! _)
Behaviors.same
case SuggestMultiLoot(partyId, pieces, client) =>
run {
getParty(partyId, withBiS = true, withLoot = true)
.map(_.suggestLoot(pieces))
}(client ! _) }(client ! _)
Behaviors.same Behaviors.same
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 Evgeniy Alekseev. * Copyright (c) 2021-2026 Evgeniy Alekseev.
* *
* This file is part of ffxivbis * This file is part of ffxivbis
* (see https://github.com/arcan1s/ffxivbis). * (see https://github.com/arcan1s/ffxivbis).

View File

@ -67,6 +67,21 @@ object Fixtures {
Piece.Ring(pieceType = PieceType.Tome, Job.VPR, "right ring") Piece.Ring(pieceType = PieceType.Tome, Job.VPR, "right ring")
) )
) )
lazy val bis5: BiS = BiS(
Seq(
Piece.Weapon(pieceType = PieceType.Savage ,Job.BRD),
Piece.Head(pieceType = PieceType.Tome, Job.BRD),
Piece.Body(pieceType = PieceType.Savage, Job.BRD),
Piece.Hands(pieceType = PieceType.Savage, Job.BRD),
Piece.Legs(pieceType = PieceType.Tome, Job.BRD),
Piece.Feet(pieceType = PieceType.Tome, Job.BRD),
Piece.Ears(pieceType = PieceType.Tome, Job.BRD),
Piece.Neck(pieceType = PieceType.Savage, Job.BRD),
Piece.Wrist(pieceType = PieceType.Tome, Job.BRD),
Piece.Ring(pieceType = PieceType.Tome, Job.BRD, "left ring"),
Piece.Ring(pieceType = PieceType.Savage, Job.BRD, "right ring")
)
)
lazy val link: String = "https://ffxiv.ariyala.com/19V5R" lazy val link: String = "https://ffxiv.ariyala.com/19V5R"
lazy val link2: String = "https://ffxiv.ariyala.com/1A0WM" lazy val link2: String = "https://ffxiv.ariyala.com/1A0WM"
@ -74,6 +89,7 @@ object Fixtures {
lazy val link4: String = "https://etro.gg/gearset/865fc886-994f-4c28-8fc1-4379f160a916" lazy val link4: String = "https://etro.gg/gearset/865fc886-994f-4c28-8fc1-4379f160a916"
lazy val link5: String = "https://ffxiv.ariyala.com/1FGU0" lazy val link5: String = "https://ffxiv.ariyala.com/1FGU0"
lazy val link6: String = "https://xivgear.app/?page=sl%7Cd65b4776-01e1-4269-af74-0bc6e01ca2ec" lazy val link6: String = "https://xivgear.app/?page=sl%7Cd65b4776-01e1-4269-af74-0bc6e01ca2ec"
lazy val link7: String = "https://xivgear.app/?page=sl|22777835-b7c8-457e-bf21-6221d0d122ea"
lazy val lootWeapon: Piece = Piece.Weapon(pieceType = PieceType.Tome, Job.AnyJob) lazy val lootWeapon: Piece = Piece.Weapon(pieceType = PieceType.Tome, Job.AnyJob)
lazy val lootBody: Piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob) lazy val lootBody: Piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)

View File

@ -43,14 +43,14 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
"loot selector" must { "loot selector" must {
"suggest loot by isRequired" in { "suggest loot by isRequired" in {
toPlayerId(default.suggestLoot(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(dnc.playerId, drg.playerId) toPlayerId(default.suggestLoot(Seq(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob)))) shouldEqual Seq(dnc.playerId, drg.playerId)
} }
"suggest loot if a player already have it" in { "suggest loot if a player already have it" in {
val piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob) val piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)
val party = default.withPlayer(dnc.withLoot(piece)) val party = default.withPlayer(dnc.withLoot(piece))
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId) toPlayerId(party.suggestLoot(Seq(piece))) shouldEqual Seq(drg.playerId, dnc.playerId)
} }
"suggest upgrade" in { "suggest upgrade" in {
@ -60,26 +60,26 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
) )
) )
toPlayerId(party.suggestLoot(Piece.WeaponUpgrade)) shouldEqual Seq(dnc.playerId, drg.playerId) toPlayerId(party.suggestLoot(Seq(Piece.WeaponUpgrade))) shouldEqual Seq(dnc.playerId, drg.playerId)
} }
"suggest loot by priority" in { "suggest loot by priority" in {
val party = default.withPlayer(dnc.copy(priority = 2)) val party = default.withPlayer(dnc.copy(priority = 2))
toPlayerId(party.suggestLoot(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId) toPlayerId(party.suggestLoot(Seq(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)))) shouldEqual Seq(drg.playerId, dnc.playerId)
} }
"suggest loot by bis pieces got" in { "suggest loot by bis pieces got" in {
val party = default.withPlayer(dnc.withLoot(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob))) val party = default.withPlayer(dnc.withLoot(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob)))
toPlayerId(party.suggestLoot(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId) toPlayerId(party.suggestLoot(Seq(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)))) shouldEqual Seq(drg.playerId, dnc.playerId)
} }
"suggest loot by this piece got" in { "suggest loot by this piece got" in {
val piece = Piece.Body(pieceType = PieceType.Tome, Job.AnyJob) val piece = Piece.Body(pieceType = PieceType.Tome, Job.AnyJob)
val party = default.withPlayer(dnc.withLoot(piece)) val party = default.withPlayer(dnc.withLoot(piece))
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId) toPlayerId(party.suggestLoot(Seq(piece))) shouldEqual Seq(drg.playerId, dnc.playerId)
} }
"suggest loot by total piece got" in { "suggest loot by total piece got" in {
@ -88,7 +88,7 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
.withPlayer(dnc.withLoot(Seq(piece, piece).map(pieceToLoot))) .withPlayer(dnc.withLoot(Seq(piece, piece).map(pieceToLoot)))
.withPlayer(drg.withLoot(piece)) .withPlayer(drg.withLoot(piece))
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId) toPlayerId(party.suggestLoot(Seq(piece))) shouldEqual Seq(drg.playerId, dnc.playerId)
} }
} }

View File

@ -35,7 +35,7 @@ class BisProviderTest extends ScalaTestWithActorTestKit(Settings.withRandomDatab
probe.expectMessage(askTimeout, Fixtures.bis) probe.expectMessage(askTimeout, Fixtures.bis)
} }
"get best in slot set (etro 2)" in { "get best in slot set (etro 2)" ignore {
val probe = testKit.createTestProbe[BiS]() val probe = testKit.createTestProbe[BiS]()
provider ! DownloadBiS(Fixtures.link4, Job.DNC, probe.ref) provider ! DownloadBiS(Fixtures.link4, Job.DNC, probe.ref)
probe.expectMessage(askTimeout, Fixtures.bis2) probe.expectMessage(askTimeout, Fixtures.bis2)
@ -47,5 +47,11 @@ class BisProviderTest extends ScalaTestWithActorTestKit(Settings.withRandomDatab
probe.expectMessage(askTimeout, Fixtures.bis4) probe.expectMessage(askTimeout, Fixtures.bis4)
} }
"get best in slot set (xivgear 2)" in {
val probe = testKit.createTestProbe[BiS]()
provider ! DownloadBiS(Fixtures.link7, Job.BRD, probe.ref)
probe.expectMessage(askTimeout, Fixtures.bis5)
}
} }
} }

View File

@ -1 +1 @@
version := "0.15.5" version := "0.15.7"