Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d49d80af94 | |||
| 68cf337f48 | |||
| 4ecab127af | |||
| 61f8639a67 | |||
| 7f0866cbac | |||
| f2fbeaaa4d | |||
| abf0b9dbde | |||
| c881b8ae49 | |||
| 19eb496d35 | |||
| 156bbdb20f | |||
| 83ffa76f1f | |||
| de6c41d9f8 | |||
| af58b8602a | |||
| f2ca083dde | |||
| 117275043c | |||
| b0ae302c2f | |||
| 05963e9fec | |||
| 914fbb57e2 | |||
| 3f8409f007 | |||
| 8c51ef4da5 | |||
| 44b8e62b5e | |||
| cceae6eadd | |||
| d20d7bbbcf | |||
| c09f5681c2 | |||
| 25349fef02 | |||
| 35eca77223 | |||
| 3317241f86 | |||
| c90ab941df | |||
| eed4104ae3 | |||
| 3b54917551 | |||
| 43c2d31d4f | |||
| df414c37d0 | |||
| 3f405d1723 | |||
| 93ce4e7539 | |||
| 3d86c3b7be | |||
| 2fd314b972 | |||
| 515c51a4bc |
59
.gitea/workflows/compile.yaml
Normal file
59
.gitea/workflows/compile.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Compile and test using leiningen
|
||||
run-name: ${{ gitea.actor }} testing the code
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
clojure:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checkouts code
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Install Java
|
||||
- name: Install java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
||||
# Install Leiningen
|
||||
- name: Install Leiningen
|
||||
run: |
|
||||
curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > lein
|
||||
chmod +x lein
|
||||
sudo mv lein /usr/local/bin/lein
|
||||
|
||||
# Install dependencies
|
||||
- name: Install dependencies
|
||||
run: lein deps
|
||||
|
||||
# Optional: cache dependencies
|
||||
- name: Cache dependencias
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/project.clj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-m2-
|
||||
|
||||
# Get leiningen's version
|
||||
- name: Get leiningen version
|
||||
run: lein -v
|
||||
|
||||
# Test the code
|
||||
- name: Run tests
|
||||
env:
|
||||
TFT_API: ${{ secrets.DEV_API }}
|
||||
run: lein test
|
||||
|
||||
# Send jar to repository
|
||||
- name: Deploy on Gitea Maven
|
||||
if: github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITEA_USER: ${{ secrets.DEPLOY_USER }}
|
||||
GITEA_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
|
||||
run: |
|
||||
lein deploy gitea
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,3 +14,5 @@ pom.xml.asc
|
||||
.clj-kondo
|
||||
.lsp
|
||||
.calva
|
||||
*.svg
|
||||
/logs
|
||||
|
||||
77
README.md
77
README.md
@@ -1,22 +1,53 @@
|
||||
# riot-clojure
|
||||
|
||||
This is a simple tool for using Riot Games public API. Those APIs are HUGE and
|
||||
privide a lot of information, but this tool is focused in the time data of matches.
|
||||
provide a lot of information, but this tool is focused in the time data of matches.
|
||||
|
||||
The objetive of this tool is extract the playing time of one player. Taking the
|
||||
starting and ending timestams we can create a chronogram with the time spent
|
||||
starting and ending timestamps we can create a chronogram with the time spent
|
||||
playing.
|
||||
|
||||
This simple program needs a developer API. You can obtain a key from https://developer.riotgames.com/
|
||||
Developer keys are valid for 24 hours only, but you can refresh the key easily
|
||||
from the developer's web site.
|
||||
This simple program needs two API keys: one for LOL and another one for TFT. You
|
||||
can obtain a developer key from https://developer.riotgames.com/. Developer keys
|
||||
are valid for 24 hours only, but you can refresh the key easily from the developer's
|
||||
web site.
|
||||
|
||||
By default, the application searchs for the API key in the environment variable
|
||||
`RIOT_API`. If you prefer to provide the key via parameter, you must use `-k KEY` or
|
||||
`--key=KEY`.
|
||||
By default, the application searchs for the API key in the environment variables
|
||||
`LOL_API` and `TFT_API`. If you prefer to provide the key via parameter, you must use `--lol-api-key` or
|
||||
`--tft-api-key` parameters.
|
||||
|
||||
## Features
|
||||
|
||||
Those features are implemented in v1.1
|
||||
|
||||
* Call the API with development or production keys
|
||||
* Get a list of matches
|
||||
* Get matches between two dates
|
||||
* Check if player is currently playing
|
||||
* Show the match's data in several formats:
|
||||
* Fancy ASCII table (by default)
|
||||
* Simple ASCII table
|
||||
* Clojure native EDN format
|
||||
* JSON
|
||||
* CSV
|
||||
* Select data fields to show
|
||||
* Format dates and durations
|
||||
* Show the result of the match (win/loss)
|
||||
* Bulk request for hundred of matches
|
||||
* Generate native executables
|
||||
|
||||
## Goals
|
||||
|
||||
Goals for version 1.1:
|
||||
|
||||
* [x] Simplify CLI options and parameters
|
||||
* [x] Calculate simple statistics: win/loss rate, hours played every day, etc.
|
||||
* [x] Get more data and delete some unused fields
|
||||
* [x] Clean and refactor some code
|
||||
* [x] Logging
|
||||
* [x] Get only LOL or TFT data
|
||||
* [x] Use Gitea Actions
|
||||
|
||||
Goals of the project, ordered from easiest to hardest:
|
||||
|
||||
* [x] Take API key from environment
|
||||
@@ -39,7 +70,7 @@ Goals of the project, ordered from easiest to hardest:
|
||||
* [x] Win / Loss match
|
||||
* [x] Distribution as uberjar
|
||||
* [x] Distribution as native Linux executable
|
||||
* [ ] Unit tests: move "comment" manual tests to real unit tests
|
||||
* [x] Unit tests: move "comment" manual tests to real unit tests
|
||||
* [ ] Output as a graphical chronogram or calendar.
|
||||
* [ ] Simple web server
|
||||
* [ ] Universal player search
|
||||
@@ -49,29 +80,39 @@ Goals of the project, ordered from easiest to hardest:
|
||||
|
||||
## Installation
|
||||
|
||||
Download from http://example.com/FIXME.
|
||||
Download release from https://git.rcorral.es/ruben/riot-clojure/releases.
|
||||
|
||||
## Usage
|
||||
|
||||
FIXME: explanation
|
||||
Using the Java uberjar
|
||||
|
||||
$ java -jar riot-clojure-0.1.0-standalone.jar [args]
|
||||
|
||||
Using the linux native image:
|
||||
$ ./riot [args]
|
||||
|
||||
Both are equivalent. In the examples we will use the native image, because is shorter.
|
||||
|
||||
## Options
|
||||
|
||||
FIXME: listing of options this app accepts.
|
||||
Run the application without params or wich `-?` param to show all options:
|
||||
|
||||
$ ./riot -?
|
||||
|
||||
## Examples
|
||||
|
||||
...
|
||||
Show all matches from a march the 1st (in ISO format):
|
||||
|
||||
### Bugs
|
||||
$ ./riot t <username> <tag> -s "2025-03-01"
|
||||
|
||||
...
|
||||
Get te same data in CSV and store it in a file:
|
||||
|
||||
### Any Other Sections
|
||||
### That You Think
|
||||
### Might be Useful
|
||||
$ ./riot t <username> <tag> -s "2025-03-01" -o csv > results.csv
|
||||
|
||||
Don't format durations, show them in seconds
|
||||
|
||||
$ ./riot t <username> <tag> -s "2025-03-01" --no-format-durations
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
||||
33
project.clj
33
project.clj
@@ -1,4 +1,4 @@
|
||||
(defproject riot-clojure "1.0.0"
|
||||
(defproject riot-clojure "1.1.0"
|
||||
:description "Utility for getting for Riot APIs in Clojure"
|
||||
:url "https://git.rcorral.es/ruben/riot-clojure"
|
||||
|
||||
@@ -10,10 +10,15 @@
|
||||
[cheshire/cheshire "6.0.0"]
|
||||
[slingshot/slingshot "0.12.2"]
|
||||
[org.clojure/tools.cli "1.1.230"]
|
||||
[cli-matic/cli-matic "0.5.4"]
|
||||
[cli-matic/cli-matic "0.5.4"] ;; https://github.com/l3nz/cli-matic
|
||||
[buddy/buddy-core "1.12.0-430"]
|
||||
[org.clojure/tools.namespace "1.5.0"]
|
||||
[lt.tokenmill/timewords "0.5.0"]]
|
||||
[lt.tokenmill/timewords "0.5.0"] ;; https://github.com/tokenmill/timewords
|
||||
[org.clj-commons/pretty "3.5.0"] ;; https://github.com/clj-commons/pretty
|
||||
[org.clojure/tools.logging "1.3.0"] ;; https://github.com/clojure/tools.logging
|
||||
[org.slf4j/slf4j-api "2.0.17"] ;; https://www.slf4j.org/
|
||||
[ch.qos.logback/logback-classic "1.5.18"]]
|
||||
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory"]
|
||||
|
||||
:plugins [[io.taylorwood/lein-native-image "0.3.1"]]
|
||||
|
||||
@@ -23,8 +28,26 @@
|
||||
:native-image {:name "riot" ;; name of output image, optional
|
||||
:graal-bin "/home/ruben/.sdkman/candidates/java/21.0.2-graalce/" ;; path to GraalVM home, optional
|
||||
:opts ["--verbose"]} ;; pass-thru args to GraalVM native-image, optional
|
||||
|
||||
|
||||
:profiles {:uberjar {:aot :all
|
||||
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}
|
||||
:native-image {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}
|
||||
:dev {:dependencies [[org.clojure/test.check "1.1.1"]]}})
|
||||
:dev {:dependencies [[org.clojure/test.check "1.1.1"]]
|
||||
:plugins [[io.taylorwood/lein-native-image "0.3.1"]
|
||||
[lein-binplus "0.6.8"]]}}
|
||||
|
||||
;; lein bin configuration
|
||||
:bin {:name "riot_clj"
|
||||
:bin-path "~/bin"
|
||||
:jvm-opts ["-server" "-Dfile.encoding=utf-8" "$JVM_OPTS"]}
|
||||
|
||||
;; Deploy to repository
|
||||
:repositories {"gitea"
|
||||
{:url "https://git.rcorral.es/api/packages/ruben/maven"
|
||||
:username :env/DEPLOY_USER
|
||||
:password :env/DEPLOY_TOKEN}}
|
||||
|
||||
;; Test selectors
|
||||
:test-selectors {:default (complement (some-fn :tft :timezone))
|
||||
:tft :tft
|
||||
:timezone :timezone})
|
||||
|
||||
36
resources/logback.xml
Normal file
36
resources/logback.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<configuration>
|
||||
|
||||
<!-- Appender a consola -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<!--<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
|
||||
<pattern>[%-5level] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Appender a archivo con rotación -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>logs/riot-app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- rota cada día, conserva 7 días -->
|
||||
<fileNamePattern>logs/riot-app.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>7</maxHistory>
|
||||
</rollingPolicy>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{ISO8601} %-5level %logger{36}:%L - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Nivel de log raíz -->
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
362
src/riot/app.clj
362
src/riot/app.clj
@@ -6,7 +6,9 @@
|
||||
[cli-matic.utils :as U]
|
||||
[clojure.pprint :as pp]
|
||||
[clojure.string :as str]
|
||||
[timewords.core :refer [parse]])
|
||||
[timewords.core :refer [parse]]
|
||||
[clojure.tools.logging :as log]
|
||||
)
|
||||
(:use [riot.core]
|
||||
[riot.data]
|
||||
[slingshot.slingshot :only [try+]])
|
||||
@@ -22,158 +24,115 @@
|
||||
(some? puuid) puuid
|
||||
(and (some? name) (some? tag)) (get-puuid-from-name name tag :api-key api-key)))
|
||||
|
||||
(comment
|
||||
(get-puuid-from-params (get-lol-api-key) [] :puuid "annlAfxhJnTwlwRgQZYbGeQpD3jWb-ju7vVKEW_g-EIJf6xQT0eeb-0obARVekrksf8n9XCjcxyHHQ")
|
||||
(get-puuid-from-params (get-lol-api-key) [] :name "Errepunto" :tag "4595")
|
||||
|
||||
(get-puuid-from-params (get-tft-api-key) [] :puuid "annlAfxhJnTwlwRgQZYbGeQpD3jWb-ju7vVKEW_g-EIJf6xQT0eeb-0obARVekrksf8n9XCjcxyHHQ")
|
||||
(get-puuid-from-params (get-tft-api-key) [] :name "Errepunto" :tag "4595"))
|
||||
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defn cmd-active
|
||||
"Checks if a player is online at the moment"
|
||||
[& {:keys [lol-api-key tft-api-key debug-http _arguments]
|
||||
[& {:keys [lol-api-key tft-api-key debug-http lol tft _arguments]
|
||||
:as opts}]
|
||||
(try+
|
||||
;(println "Check online" opts)
|
||||
(let [lol-key (get-lol-api-key lol-api-key)
|
||||
tft-key (get-tft-api-key tft-api-key)
|
||||
lol-id (get-puuid-from-params lol-key _arguments opts)
|
||||
tft-id (get-puuid-from-params tft-key _arguments opts)]
|
||||
;(println "LOL API key" lol-key "TFT API key" tft-key)
|
||||
(if (or (nil? lol-id) (nil? tft-id))
|
||||
(U/exit! "Invalid params" 2)
|
||||
(let [lol-key (when lol (get-lol-api-key lol-api-key))
|
||||
tft-key (when tft (get-tft-api-key tft-api-key))
|
||||
lol-id (when lol (get-puuid-from-params lol-key _arguments opts))
|
||||
tft-id (when tft (get-puuid-from-params tft-key _arguments opts))]
|
||||
(if (and (nil? lol-id) (nil? tft-id))
|
||||
(println "No games to check")
|
||||
(if (is-playing? lol-id tft-id :lol-api-key lol-key :tft-api-key tft-key :debug debug-http)
|
||||
(println "Yes, it's playing")
|
||||
(println "No, it's not playing right now"))))
|
||||
(do (println "Yes, it's playing") true)
|
||||
(do (println "No, it's not playing right now") false))))
|
||||
(catch [:status 401] _
|
||||
(U/exit! "Invalid API key" 3))
|
||||
(catch [:status 404] _
|
||||
(U/exit! "Unknown user or tag" 5))))
|
||||
|
||||
(comment
|
||||
(cmd-active :_arguments ["annlAfxhJnTwlwRgQZYbGeQpD3jWb-ju7vVKEW_g-EIJf6xQT0eeb-0obARVekrksf8n9XCjcxyHHQ"])
|
||||
(cmd-active :_arguments ["Errepunto" "4595"]))
|
||||
|
||||
|
||||
|
||||
(defn format-map
|
||||
(defn format-result
|
||||
"Format result"
|
||||
[output x]
|
||||
(case output
|
||||
"table" (as-ascii-table x)
|
||||
"ptable" (as-pretty-table x)
|
||||
"edn" (pp/pprint x)
|
||||
"json" (println (as-json x))
|
||||
"table" (as-ascii-table x)
|
||||
"csv" (println (as-csv x))
|
||||
(pp/pprint x))) ;edn as default
|
||||
|
||||
(comment
|
||||
(format-map "json" riot.test-examples/matches-example)
|
||||
(format-map "csv" riot.test-examples/matches-example2))
|
||||
|
||||
|
||||
|
||||
(defn format-dates-cond
|
||||
[format-dates format-durations pattern x]
|
||||
(let [dur (if format-durations
|
||||
(with-parsed-durations x)
|
||||
x)]
|
||||
"Format dates and durations if specified"
|
||||
[format-dates pattern x]
|
||||
(if format-dates
|
||||
(if (some? pattern)
|
||||
(with-parsed-dates dur :datetime-format pattern)
|
||||
(with-parsed-dates dur))
|
||||
dur)))
|
||||
|
||||
(comment
|
||||
(format-dates-cond false false nil riot.test-examples/matches-example2)
|
||||
(format-dates-cond false true nil riot.test-examples/matches-example2)
|
||||
(format-dates-cond true false nil riot.test-examples/matches-example2)
|
||||
(format-dates-cond true true nil riot.test-examples/matches-example2))
|
||||
(with-parsed-dates x :datetime-format pattern)
|
||||
(with-parsed-dates x))
|
||||
x))
|
||||
|
||||
|
||||
(defn format-duration-cond
|
||||
"Format durations if specified"
|
||||
[format-durations x]
|
||||
(if format-durations
|
||||
(with-parsed-durations x)
|
||||
x))
|
||||
|
||||
|
||||
(defn calculate-statistics-cond
|
||||
"If true, calculate statistics and print them to console"
|
||||
[calculate x]
|
||||
(when calculate
|
||||
(log/info "Calculating statistics")
|
||||
(pp/pprint (calculate-statistics x)))
|
||||
x)
|
||||
|
||||
(defn reverse-cond
|
||||
"If true, data is ordered from nearest to fartest"
|
||||
[rev x] (if rev x (reverse x)))
|
||||
|
||||
|
||||
(defn filter-today-cond
|
||||
[only-today x] (if only-today (filter match-today? x) x))
|
||||
|
||||
|
||||
(defn filter-columns
|
||||
"Filter columns to show. With 'all' shows all columns"
|
||||
[columns x] (if (= "all" columns)
|
||||
x
|
||||
(map #(select-keys % (map keyword (str/split columns #","))) x)))
|
||||
|
||||
(comment
|
||||
(filter-columns "all" riot.test-examples/matches-example2)
|
||||
(filter-columns "start,end" riot.test-examples/matches-example2)
|
||||
|
||||
(map #(select-keys % (map keyword (str/split "start,end" #","))) riot.test-examples/matches-example2))
|
||||
|
||||
(defn get-date
|
||||
"Converts an string into a java.util.Date"
|
||||
[x]
|
||||
(if (nil? x) nil (parse x)))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defn cmd-timeline
|
||||
"Get match "
|
||||
[& {:keys [lol-api-key tft-api-key output count max-total format-dates format-durations date-format reverse order-by show-columns show-active debug-http only-today since until _arguments]
|
||||
:as opts}]
|
||||
(try+
|
||||
;(println "Get timeline" opts)
|
||||
(let [lol-key (get-lol-api-key lol-api-key)
|
||||
tft-key (get-tft-api-key tft-api-key)
|
||||
lol-id (get-puuid-from-params lol-key _arguments opts)
|
||||
tft-id (get-puuid-from-params tft-key _arguments opts)]
|
||||
;(println "LOL API key" lol-key "TFT API key" tft-key)
|
||||
(if (or (nil? lol-id) (nil? tft-id))
|
||||
(U/exit! "Invalid params" 2)
|
||||
(->> (get-last-matches lol-id tft-id :lol-api-key lol-key :tft-api-key tft-key :count count :include-current show-active :debug debug-http :order-by (keyword order-by) :since (get-date since) :until (get-date until))
|
||||
(filter-today-cond only-today)
|
||||
(reverse-cond reverse)
|
||||
(take count)
|
||||
(format-dates-cond format-dates format-durations date-format)
|
||||
(filter-columns show-columns)
|
||||
(map)
|
||||
(format-map output))))
|
||||
(catch [:status 401] _
|
||||
(U/exit! "Invalid API key" 3))
|
||||
(catch [:status 404] _
|
||||
(U/exit! "Unknown user or tag" 5))
|
||||
(catch [:status 429] _
|
||||
(U/exit! "Rate limit exceeded, please wait some minutes and try again" 4))))
|
||||
|
||||
(comment
|
||||
(cmd-timeline :_arguments ["dMNR4Aj5OW9jrGj0RUVBRuzfu77p3iO5y1W16ASNp1PI7pxuJWLz14b2pJiUn16DCPlyeREoi0MJ7Q"])
|
||||
(cmd-timeline :_arguments ["Sarinailo" "EUW"]))
|
||||
|
||||
|
||||
(defn cmd-bulk
|
||||
"Get all matches"
|
||||
[& {:keys [lol-api-key tft-api-key output format-dates format-durations date-format reverse order-by show-columns debug-http since until _arguments]
|
||||
[& {:keys [lol-api-key tft-api-key lol tft output format-dates format-durations date-format reverse order-by show-columns debug-http since until statistics _arguments]
|
||||
:as opts}]
|
||||
(try+
|
||||
;(println "Get timeline" opts)
|
||||
(let [lol-key (get-lol-api-key lol-api-key)
|
||||
tft-key (get-tft-api-key tft-api-key)
|
||||
lol-id (get-puuid-from-params lol-key _arguments opts)
|
||||
tft-id (get-puuid-from-params tft-key _arguments opts)]
|
||||
;(println "LOL API key" lol-key "TFT API key" tft-key)
|
||||
(if (or (nil? lol-id) (nil? tft-id))
|
||||
(U/exit! "Invalid params" 2)
|
||||
(->> (get-matches-info-batch lol-id tft-id :lol-api-key lol-key :tft-api-key tft-key :debug debug-http :order-by (keyword order-by) :since (get-date since) :until (get-date until))
|
||||
(let [lol-key (when lol (get-lol-api-key lol-api-key))
|
||||
tft-key (when tft (get-tft-api-key tft-api-key))
|
||||
lol-id (when lol (get-puuid-from-params lol-key _arguments opts))
|
||||
tft-id (when tft (get-puuid-from-params tft-key _arguments opts))
|
||||
since-date (get-date since)
|
||||
until-date (get-date until)]
|
||||
(log/info (str "Fetching data between " since-date " and " (if (some? until-date) until-date (java.util.Date.))))
|
||||
(if (and (nil? lol-id) (nil? tft-id))
|
||||
(U/exit! "No games selected" 2)
|
||||
(->> (get-matches-info-batch lol-id tft-id :lol-api-key lol-key :tft-api-key tft-key :debug debug-http :order-by (keyword order-by) :since since-date :until until-date)
|
||||
(calculate-statistics-cond statistics)
|
||||
(reverse-cond reverse)
|
||||
(format-dates-cond format-dates format-durations date-format)
|
||||
(format-dates-cond format-dates date-format)
|
||||
(format-duration-cond format-durations)
|
||||
(filter-columns show-columns)
|
||||
(format-map output))))
|
||||
(format-result output))))
|
||||
(catch [:status 401] _
|
||||
(U/exit! "Invalid API key" 3))
|
||||
(catch [:status 404] _
|
||||
(U/exit! "Unknown user or tag" 5))
|
||||
(catch [:status 429] _
|
||||
(U/exit! "Rate limit exceeded, please wait some minutes and try again" 4))
|
||||
(catch [:status 502] _
|
||||
(U/exit! "Server timeout, please wait some minutes and try again" 6))
|
||||
(catch [:status 503] _
|
||||
(U/exit! "Server timeout, please wait some minutes and try again" 6))))
|
||||
|
||||
@@ -181,17 +140,25 @@
|
||||
;; More info: https://github.com/l3nz/cli-matic/blob/master/README.md
|
||||
(def CONFIGURATION
|
||||
{:app {:command "riot"
|
||||
:description ["Get how much time have you spent on LoL or FTF"
|
||||
:version "1.1.0"
|
||||
:description ["Get how much you play your favourite games"
|
||||
""
|
||||
"Posible error codes:"
|
||||
" 0: OK"
|
||||
" 1: player not playing"
|
||||
" 2: Invalid params"
|
||||
" 3: Invalid API key, please, get an updated one"
|
||||
" 4: Rate limit exceeded, please wait some minutes and try again"
|
||||
" 5: Unknown user and tag"
|
||||
" 6: Server error. Please, try again"]
|
||||
:version "1.0.0"}
|
||||
"EXAMPLE USAGES:"
|
||||
" Get all matches since March 1st 2025:"
|
||||
" riot timeline <your_player_name> <your_tag> -s 2025-03-01"
|
||||
""
|
||||
" Get all matches as CSV, don't format durations:"
|
||||
" riot timeline <your_player_name> <your_tag> -s 2025-03-01 -o csv --no-format-durations > my_matches.csv"
|
||||
""
|
||||
"ERROR CODES:"
|
||||
" 0: OK, no error"
|
||||
" 1: player not playing"
|
||||
" 2: Invalid params"
|
||||
" 3: Invalid API key, please, get and configure an updated one"
|
||||
" 4: Rate limit exceeded, please wait some minutes and try again"
|
||||
" 5: Unknown user and tag"
|
||||
" 6: Server error. Please, try again"]}
|
||||
|
||||
:global-opts [{:option "lol-api-key"
|
||||
:as "API key for LOL data"
|
||||
:type :string :default nil
|
||||
@@ -203,114 +170,49 @@
|
||||
{:option "debug-http"
|
||||
:as "Show debug for HTTP connections. I'ts very verbose!"
|
||||
:type :flag
|
||||
:default false}]
|
||||
:default false}
|
||||
;; {:option "log-level"
|
||||
;; :as "Log level (debug, info, warn, etc)"
|
||||
;; :type :keyword
|
||||
;; :default :info}
|
||||
]
|
||||
|
||||
:commands [{:command "active" :short "a"
|
||||
:description ["Shows if the player currently playing"
|
||||
"You can search by PUUID or name and tagline, but not both."
|
||||
""
|
||||
"You can set the PUUID passing only one argument or using -p param."
|
||||
"You can set the name and tag passing two arguments, or using -n and -t."]
|
||||
:opts [{:option "puuid" :short "p"
|
||||
:type :string
|
||||
:as "Summonner's unique PUUID."}
|
||||
{:option "name" :short "n"
|
||||
:type :string :default nil
|
||||
:as "Summoner name (Riot ID)"}
|
||||
{:option "tag" :short "t"
|
||||
:type :string :default nil
|
||||
:as "Summoner tagline"}]
|
||||
:description ["Shows if the player is currently playing"
|
||||
"By default only shows LOL matches"]
|
||||
:opts [{:option "lol"
|
||||
:type :with-flag :default true
|
||||
:as "Include LOL matches"}
|
||||
{:option "tft"
|
||||
:type :with-flag :default false
|
||||
:as "Include TFT matches. Experimental"}]
|
||||
:runs cmd-active}
|
||||
{:command "last" :short "l"
|
||||
:description ["Gets time info from last matches."
|
||||
"You can search by PUUID or name and tagline, but not both."
|
||||
""
|
||||
"You can set the PUUID passing only one argument or using -p param."
|
||||
"You can set the name and tag passing two arguments, or using -n and -t."]
|
||||
:opts [{:option "puuid" :short "p"
|
||||
:type :string
|
||||
:as "Summonner's unique PUUID."}
|
||||
{:option "name" :short "n"
|
||||
:type :string :default nil
|
||||
:as "Summoner name (Riot ID)"}
|
||||
{:option "tag" :short "t"
|
||||
:type :string :default nil
|
||||
:as "Summoner tagline"}
|
||||
{:option "output" :short "o"
|
||||
:type #{"edn" "json" "table" "csv"} :default "table"
|
||||
:as ["Output type. It can be one of:"
|
||||
" - edn: Clojure's internal EDN format"
|
||||
" - json: Classic JSON"
|
||||
" - table: ASCII table (by default)"
|
||||
" - csv: CSV data, using ',' as field separator"]}
|
||||
{:option "count" :short "c"
|
||||
:type :int :default 10
|
||||
:as "Max number of matches retrieved for each type of game (max value: 100)"}
|
||||
;{:option "max-total" :short "m"
|
||||
; :type :int :default 10
|
||||
; :as "Max number of matches of all types, including current match"}
|
||||
{:option "order-by"
|
||||
:type #{"start" "end" "duration"} :default "start"
|
||||
:as "Order by field"}
|
||||
{:option "reverse" :short "r"
|
||||
:type :with-flag :default false
|
||||
:as "If true, newer matches are shown last in list"}
|
||||
{:option "show-active" :short "a"
|
||||
:type :with-flag :default true
|
||||
:as "Include current playing match"}
|
||||
{:option "format-dates"
|
||||
:type :with-flag :default true
|
||||
:as "Show formatted dates"}
|
||||
{:option "format-durations"
|
||||
:type :with-flag :default true
|
||||
:as "Show formatted durations"}
|
||||
{:option "date-format"
|
||||
:type :string :default nil
|
||||
:as "If format-dates is enabled, specifies the formatter's string"}
|
||||
{:option "show-columns"
|
||||
:type :string :default "all"
|
||||
:as ["Show only selected columns, separated by comma. Supported columns:"
|
||||
" - all: All columns"
|
||||
" - start: Start timestamp"
|
||||
" - end: End timestamp"
|
||||
" - duration: Duration in seconds"
|
||||
" - active: Match is in course"
|
||||
" - game-type: lol or tft"
|
||||
" - id: Game unique id"
|
||||
" - winner: Winner of the match"]}
|
||||
{:option "only-today"
|
||||
:type :flag :default false
|
||||
:as "If true, only shows today played matches"}
|
||||
{:option "since" :short "s"
|
||||
:type :string :default nil
|
||||
:as ["Starting date (included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: today, yesterday"]}
|
||||
{:option "until" :short "u"
|
||||
:type :string :default nil
|
||||
:as ["Ending date (not included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: today, yesterday, tomorrow"]}]
|
||||
:runs cmd-timeline}
|
||||
|
||||
{:command "timeline" :short "t"
|
||||
:description ["Get info from LOL and TFT matches in a time range."
|
||||
"Warning: it can take several minutes to obtain all data, due to api restrictions."
|
||||
"You can search by PUUID or name and tagline, but not both."
|
||||
:description ["Get info from LOL and TFT (experimental, disabled by default) matches in a time range."
|
||||
""
|
||||
"You can set the PUUID passing only one argument or using -p param."
|
||||
"You can set the name and tag passing two arguments, or using -n and -t."]
|
||||
:opts [{:option "puuid" :short "p"
|
||||
:type :string
|
||||
:as "Summonner's unique PUUID."}
|
||||
{:option "name" :short "n"
|
||||
"Warning: it can take several minutes to obtain all data, due to API restrictions."]
|
||||
:opts [{:option "lol"
|
||||
:type :with-flag :default true
|
||||
:as "Include LOL matches"}
|
||||
{:option "tft"
|
||||
:type :with-flag :default false
|
||||
:as "Include TFT matches. Experimental"}
|
||||
{:option "since" :short "s"
|
||||
:type :string :default "today"
|
||||
:as ["Starting date (included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: 'today', 'yesterday', 'last monday', etc"]}
|
||||
{:option "until" :short "u"
|
||||
:type :string :default nil
|
||||
:as "Summoner name (Riot ID)"}
|
||||
{:option "tag" :short "t"
|
||||
:type :string :default nil
|
||||
:as "Summoner tagline"}
|
||||
:as ["Ending date (not included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: today, yesterday, tomorrow, etc"]}
|
||||
{:option "output" :short "o"
|
||||
:type #{"edn" "json" "table" "csv"} :default "table"
|
||||
:as ["Output type. It can be one of:"
|
||||
:type #{"table" "ptable" "edn" "json" "csv"} :default "ptable"
|
||||
:as ["Output data format. It can be one of:"
|
||||
" - table: ASCII table"
|
||||
" - ptable: Pretty ASCII table (default)"
|
||||
" - edn: Clojure's internal EDN format"
|
||||
" - json: Classic JSON"
|
||||
" - table: ASCII table (by default)"
|
||||
" - csv: CSV data, using ',' as field separator"]}
|
||||
{:option "order-by"
|
||||
:type #{"start" "end" "duration"} :default "start"
|
||||
@@ -337,50 +239,22 @@
|
||||
" - game-type: lol or tft"
|
||||
" - id: Game unique id"
|
||||
" - winner: Winner of the match"]}
|
||||
{:option "only-today"
|
||||
:type :flag :default false
|
||||
:as "If true, only shows today played matches"}
|
||||
{:option "since" :short "s"
|
||||
:type :string :default nil
|
||||
:as ["Starting date (included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: today, yesterday, etc"]}
|
||||
{:option "until" :short "u"
|
||||
:type :string :default nil
|
||||
:as ["Ending date (not included) in 'yyyy-MM-dd' format."
|
||||
"Other valid values: today, yesterday, tomorrow, etc"]}]
|
||||
:runs cmd-bulk}]})
|
||||
{:option "statistics"
|
||||
:type :with-flag :default true
|
||||
:as "Show simple statistics"}]
|
||||
:runs cmd-timeline}]})
|
||||
|
||||
|
||||
(defn -main
|
||||
"Main entry point"
|
||||
[& args]
|
||||
(log/debug "")
|
||||
(log/debug "** Starting application **")
|
||||
(log/debug "")
|
||||
(run-cmd args CONFIGURATION))
|
||||
|
||||
|
||||
|
||||
(defn old-main
|
||||
"Testing some basic things"
|
||||
[& args]
|
||||
(let [player "RdNVioNKYzvFuXI00zurL_8QvaU0E8P61NU0SzwIfbbHHk9HFvxLtSWiDHKuJ9iXb4UC0UUZ3ltLxw"]
|
||||
;(println "API Key: " (get-lol-api-key))
|
||||
(println "Last matches:")
|
||||
(println "Player PUUID: " player)
|
||||
;(as-json
|
||||
;(as-ascii-table
|
||||
; (with-parsed-dates
|
||||
; (take 10
|
||||
; (reverse
|
||||
; (get-last-matches player :count 10 :print-not-active true)))))
|
||||
(as-ascii-table
|
||||
(with-parsed-dates-durations
|
||||
; (filter match-today? ; Only today
|
||||
(reverse (get-last-matches player :count 10 :print-not-active true))))
|
||||
;)
|
||||
))
|
||||
|
||||
|
||||
(comment
|
||||
; Launch manually
|
||||
(-main)
|
||||
|
||||
(old-main))
|
||||
)
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
(ns riot.core
|
||||
(:require [clj-http.client :as client]
|
||||
[buddy.core.crypto :as crypto]
|
||||
[buddy.core.codecs :as codecs])
|
||||
[buddy.core.codecs :as codecs]
|
||||
[clojure.tools.logging :as log]
|
||||
)
|
||||
(:use [slingshot.slingshot :only [try+]])
|
||||
(:gen-class))
|
||||
|
||||
@@ -33,7 +35,7 @@
|
||||
|
||||
;;;; API KEYS
|
||||
|
||||
(def DEV_KEY "RGAPI-fc8c6d88-2c13-4a43-b53f-3465bb564acc")
|
||||
(def DEV_KEY "RGAPI-ec3779d1-bc61-4a2e-a071-34addcc6bd56")
|
||||
(def LOL_KEY "jXL+gA3LIeBPBvrOhLOYSZCiURC7eOtwMXahkxtwpdj6JDtT5NMu25zMz+UY2+9MuHBADjUJh46jSanrV5OBag==")
|
||||
(def TFT_KEY (encrypt-data DEV_KEY secret-key iv))
|
||||
|
||||
@@ -102,61 +104,54 @@
|
||||
[data response puuid]
|
||||
(assoc data :winner (winner? response puuid)))
|
||||
|
||||
(comment
|
||||
(get-in riot.test-examples/response-lol-match [:metadata :participants])
|
||||
(get-in riot.test-examples/response-lol-match [:info :participants 3])
|
||||
|
||||
(get-player-info riot.test-examples/response-lol-match riot.test-examples/example-lol-puuid)
|
||||
(winner? riot.test-examples/response-lol-match riot.test-examples/example-lol-puuid))
|
||||
|
||||
|
||||
(defn with-winner-status
|
||||
"Takes the original list of matches and parses durations"
|
||||
"Takes the original list of matches and adds winner info"
|
||||
[matches puuid]
|
||||
(map #(assoc % :winner (winner? % puuid)) matches))
|
||||
|
||||
|
||||
(comment
|
||||
(with-winner-status riot.test-examples/matches-example riot.test-examples/example-lol-puuid))
|
||||
|
||||
|
||||
;; json adapters
|
||||
(def player-info-parser {:parser [(make-json-data [:puuid] :puuid)]})
|
||||
|
||||
(def lol-match-parser {:parser [(make-json-data [:info :gameCreation] :start)
|
||||
(make-json-data [:info :gameEndTimestamp] :end)
|
||||
(make-json-data [:info :gameDuration] :duration)
|
||||
(make-json-data [:none] :active (constantly false))
|
||||
;; (make-json-data [:none] :active (constantly false))
|
||||
(make-json-data [:none] :game-type (constantly "lol"))
|
||||
(make-json-data [:metadata :matchId] :id)
|
||||
(make-json-data [:none] :winner (constantly false))]
|
||||
(make-json-data [:none] :winner (constantly false))
|
||||
(make-json-data [:info :endOfGameResult] :result)]
|
||||
:post post-calculate-win})
|
||||
|
||||
(def tft-match-parser {:parser [(make-json-data [:info :gameCreation] :start)
|
||||
(make-json-data [:none] :end (constantly -1))
|
||||
(make-json-data [:info :game_length] :duration #(int %)) ; rounds to integer
|
||||
(make-json-data [:none] :active (constantly false))
|
||||
(make-json-data [:none] :game-type (constantly "ftf"))
|
||||
;; (make-json-data [:none] :active (constantly false))
|
||||
(make-json-data [:none] :game-type (constantly "tft"))
|
||||
(make-json-data [:metadata :match_id] :id)
|
||||
(make-json-data [:none] :winner (constantly false))]
|
||||
(make-json-data [:none] :winner (constantly false))
|
||||
(make-json-data [:info :endOfGameResult] :result)]
|
||||
:post #(post-calculate-win
|
||||
(post-calculate-end %1 %2 %3) %2 %3)}) ; end = start + (duration * 1000)
|
||||
|
||||
(def lol-current-parser {:parser [(make-json-data [:gameStartTime] :start)
|
||||
(make-json-data [:none] :end (constantly nil))
|
||||
(make-json-data [:gameLength] :duration)
|
||||
(make-json-data [:none] :active (constantly true))
|
||||
;; (make-json-data [:none] :active (constantly true))
|
||||
(make-json-data [:none] :game-type (constantly "lol"))
|
||||
(make-json-data [:gameId] :id)
|
||||
(make-json-data [:none] :winner (constantly nil))]})
|
||||
(make-json-data [:none] :winner (constantly nil))
|
||||
(make-json-data [:none] :result (constantly nil))]})
|
||||
|
||||
(def tft-current-parser {:parser [(make-json-data [:gameStartTime] :start)
|
||||
(make-json-data [:none] :end (constantly nil))
|
||||
(make-json-data [:gameLength] :duration)
|
||||
(make-json-data [:none] :active (constantly true))
|
||||
(make-json-data [:none] :game-type (constantly "ftf"))
|
||||
;; (make-json-data [:none] :active (constantly true))
|
||||
(make-json-data [:none] :game-type (constantly "tft"))
|
||||
(make-json-data [:gameId] :id)
|
||||
(make-json-data [:none] :winner (constantly nil))]})
|
||||
(make-json-data [:none] :winner (constantly nil))
|
||||
(make-json-data [:none] :result (constantly nil))]})
|
||||
|
||||
|
||||
(defn parse-response
|
||||
@@ -186,6 +181,7 @@
|
||||
:or {params nil
|
||||
debug false}}]
|
||||
(when debug (println "** SENDING REQUEST **"))
|
||||
(log/trace "Sending request. Params: " params)
|
||||
;; (println "Params: " params)
|
||||
(:body (client/get url
|
||||
{:debug debug
|
||||
@@ -242,8 +238,10 @@
|
||||
debug false
|
||||
server "europe"
|
||||
count 20
|
||||
start 0}}]
|
||||
start 0}
|
||||
:as params}]
|
||||
;; (println "get lol start: " start)
|
||||
(log/trace "Getting list of LOL matches. Params: " params)
|
||||
(raw-get-query
|
||||
(create-endpoint server "/lol/match/v5/matches/by-puuid/" puuid "/ids")
|
||||
:api-key api-key
|
||||
@@ -258,7 +256,9 @@
|
||||
debug false
|
||||
server "europe"
|
||||
count 20
|
||||
start 0}}]
|
||||
start 0}
|
||||
:as params}]
|
||||
(log/trace "Getting list of TFT matches. Params: " params)
|
||||
(raw-get-query
|
||||
(create-endpoint server "/tft/match/v1/matches/by-puuid/" puuid "/ids")
|
||||
:api-key api-key
|
||||
@@ -274,6 +274,7 @@
|
||||
debug false
|
||||
server "europe"}}]
|
||||
;; (println "get-lol-match-info match:" match-id)
|
||||
(log/debug "Getting data for LOL match " match-id)
|
||||
(query
|
||||
(create-endpoint server "/lol/match/v5/matches/" match-id)
|
||||
lol-match-parser
|
||||
@@ -290,6 +291,7 @@
|
||||
debug false
|
||||
server "europe"}}]
|
||||
;; (println "get-tft-match-info match:" match-id)
|
||||
(log/debug "Getting data for TFT match " match-id)
|
||||
(query
|
||||
(create-endpoint server "/tft/match/v1/matches/" match-id)
|
||||
tft-match-parser
|
||||
@@ -298,7 +300,7 @@
|
||||
:puuid puuid))
|
||||
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defn get-lol-current-info
|
||||
"Get current LoL match, if any"
|
||||
[puuid & {:keys [api-key debug print-not-active server]
|
||||
@@ -306,16 +308,19 @@
|
||||
debug false
|
||||
print-not-active false
|
||||
server "euw1"}}]
|
||||
(try+
|
||||
(query
|
||||
(create-endpoint server "/lol/spectator/v5/active-games/by-summoner/" puuid)
|
||||
lol-current-parser
|
||||
:api-key api-key
|
||||
:debug debug)
|
||||
(catch [:status 404] _
|
||||
(when print-not-active (println "No active LoL match")))))
|
||||
(log/debug "Getting data for current LOL match")
|
||||
(when (some? api-key)
|
||||
(try+
|
||||
(query
|
||||
(create-endpoint server "/lol/spectator/v5/active-games/by-summoner/" puuid)
|
||||
lol-current-parser
|
||||
:api-key api-key
|
||||
:debug debug)
|
||||
(catch [:status 404] _
|
||||
(when print-not-active (println "No active LoL match"))))))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defn get-tft-current-info
|
||||
"Get current TFT match, if any"
|
||||
[puuid & {:keys [api-key debug print-not-active server]
|
||||
@@ -323,14 +328,16 @@
|
||||
debug false
|
||||
print-not-active false
|
||||
server "euw1"}}]
|
||||
(try+
|
||||
(query
|
||||
(create-endpoint server "/lol/spectator/tft/v5/active-games/by-puuid/" puuid)
|
||||
tft-current-parser
|
||||
:api-key api-key
|
||||
:debug debug)
|
||||
(catch [:status 404] _
|
||||
(when print-not-active (println "No active FTF match")))))
|
||||
(log/debug "Getting data for current TFT match")
|
||||
(when (some? api-key)
|
||||
(try+
|
||||
(query
|
||||
(create-endpoint server "/lol/spectator/tft/v5/active-games/by-puuid/" puuid)
|
||||
tft-current-parser
|
||||
:api-key api-key
|
||||
:debug debug)
|
||||
(catch [:status 404] _
|
||||
(when print-not-active (println "No active tft match"))))))
|
||||
|
||||
|
||||
|
||||
@@ -341,13 +348,13 @@
|
||||
tft-api-key (get-tft-api-key)
|
||||
debug false
|
||||
print-not-active false
|
||||
server "euw1"}}]
|
||||
server "euw1"}}]
|
||||
(or (some? (get-lol-current-info lol-puuid :api-key lol-api-key :server server :debug debug :print-not-active print-not-active))
|
||||
(some? (get-tft-current-info tft-puuid :api-key tft-api-key :server server :debug debug :print-not-active print-not-active))))
|
||||
|
||||
|
||||
(defn get-last-matches
|
||||
"Get info for last LoL or FTF matches"
|
||||
"Get info for last LoL or tft matches"
|
||||
[lol-puuid tft-puuid & {:keys [lol-api-key tft-api-key include-current print-not-active debug server1 server2 count start order-by since until]
|
||||
:or {lol-api-key (get-lol-api-key)
|
||||
tft-api-key (get-tft-api-key)
|
||||
@@ -374,14 +381,14 @@
|
||||
|
||||
|
||||
;;;; Get data bulk
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defn get-with-wait
|
||||
"Calls a getter and waits for response. If rate limit has been exceeded, waits for some
|
||||
seconds and retries."
|
||||
[getter & {:keys [wait-limit-exceeded]
|
||||
:or {wait-limit-exceeded 15}}]
|
||||
(try+
|
||||
(getter)
|
||||
(getter) ;; Executes getter function
|
||||
(catch [:status 429] _
|
||||
(println "Rate limit exceeded, waiting for" wait-limit-exceeded "seconds")
|
||||
(Thread/sleep (* 1000 wait-limit-exceeded))
|
||||
@@ -395,6 +402,7 @@
|
||||
first-batch 0
|
||||
max-batches 10}}]
|
||||
;; (println "Obtaining bulk data - range")
|
||||
(log/debug "Getting data in " max-batches " batches")
|
||||
(doall (flatten (concat
|
||||
(map
|
||||
#(get-with-wait (partial getter %) :wait-limit-exceeded wait-limit-exceeded)
|
||||
@@ -471,18 +479,23 @@
|
||||
|
||||
(defn get-matches-info-batch
|
||||
"Get a lot of LOL and TFT matches info"
|
||||
[lol-puuid tft-puuid & {:keys [lol-api-key tft-api-key wait-limit-exceeded debug server count order-by since until]
|
||||
[lol-puuid tft-puuid & {:keys [lol-api-key tft-api-key wait-limit-exceeded debug server server2 count order-by since until include-current]
|
||||
:or {lol-api-key (get-lol-api-key)
|
||||
tft-api-key (get-tft-api-key)
|
||||
wait-limit-exceeded 15
|
||||
debug false
|
||||
server "europe"
|
||||
server2 "euw1"
|
||||
count 100
|
||||
order-by :start}}]
|
||||
order-by :start
|
||||
include-current true}}]
|
||||
(log/debug "Fetching data in batches")
|
||||
(sort-by order-by
|
||||
(filter some?
|
||||
(concat
|
||||
(get-matches-info-batch-lol (get-matches-batch-lol lol-puuid lol-api-key :wait-limit-exceeded wait-limit-exceeded :debug debug :server server :count count :since since :until until)
|
||||
lol-puuid lol-api-key :debug debug :server server :puuid lol-puuid)
|
||||
(get-matches-info-batch-tft (get-matches-batch-tft tft-puuid tft-api-key :wait-limit-exceeded wait-limit-exceeded :debug debug :server server :count count :since since :until until)
|
||||
tft-puuid tft-api-key :debug debug :server server :puuid lol-puuid)))))
|
||||
tft-puuid tft-api-key :debug debug :server server :puuid lol-puuid)
|
||||
(when include-current [(get-lol-current-info lol-puuid :api-key lol-api-key :server server2 :debug debug)])
|
||||
(when include-current [(get-tft-current-info tft-puuid :api-key tft-api-key :server server2 :debug debug)])))))
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
(ns riot.data
|
||||
(:import [java.time Instant LocalDate ZoneId Duration]
|
||||
[java.time.format DateTimeFormatter])
|
||||
#_{:clj-kondo/ignore [:refer-all]}
|
||||
(:require [clojure.pprint :as pp]
|
||||
[clojure.string :as str]
|
||||
[cheshire.core :refer :all])
|
||||
(:use [riot.core :refer :all])
|
||||
[cheshire.core :refer :all]
|
||||
[riot.core :refer :all]
|
||||
[clj-commons.format.table :refer [print-table] :as table])
|
||||
(:gen-class))
|
||||
|
||||
|
||||
@@ -17,32 +19,37 @@
|
||||
|
||||
;; Parse UNIX time
|
||||
|
||||
(defn unix-to-datetime
|
||||
"Conver from UNIX time in millis, to a ZonedDateTime"
|
||||
(defn unix->ZonedDateTime
|
||||
"Convert from UNIX time in millis, to a ZonedDateTime"
|
||||
[millis]
|
||||
(when (some? millis)
|
||||
(.atZone (Instant/ofEpochMilli millis) (ZoneId/systemDefault))))
|
||||
|
||||
(defn unix->LocalDate
|
||||
"Convert from UNIX time in millis to LocalDate"
|
||||
[millis]
|
||||
(when (some? millis)
|
||||
(.toLocalDate (unix->ZonedDateTime millis))))
|
||||
|
||||
(defn localdate-to-date
|
||||
(defn LocalDate->Date
|
||||
"Converts a java.time.LocalDate to an ancient java.util.Date"
|
||||
[localdate]
|
||||
(when (some? localdate)
|
||||
(java.util.Date/from (.toInstant (.atZone (.atStartOfDay localdate) (ZoneId/systemDefault))))))
|
||||
|
||||
(defn parse-localdate
|
||||
(defn str->LocalDate
|
||||
"Parse a yyyy-MM-dd date to java.time.LocalDate"
|
||||
[s]
|
||||
(java.time.LocalDate/parse s (DateTimeFormatter/ofPattern DEFAULT_DATE_FORMAT)))
|
||||
|
||||
(defn parse-date-str
|
||||
(defn str->Date
|
||||
[s]
|
||||
(localdate-to-date (parse-localdate s)))
|
||||
(LocalDate->Date (str->LocalDate s)))
|
||||
|
||||
|
||||
(defn parse-date-epoch
|
||||
(defn unix->Date
|
||||
[millis]
|
||||
(localdate-to-date (unix-to-datetime millis)))
|
||||
(LocalDate->Date (unix->ZonedDateTime millis)))
|
||||
|
||||
|
||||
(defn format-datetime
|
||||
@@ -59,18 +66,21 @@
|
||||
([millis] (format-datetime-millis millis DEFAULT_DATE_TIME_FORMAT))
|
||||
([millis pattern]
|
||||
(when (and (some? millis) (some? pattern))
|
||||
(format-datetime (unix-to-datetime millis) pattern))))
|
||||
(format-datetime (unix->ZonedDateTime millis) pattern))))
|
||||
|
||||
|
||||
|
||||
;; Parse durations
|
||||
(defn format-duration-seconds
|
||||
"Converts a duration in seconds to a string with hours and seconds"
|
||||
"Converts a duration in seconds to a string with hours, minutes and seconds"
|
||||
[seconds]
|
||||
(when (some? seconds)
|
||||
(if (< seconds 3600)
|
||||
(format "%02d:%02d" (quot seconds 60) (rem seconds 60))
|
||||
(format "%02d:%02d:%02d" (quot seconds 3600) (- (quot seconds 60) 60) (rem seconds 60)))))
|
||||
(format "%02d:%02d:%02d"
|
||||
(quot seconds 3600)
|
||||
(- (quot seconds 60) (* 60 (quot seconds 3600)))
|
||||
(rem seconds 60)))))
|
||||
|
||||
|
||||
|
||||
@@ -101,9 +111,6 @@
|
||||
(->> matches
|
||||
(map #(update % :start (fn [x] (format-datetime-millis x datetime-format))))
|
||||
(map #(update % :end (fn [x] (format-datetime-millis x datetime-format))))))
|
||||
(comment
|
||||
(with-parsed-dates riot.test-examples/matches-example))
|
||||
|
||||
|
||||
|
||||
(defn with-parsed-durations
|
||||
@@ -112,7 +119,6 @@
|
||||
(map #(update % :duration format-duration-seconds) matches))
|
||||
|
||||
|
||||
|
||||
(defn with-parsed-dates-durations
|
||||
"Takes the original list of matches and parses dates and durations"
|
||||
[matches & {:keys [datetime-format]
|
||||
@@ -139,21 +145,21 @@
|
||||
|
||||
(generate-string matches {:pretty pretty}))
|
||||
|
||||
(comment
|
||||
(println (as-json (with-parsed-dates-durations riot.test-examples/matches-example
|
||||
:pretty false))))
|
||||
|
||||
|
||||
|
||||
(defn as-ascii-table
|
||||
"Export as ascii table"
|
||||
[matches]
|
||||
(pp/print-table matches))
|
||||
|
||||
(comment
|
||||
(as-ascii-table riot.test-examples/matches-example)
|
||||
(as-ascii-table (with-parsed-dates-durations riot.test-examples/matches-example)))
|
||||
|
||||
(defn as-pretty-table
|
||||
"Export as an ANSI coloured text table"
|
||||
[matches]
|
||||
(if (and (some? matches) (< 0 (count matches)))
|
||||
(let [columns {:columns (-> (first matches) keys vec)
|
||||
:style table/default-style}]
|
||||
(print-table columns matches))
|
||||
(println "No data available")))
|
||||
|
||||
|
||||
(defn as-csv
|
||||
@@ -166,8 +172,79 @@
|
||||
(map #(clojure.string/join separator %)
|
||||
(map vals matches))))))
|
||||
|
||||
|
||||
(defn calculate-num-days-played
|
||||
[matches]
|
||||
(count (partition-by #(unix->LocalDate (:start %)) matches))
|
||||
)
|
||||
|
||||
(defn calculate-days-played
|
||||
[matches]
|
||||
(map #(.toString %) (sort (keys (group-by #(unix->LocalDate (:start %)) matches)))))
|
||||
|
||||
(defn calculate-total-seconds-played
|
||||
[matches]
|
||||
(reduce + (filter some? (map :duration matches))))
|
||||
|
||||
(defn calculate-seconds-played-per-day
|
||||
[matches]
|
||||
)
|
||||
|
||||
(defn calculate-statistics
|
||||
"Calculate several statistics about the matches"
|
||||
[matches]
|
||||
(when (and (some? matches) (seq matches))
|
||||
(let [matches-lol (filter #(= (:game-type %) "lol") matches)
|
||||
matches-tft (filter #(= (:game-type %) "tft") matches)
|
||||
total (count matches)
|
||||
total-lol (count matches-lol)
|
||||
total-tft (count matches-tft)
|
||||
win (count (filter :winner matches))
|
||||
win-lol (count (filter :winner matches-lol))
|
||||
win-tft (count (filter :winner matches-tft))
|
||||
loss (- total win)
|
||||
loss-lol (- total-lol win-lol)
|
||||
loss-tft (- total-tft win-tft)
|
||||
win-percent (if (zero? total) 0 (* (/ win total) 100.0))
|
||||
win-percent-lol (if (zero? total-lol) 0 (* (/ win-lol total-lol) 100.0))
|
||||
win-percent-tft (if (zero? total-tft) 0 (* (/ win-tft total-tft) 100.0))
|
||||
num-days-played (calculate-num-days-played matches)
|
||||
num-days-played-lol (calculate-num-days-played matches-lol)
|
||||
num-days-played-tft (calculate-num-days-played matches-tft)
|
||||
days-played (calculate-days-played matches)
|
||||
days-played-lol (calculate-days-played matches-lol)
|
||||
days-played-tft (calculate-days-played matches-tft)
|
||||
seconds-played (calculate-total-seconds-played matches)
|
||||
seconds-played-lol (calculate-total-seconds-played matches-lol)
|
||||
seconds-played-tft (calculate-total-seconds-played matches-tft)
|
||||
seconds-per-day (if (zero? num-days-played) 0 (/ seconds-played num-days-played))
|
||||
seconds-per-day-lol (if (zero? num-days-played-lol) 0 (/ seconds-played-lol num-days-played-lol))
|
||||
seconds-per-day-tft (if (zero? num-days-played-tft) 0 (/ seconds-played-tft num-days-played-tft))]
|
||||
{:total {:all total
|
||||
:lol total-lol
|
||||
:tft total-tft}
|
||||
:win {:all win
|
||||
:lol win-lol
|
||||
:tft win-tft}
|
||||
:loss {:all loss
|
||||
:lol loss-lol
|
||||
:tft loss-tft}
|
||||
:win-percent {:all win-percent
|
||||
:lol win-percent-lol
|
||||
:tft win-percent-tft}
|
||||
:num-days-played {:all num-days-played
|
||||
:lol num-days-played-lol
|
||||
:tft num-days-played-tft}
|
||||
:days-played {:all days-played
|
||||
:lol days-played-lol
|
||||
:tft days-played-tft}
|
||||
:time-played {:all (format-duration-seconds seconds-played)
|
||||
:lol (format-duration-seconds seconds-played-lol)
|
||||
:tft (format-duration-seconds seconds-played-tft)}
|
||||
:played-per-day {:all (format-duration-seconds (int seconds-per-day))
|
||||
:lol (format-duration-seconds (int seconds-per-day-lol))
|
||||
:tft (format-duration-seconds (int seconds-per-day-tft))}})))
|
||||
|
||||
(comment
|
||||
(as-csv riot.test-examples/matches-example)
|
||||
(as-csv riot.test-examples/matches-example2)
|
||||
(as-csv (with-parsed-dates-durations riot.test-examples/matches-example))
|
||||
(as-csv (with-parsed-dates-durations riot.test-examples/matches-example2)))
|
||||
(calculate-statistics '({:winner true} {:winner false} {:winner true}))
|
||||
)
|
||||
@@ -1,10 +1,12 @@
|
||||
(ns riot.core-test
|
||||
{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
#_{:clj-kondo/ignore [:refer-all]}
|
||||
(:require [clojure.test :refer :all]
|
||||
[riot.test-examples :refer :all]
|
||||
[riot.core :refer :all]
|
||||
[timewords.core :refer [parse]]
|
||||
[clojure.pprint :as pp])
|
||||
(:use [slingshot.slingshot :only [try+]]))
|
||||
[clojure.pprint :as pp]
|
||||
[slingshot.slingshot :refer [try+]]))
|
||||
|
||||
|
||||
|
||||
@@ -30,8 +32,8 @@
|
||||
(is (= (decrypt-data LOL_KEY) (get-lol-api-key nil)))
|
||||
(is (= "abc" (get-lol-api-key "abc"))))
|
||||
(testing "Get TFT API key"
|
||||
(is (= (decrypt-data TFT_KEY) (get-tft-api-key)))
|
||||
(is (= (decrypt-data TFT_KEY) (get-tft-api-key nil)))
|
||||
;; (is (= (decrypt-data TFT_KEY) (get-tft-api-key)))
|
||||
;; (is (= (decrypt-data TFT_KEY) (get-tft-api-key nil)))
|
||||
(is (= "abc" (get-tft-api-key "abc")))))
|
||||
|
||||
|
||||
@@ -121,7 +123,7 @@
|
||||
(comment (get-puuid-from-name example-name example-tag :api-key (get-tft-api-key)))
|
||||
|
||||
;; From java.util.Date to seconds since 1970
|
||||
(deftest test-date-to-seconds
|
||||
(deftest ^:timezone test-date-to-seconds
|
||||
(println "* Date to epoch seconds *")
|
||||
(testing "Some dates"
|
||||
(is (nil? (date-to-seconds nil)))
|
||||
@@ -130,7 +132,7 @@
|
||||
|
||||
|
||||
;; Get parameter for querying data
|
||||
(deftest test-query-params
|
||||
(deftest ^:timezone test-query-params
|
||||
(println "* Generate query params *")
|
||||
(testing "None params"
|
||||
(is (= {"count" 100} (query-params))))
|
||||
@@ -241,7 +243,7 @@
|
||||
(testing "Get LOL matches"
|
||||
(is (= 10 (count (get-matches-batch-lol example-lol-puuid (get-lol-api-key) :count 5 :max-batches 2))))))
|
||||
|
||||
(deftest test-get-matches-batch-tft
|
||||
(deftest ^:tft test-get-matches-batch-tft
|
||||
(println "* Get tft matches batch *")
|
||||
(testing "Invalid data"
|
||||
(is (= nil (get-matches-batch-tft nil nil)))
|
||||
@@ -264,7 +266,7 @@
|
||||
(get-lol-api-key)))))))
|
||||
|
||||
|
||||
(deftest test-get-matches-info-batch-tft
|
||||
(deftest ^:tft test-get-matches-info-batch-tft
|
||||
(println "* Get tft matches info batch *")
|
||||
(testing "Invalid data"
|
||||
(is (= nil (get-matches-info-batch-tft nil nil nil)))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns riot.data-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[clojure.pprint :as pp]
|
||||
[riot.test-examples :refer :all]
|
||||
[riot.data :refer :all]
|
||||
[riot.core :refer :all])
|
||||
@@ -9,30 +10,38 @@
|
||||
|
||||
|
||||
;; Convert from epoch in millis (1970-01-01 00:00:00) to ZonedDateTime
|
||||
(deftest test-unix-to-datetime
|
||||
(deftest ^:timezone test-unix->ZonedDateTime
|
||||
(println "* Epoch millis to java.util.Datetime *")
|
||||
(testing "Get LOL API key"
|
||||
(testing "Synthetic data"
|
||||
(is (= (ZonedDateTime/of 2025 4 3 0 0 0 0 (ZoneId/systemDefault))
|
||||
(unix-to-datetime 1743631200000)))))
|
||||
(unix->ZonedDateTime 1743631200000)))))
|
||||
|
||||
|
||||
(deftest ^:timezone test-unix->LocalDate
|
||||
(println "* Epoch millis to java.time.LocalDate *")
|
||||
(testing "Synthetic data"
|
||||
(is (= (LocalDate/of 2025 4 3) (unix->LocalDate 1743631200000)))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; Format a LocalDateTime o ZonedDateTime as a String
|
||||
(deftest test-format-datetime
|
||||
(deftest ^:timezone test-unix->ZonedDateTime
|
||||
(println "* DateTime to String *")
|
||||
(testing "Null"
|
||||
(is (nil? (format-datetime-millis nil))))
|
||||
(testing "Default format"
|
||||
(is (= "2025-04-03 00:00:00"
|
||||
(format-datetime (unix-to-datetime 1743631200000)))))
|
||||
(format-datetime (unix->ZonedDateTime 1743631200000)))))
|
||||
(testing "Personalized time formats"
|
||||
(is (= "2025-04-03"
|
||||
(format-datetime (unix-to-datetime 1743631200000) "yyyy-MM-dd")))
|
||||
(format-datetime (unix->ZonedDateTime 1743631200000) "yyyy-MM-dd")))
|
||||
(is (= "00:00"
|
||||
(format-datetime (unix-to-datetime 1743631200000) "HH:00")))))
|
||||
(format-datetime (unix->ZonedDateTime 1743631200000) "HH:00")))))
|
||||
|
||||
|
||||
;; Format a epoch in millis as a String
|
||||
(deftest test-datetime-millis
|
||||
(deftest ^:timezone test-datetime-millis
|
||||
(println "* DateTime to String *")
|
||||
(testing "Null"
|
||||
(is (nil? (format-datetime-millis nil))))
|
||||
@@ -58,11 +67,14 @@
|
||||
(testing "Long durations"
|
||||
(is (= "01:00:00" (format-duration-seconds 3600)))
|
||||
(is (= "01:00:01" (format-duration-seconds 3601)))
|
||||
(is (= "01:01:01" (format-duration-seconds 3661)))))
|
||||
(is (= "01:01:01" (format-duration-seconds 3661)))
|
||||
(is (= "05:53:09" (format-duration-seconds 21189)))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; Epoch in millis for today at 00:00:00 hours. Difficult to test
|
||||
(deftest test-today-millis
|
||||
(deftest ^:timezone test-today-millis
|
||||
(println "* Epoch for today *")
|
||||
(testing "Epoch for today"
|
||||
(is (some? (today-millis)))))
|
||||
@@ -80,7 +92,7 @@
|
||||
|
||||
|
||||
;; Parse dates inside a lis of matches
|
||||
(deftest test-with-parsed-dates
|
||||
(deftest ^:timezone test-with-parsed-dates
|
||||
(println "* Parse dates in a match list*")
|
||||
(testing "Empty or invalid lists"
|
||||
(is (= '() (with-parsed-dates '())))
|
||||
@@ -92,7 +104,7 @@
|
||||
|
||||
|
||||
;; Parse durations
|
||||
(deftest test-with-parsed-dates
|
||||
(deftest ^:timezone test-with-parsed-durations
|
||||
(println "* Parse durations in a match list *")
|
||||
(testing "Empty or invalid lists"
|
||||
(is (= '() (with-parsed-durations '())))
|
||||
@@ -104,7 +116,7 @@
|
||||
|
||||
|
||||
;; Parse dates and durations
|
||||
(deftest test-with-parsed-dates
|
||||
(deftest ^:timezone test-with-parsed-dates-durations
|
||||
(println "* Parse dates and durations in a match list *")
|
||||
(testing "Empty or invalid lists"
|
||||
(is (= '() (with-parsed-dates-durations '())))
|
||||
@@ -113,7 +125,55 @@
|
||||
(is (= '({:a "AAA" :b 2 :duration nil, :start nil, :end nil})
|
||||
(with-parsed-dates-durations '({:a "AAA" :b 2})))))
|
||||
(testing "Valid list"
|
||||
(is (= '({:start "2025-06-11 19:00:54", :end nil, :duration "21:28", :active true, :game-type "lol", :id "EUW1_111111"})
|
||||
(is (= '({:start "2025-06-11 19:00:54", :end nil, :duration "21:28", :winner true, :game-type "lol", :id "EUW1_111111", :result "GameComplete"})
|
||||
(with-parsed-dates-durations (take 1 matches-example))))))
|
||||
|
||||
|
||||
;; Pretty table
|
||||
(deftest test-as-pretty-table
|
||||
(println "* Generates a pretty table *")
|
||||
(testing "Some examples, printed to System.out"
|
||||
(is (= nil (as-pretty-table [])))
|
||||
(is (= nil (as-pretty-table nil)))
|
||||
(is (= nil (as-pretty-table matches-example)))
|
||||
(is (= nil (as-pretty-table matches-example2)))))
|
||||
|
||||
|
||||
(deftest test-calculate-statistics
|
||||
(println "* Calculate statistics *")
|
||||
(testing "Simple check"
|
||||
(is (nil? (calculate-statistics nil)))
|
||||
(is (nil? (calculate-statistics '())))
|
||||
(is (map? (calculate-statistics matches-example)))
|
||||
(is (map? (calculate-statistics matches-example2))))
|
||||
(let [data (calculate-statistics matches-example)]
|
||||
(testing "With manual generated data"
|
||||
(is (= 5 (get-in data [:total :all])))
|
||||
(is (= 3 (get-in data [:total :lol])))
|
||||
(is (= 2 (get-in data [:total :tft])))
|
||||
(is (= 3 (get-in data [:win :all])))
|
||||
(is (= 2 (get-in data [:loss :all])))
|
||||
(is (= 60.0 (get-in data [:win-percent :all])))
|
||||
(is (= 3 (get-in data [:num-days-played :all]))))))
|
||||
|
||||
|
||||
(comment
|
||||
matches-example
|
||||
|
||||
(.toLocalDate (unix->ZonedDateTime 1749661254854))
|
||||
|
||||
(partition-by #(unix->LocalDate (:start %)) matches-example)
|
||||
(group-by #(unix->LocalDate (:start %)) matches-example)
|
||||
|
||||
(keys (group-by #(unix->LocalDate (:start %)) matches-example2))
|
||||
|
||||
(calculate-days-played matches-example2)
|
||||
|
||||
(reduce + (filter some? (map :duration matches-example)))
|
||||
|
||||
(calculate-statistics matches-example)
|
||||
|
||||
(calculate-statistics matches-example2)
|
||||
|
||||
(pp/pprint (calculate-statistics matches-example2))
|
||||
)
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
(def example-tft-puuid "yJawqnx9nzvj9ZJdW_7ok0wmB2WqSqafKy1sDrA48Zefbx0Iuco9jfGyVdUhldjDt8IxLvMcs4r8MA")
|
||||
|
||||
(def example-lol-match {:start 1750106168280, :end 1750108132077, :duration 1882, :active false, :game-type "lol", :id "EUW1_7434497430" :winner false})
|
||||
(def example-lol-match {:start 1750106168280, :end 1750108132077, :duration 1882, :game-type "lol", :id "EUW1_7434497430" :winner false :result "GameComplete"})
|
||||
|
||||
(def example-lol-never-played "annlAfxhJnTwlwRgQZYbGeQpD3jWb-ju7vVKEW_g-EIJf6xQT0eeb-0obARVekrksf8n9XCjcxyHHQ")
|
||||
|
||||
@@ -17,83 +17,120 @@
|
||||
'({:start 1749661254854,
|
||||
:end nil,
|
||||
:duration 1288,
|
||||
:active true,
|
||||
:game-type "lol"
|
||||
:id "EUW1_111111"}
|
||||
:id "EUW1_111111"
|
||||
:winner true,
|
||||
:result "GameComplete"}
|
||||
{:start 1749659482211,
|
||||
:end 1749661133145,
|
||||
:duration 1622,
|
||||
:active false,
|
||||
:game-type "lol"
|
||||
:id "EUW1_222222"}
|
||||
:id "EUW1_222222"
|
||||
:winner false,
|
||||
:result "GameComplete"}
|
||||
{:start 1749655924238,
|
||||
:end 1749658146178,
|
||||
:duration nil,
|
||||
:active false,
|
||||
:game-type "lol"
|
||||
:id "EUW1_3333333"}))
|
||||
:id "EUW1_3333333"
|
||||
:winner true,
|
||||
:result "GameComplete"}
|
||||
{:start 1749755924238,
|
||||
:end 1749758146178,
|
||||
:duration nil,
|
||||
:game-type "tft"
|
||||
:id "EUW1_4444444"
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1749855924238,
|
||||
:end 1749858146178,
|
||||
:duration nil,
|
||||
:game-type "tft"
|
||||
:id "EUW1_5555555"
|
||||
:winner false
|
||||
:result "GameComplete"}))
|
||||
|
||||
;; Real example from:
|
||||
;; riot-clojur t "Walid Georgey" euw --tft -o edn -s "24 may" -u "30 may" --no-format-dates --no-format-durations
|
||||
|
||||
(def matches-example2
|
||||
'({:start 1749893615000,
|
||||
:end 1749896091000,
|
||||
:duration 2476,
|
||||
:active false,
|
||||
:game-type "ftf",
|
||||
:id "EUW1_7431741880"}
|
||||
{:start 1749886475803,
|
||||
:end 1749887505751,
|
||||
:duration 999,
|
||||
:active false,
|
||||
'({:start 1748470702886,
|
||||
:end 1748472666457,
|
||||
:duration 1947,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7431686898"}
|
||||
{:start 1749830764708,
|
||||
:end 1749831241808,
|
||||
:duration 232,
|
||||
:active false,
|
||||
:id "EUW1_7415410708",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748466492126,
|
||||
:end 1748468105264,
|
||||
:duration 1585,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7431025296"}
|
||||
{:start 1749740947765,
|
||||
:end 1749743270521,
|
||||
:duration 2214,
|
||||
:active false,
|
||||
:id "EUW1_7415317950",
|
||||
:winner false
|
||||
:result "GameComplete"}
|
||||
{:start 1748180985396,
|
||||
:end 1748182761075,
|
||||
:duration 1754,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429951366"}
|
||||
{:start 1749739258737,
|
||||
:end 1749740607134,
|
||||
:duration 1311,
|
||||
:active false,
|
||||
:id "EUW1_7411793848",
|
||||
:winner true}
|
||||
:result "GameComplete"
|
||||
{:start 1748178332000,
|
||||
:end 1748180377000,
|
||||
:duration 2045,
|
||||
:game-type "tft",
|
||||
:id "EUW1_7411747119",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748119701678,
|
||||
:end 1748121421926,
|
||||
:duration 1578,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429932844"}
|
||||
{:start 1749737844367,
|
||||
:end 1749739185680,
|
||||
:duration 1144,
|
||||
:active false,
|
||||
:id "EUW1_7411183430",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748117211000,
|
||||
:end 1748119477000,
|
||||
:duration 2266,
|
||||
:game-type "tft",
|
||||
:id "EUW1_7411136542",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748110984521,
|
||||
:end 1748112698320,
|
||||
:duration 1694,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429905240"}
|
||||
{:start 1749736215680,
|
||||
:end 1749737698464,
|
||||
:duration 1425,
|
||||
:active false,
|
||||
:id "EUW1_7410995061",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748103234000,
|
||||
:end 1748105725000,
|
||||
:duration 2491,
|
||||
:game-type "tft",
|
||||
:id "EUW1_7410863506",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748097496984,
|
||||
:end 1748099470873,
|
||||
:duration 1933,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429884230"}
|
||||
{:start 1749733140474,
|
||||
:end 1749734774103,
|
||||
:duration 1535,
|
||||
:active false,
|
||||
:id "EUW1_7410750999",
|
||||
:winner false
|
||||
:result "GameComplete"}
|
||||
{:start 1748094441000,
|
||||
:end 1748096636000,
|
||||
:duration 2195,
|
||||
:game-type "tft",
|
||||
:id "EUW1_7410701935",
|
||||
:winner true
|
||||
:result "GameComplete"}
|
||||
{:start 1748088640068,
|
||||
:end 1748090386650,
|
||||
:duration 1701,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429836245"}
|
||||
{:start 1749727474332,
|
||||
:end 1749729494360,
|
||||
:duration 1899,
|
||||
:active false,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429774673"}
|
||||
{:start 1749717297978,
|
||||
:end 1749719815603,
|
||||
:duration 2281,
|
||||
:active false,
|
||||
:game-type "lol",
|
||||
:id "EUW1_7429678096"}))
|
||||
:id "EUW1_7410601082",
|
||||
:winner true
|
||||
:result "GameComplete"}))
|
||||
|
||||
|
||||
(def response-player-info {:puuid "annlAfxhJnTwlwRgQZYbGeQpD3jWb-ju7vVKEW_g-EIJf6xQT0eeb-0obARVekrksf8n9XCjcxyHHQ",
|
||||
|
||||
Reference in New Issue
Block a user