Compare commits

51 Commits

Author SHA1 Message Date
33d69c2ed3 Ignore binary files
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-02-01 01:26:02 +01:00
a808515c1d native compilation
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-02-01 01:24:30 +01:00
b1c87b1adf Code cleaning
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-02-01 00:14:56 +01:00
af895a5173 Refactoring to use my clojure-utils
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-31 23:27:42 +01:00
7d71c48536 Reestructuring code
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-30 00:47:27 +01:00
deb8746347 Simplify structure
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
simplify structure and use external library
2026-01-30 00:38:11 +01:00
c449beac80 Fix time zone
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-24 11:32:35 +01:00
f3166ceaa5 separate command for stats
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-22 00:35:04 +01:00
22cb492bbc Refactor and K/D statistics
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-21 00:32:46 +01:00
79e4b5b99b Better format for pretty table
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-20 11:10:11 +01:00
e3742971dc Basic statistics
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-20 00:29:10 +01:00
ce48d37d86 Add winner data
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-19 22:58:57 +01:00
7f0eadff2f Renamed function and check winner
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-18 21:40:46 +01:00
88d6405fb6 No participants in csv format
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-17 02:51:01 +01:00
dee8935f9c Export to table, pretty table, csv and edn
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-17 02:48:33 +01:00
284b9342ce Better param processing
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-17 02:16:39 +01:00
a09c0f86d5 Search matches by date 2026-01-17 02:01:17 +01:00
ee6245d77a Uncommited changes
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-17 01:54:59 +01:00
bd1dc3190c Join data and riot-config. Since date. 2026-01-17 01:54:19 +01:00
a1fe5ff70c param "since" for future use
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-16 00:54:20 +01:00
d1e036bdcf Is playing + matches basic
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-16 00:46:51 +01:00
5455d1e2f1 check if the player is playing
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-15 00:28:56 +01:00
055d72129a run tasks and check if playing
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-14 23:26:17 +01:00
1ec2749319 Split cli app in two modules
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-14 18:42:38 +01:00
70190d3cc9 Split cli app in two modules 2026-01-14 18:42:18 +01:00
1fbd7258fe Start CLI development
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-14 00:38:07 +01:00
5fe64ac55c Date and duration formatting 2026-01-14 00:37:46 +01:00
c1d58c838d Update README.md
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-13 14:06:18 +01:00
f13e5ba712 Check if it's playing now
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-08 00:47:26 +01:00
b311b47c4e Get basic data from matches
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-08 00:39:51 +01:00
938b618532 Rename riot-data to riot-config
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 23:19:56 +01:00
8772d28db3 TFT endpoints
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 23:17:56 +01:00
deb75e6946 Improve error handling using monads
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 21:24:02 +01:00
e0510db928 More doc fixes
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 16:51:44 +01:00
55b855f99f Improved code documentation
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 15:38:30 +01:00
f02629bb45 Improve exception handling
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 13:51:58 +01:00
34b701e1d2 Fixed calling the API
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 11:12:56 +01:00
49891244a9 Broken
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 01:45:49 +01:00
5c671f841e Code reestructuration
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 00:51:43 +01:00
1bcd75218e More endpoints for LOL
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-07 00:25:36 +01:00
420a92635a More endpoints
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-06 12:48:51 +01:00
2f33c139df More metadata and documentation
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-06 11:57:31 +01:00
22be4ad137 Endpoints has metadata
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-06 11:48:29 +01:00
fd947b87e0 Comments
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-04 01:54:46 +01:00
5162f76ffc Convert parameters to string
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-04 01:52:11 +01:00
8bc4b993a8 escape space character in URL
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-04 01:47:09 +01:00
18cea38c16 call the api with several endpoints
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-04 01:19:47 +01:00
9182e50179 Get PUUID for player
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2026-01-04 00:31:53 +01:00
fb1d9a7312 begin the core
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2025-12-31 17:32:58 +01:00
1109c5a965 More files to the base
Some checks failed
Compile and test using leiningen / Run tests (push) Has been cancelled
2025-12-31 16:42:33 +01:00
e7e270d1ec Empty base multiproject configuration 2025-12-31 16:40:33 +01:00
12 changed files with 1472 additions and 48 deletions

View File

@@ -19,17 +19,6 @@ jobs:
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
@@ -40,20 +29,20 @@ jobs:
${{ runner.os }}-m2-
# Get leiningen's version
- name: Get leiningen version
run: lein -v
- name: Get clojure version
run: clojure --version
# Test the code
- name: Run tests
env:
TFT_API: ${{ secrets.DEV_API }}
run: lein test
#- 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
#- 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

9
.gitignore vendored
View File

@@ -7,12 +7,15 @@ pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
/**/.nrepl-port
/.prepl-port
.hgignore
.hg/
.cpcache
.clj-kondo
.lsp
.calva
*.svg
/logs
# Native executable
riot
# A weid link
/bin/*

View File

@@ -6,9 +6,6 @@ application to quick get some relevant data.
The library and the CLI application are separated, you can use the library independently.
CLI application should be compatible with [babashka](https://babashka.org/), so
will use external libraries compatible with it.
# The API and the tokens
Accessing the API requires one or more API tokens. Each videogame has their own
@@ -77,35 +74,36 @@ all functionality provided by v1.1.
### General
* [ ] Reestructure all project to use `deps.edn`, `tools.clj` and subprojects
* [ ] Use babashka compatible libraries and `bb.edn`
* [ ] Gitea actions for automatic test executing
* [ ] Distribute CLI as self executable (bb, jlink or graal)
* [x] Reestructure all project to use `deps.edn`, `tools.clj` and subprojects
* [ ] Distribute CLI as self executable (jlink or graal)
### Library
* [ ] Read tokens from environment and a function to override them manually
* [ ] Fuctions for calling Riot API and read result as clojure data structures (EDN format)
* [ ] Get [PUUID](https://developer.riotgames.com/docs/lol#summoner-names-to-riot-ids_obtaining-puuid-and-summonerid-from-riotid) for a player
* [ ] Get basic player info
* [ ] Format raw data
* [x] Read tokens from environment and a function to override them manually
* [x] Fuctions for calling Riot API and read result as clojure data structures (EDN format)
* [x] Get [PUUID](https://developer.riotgames.com/docs/lol#summoner-names-to-riot-ids_obtaining-puuid-and-summonerid-from-riotid) for a player
* [x] Get basic player info
* [x] Format raw data
### CLI
* [ ] Babashka compatible CLI
* [ ] Basic parameters for log level and override API tokens
* [ ] Obtain basic player info
* [ ] Last games for player (LOL, TFT and others)
* [ ] Is the player playing just now?
* [ ] Format output as fancy table
* [ ] Format output as CSV
* [ ] Calculte statistics
* [x] Rich and easy to use CLI
* [x] Basic parameters for log level and override API tokens
* [x] Obtain basic player info
* [x] Last games for player (LOL, TFT and others)
* [x] Is the player playing just now?
* [x] Contiuosly check if the player es playing
* [x] Format output as fancy table
* [x] Format output as CSV
* [x] Format output as JSON
* [x] Calculte basic statistics
* [ ] Calculate advanced statistics: win strike, adversaries, etc.
# License
MIT License
Copyright (c) 2025 ruben
Copyright (c) 2026 ruben
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

44
build.clj Normal file
View File

@@ -0,0 +1,44 @@
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'es.rcorral/riot-clojure)
(def version (format "2.0.%s" (b/git-count-revs nil))) ;; or read from file, etc
(def class-dir "target/classes")
(def src-dirs ["src"])
(def jar-file (format "target/%s-%s.jar" (name lib) version))
(def uber-file (format "target/%s-%s-standalone.jar" (name lib) version))
(def main-ns "riot.app")
;; delay to defer side effects (artifact downloads)
(def basis (delay (b/create-basis {:project "deps.edn"})))
(defn clean [_]
(b/delete {:path "target"}))
(defn jar [_]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis @basis
:src-dirs ["src"]})
(b/copy-dir {:src-dirs ["src" "resources"]
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file}))
(defn uber
"Build a uberjar with all dependencies included"
[_]
(b/delete {:path class-dir})
(b/copy-dir {:src-dirs src-dirs
:target-dir class-dir})
(b/copy-dir {:src-dirs src-dirs
:target-dir class-dir})
(b/compile-clj {:basis @basis
:src-dirs src-dirs
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file uber-file
:basis @basis
:main main-ns})
(println "Generated uberjar executable:" uber-file))

60
deps.edn Executable file
View File

@@ -0,0 +1,60 @@
{:paths ["src" "test" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
;; https://juxt.github.io/tick/
tick/tick {:mvn/version "1.0"}
;; https://github.com/lambdaisland/cli
com.lambdaisland/cli {:mvn/version "1.27.121"}
;; https://github.com/tokenmill/timewords
lt.tokenmill/timewords {:mvn/version "0.5.0"}
;; https://github.com/clj-commons/pretty
org.clj-commons/pretty {:mvn/version "3.6.8"}
;; https://github.com/babashka/http-client
org.babashka/http-client {:mvn/version "0.4.22"}
;; https://github.com/dakrone/cheshire?tab=readme-ov-file
cheshire/cheshire {:mvn/version "6.1.0"}
;; https://git.rcorral.es/ruben/clojure-utils
rcorral/clojure-utils {:git/url "https://git.rcorral.es/ruben/clojure-utils"
:git/tag "v0.2.0"
:sha "4b1a7b1bc28b88ada871262806b8c4f3c000426f"}
;; https://cljdoc.org/d/com.github.clj-easy/graal-build-time/1.0.5/doc/readme
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}}
:aliases {;; Execute the app.
:run {:main-opts ["-m" "riot.app"]}
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
;; Check test.edn for kaocha runner's config
:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}
lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"}}
:main-opts ["-m" "kaocha.runner"]}
;; Run with clj -T:build function-in-build
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build
:exec-fn jar}
;; Build uber jar for CLI app
:uber {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build
:exec-fn uber}
;; Conjure / cider connector
:repl/conjure {:extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}
cider/cider-nrepl {:mvn/version "0.42.1"}}
:main-opts ["--main" "nrepl.cmdline"
"--middleware" "[cider.nrepl/cider-middleware]"
"--interactive"]}
;; Native image. You must link your native-image executable to a relative path: bin/native-executable
:native-image {:main-opts ["--main" "clj.native-image" "riot.app"
"--enable-url-protocols=http,https"
"--report-unsupported-elements-at-runtime"
"--initialize-at-build-time"
"-H:Name=riot"]
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]
:extra-deps {taylorwood/clj.native-image
{:git/url "https://github.com/taylorwood/clj.native-image.git"
:sha "5227df16ead1fef7cddd94e6853d810e7d08579b"}}}
}}

View File

@@ -0,0 +1,20 @@
@startuml Load key
!pragma useVerticalIf on
start
if (key in map?) then (yes)
:return key;
elseif (dev key in map?) then (yes)
:return dev key;
elseif (key in env?) then (yes)
:store key in map;
:return key;
elseif (dev key in env?) then (yes)
:store dev key in map;
:return dev key;
else (nothing)
:nil key;
endif
stop
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

114
src/riot/app.clj Normal file
View File

@@ -0,0 +1,114 @@
(ns riot.app
#_{:clj-kondo/ignore [:refer-all]}
(:require [rcorral.api.core :refer :all]
[riot.commands :refer :all]
[riot.riot-api :refer :all]
[lambdaisland.cli :as cli]
[clojure.pprint :as pp])
(:gen-class))
(def VERSION "2.0")
(defn set-keys
[params]
(when (:dev-key params) (set-riot-api-key :RIOT_DEV_KEY (:dev-key params)))
(when (:lol-key params) (set-riot-api-key :RIOT_LOL_KEY (:lol-key params)))
(when (:tft-key params) (set-riot-api-key :RIOT_TFT_KEY (:tft-key params))))
(defn check-debug
[params]
(reset! debug (:verbose params))
(reset! debug-http (:debug-http params))
(when @debug (pp/pprint params)))
(defn cmd-check-playing-adapter
[params]
(check-debug params)
(set-keys params)
(cmd-check-playing (:username params) (:usertag params)
:lol (:lol params)
:tft (:tft params)
:template (:template params)
:continuous (:continuous params)
:every-seconds (:period params)
:max (:max params)
:header (:header params)))
(defn cmd-show-matches-adapter
[params]
(check-debug params)
(set-keys params)
(cmd-show-matches (:username params) (:usertag params)
:lol (:lol params)
:tft (:tft params)
:format (:format params)
:since (:since params)
:until (:until params)
:max (:max params)))
(defn cmd-show-stats-adapter
[params]
(check-debug params)
(set-keys params)
(cmd-show-stats (:username params) (:usertag params)
:lol (:lol params)
:tft (:tft params)
:format (:format params)
:since (:since params)
:until (:until params)
:max (:max params)))
;;[player-name player-tag & {:keys [lol tft template continuous every-seconds max]
(def options {:name "riot client"
:doc "A CLI client for RIOT api"
:commands ["playing <username> <usertag>"
{:doc "Check if the player is playing"
:command #'cmd-check-playing-adapter
:flags ["--[no-]lol" {:doc "Check if playing LOL" :default true}
"--[no-]tft" {:doc "Check if playing TFT" :default true}
"-t, --template <template>" {:doc "Text template"}
"-c, --[no-]continuous" {:doc "Print continuosly" :default false}
"-p, --period <seconds>" {:doc "If printing continuosly, period in seconds" :parse parse-long :default 60}
"-m, --max <max>" {:doc "If printing continuosly, max number of checks." :parse parse-long :default -1}
"--[no-]header" {:doc "If true, print a header over each check" :default true}]}
"matches <username> <usertag>"
{:doc "Show information about matches"
:command #'cmd-show-matches-adapter
:flags ["--[no-]lol" {:doc "Check if playing LOL" :default true}
"--[no-]tft" {:doc "Check if playing TFT" :default true}
"-f, --format <ptable|table|edn|edn-full|csv|json>" {:doc "Output format" :parse keyword :default "ptable"}
"-s, --since <time>" {:doc "Obtain registries since <time>"}
"-u, --until <time>" {:doc "Obtain registries until <time>"}
"-m, --max <max>" {:doc "Max number of registries for each game type"}]}
"stats <username> <usertag>"
{:doc "Show statistics about matches"
:command #'cmd-show-stats-adapter
:flags ["--[no-]lol" {:doc "Check if playing LOL" :default true}
"--[no-]tft" {:doc "Check if playing TFT" :default true}
"-f, --format <ptable|table|edn|csv>" {:doc "Output format" :parse keyword :default "ptable"}
"-s, --since <time>" {:doc "Obtain registries since <time>"}
"-u, --until <time>" {:doc "Obtain registries until <time>"}
"-m, --max <max>" {:doc "Max number of registries for each game type"}]}
]
:flags ["-v, --verbose" "Increases verbosity"
"--debug-http" "Show HTTP request/response data"
"--dev-key <DEV key>" "Development API key"
"--lol-key <LOL key>" "API key for LOL"
"--tft-key <TFT key>" "API key for TFT"]})
(defn -main [& args]
(cli/dispatch options args))
(comment
(-main "-v" "--input" "boniato.txt" "position1" "pos2")
)

205
src/riot/commands.clj Normal file
View File

@@ -0,0 +1,205 @@
(ns riot.commands
#_{:clj-kondo/ignore [:refer-all]}
(:require [rcorral.api.core :refer :all]
[rcorral.util :refer :all]
[rcorral.date-util :refer :all]
[riot.riot-api :refer :all]
[riot.riot-data :refer :all]
[clojure.pprint :as pp]
[clojure.string :as s]
[clj-commons.format.table :as table]
[cheshire.core :as json])
(:gen-class))
(defn cmd-test
[params]
(println "Options:")
(pp/pprint params)
(println "Positional args:" (s/join "," (:lambdaisland.cli/argv params))))
(defn cmd-check-playing
"Check if the player is playing right now."
[player-name player-tag & {:keys [lol tft template continuous every-seconds max header]
:or {lol true
tft true
template ":formatted-ts - :game: :playing"
continuous false
every-seconds 60
max nil
header true}
:as params}]
(when @debug
(println "CMD check playing")
(pp/pprint params))
(let [check-fn (fn [] (is-playing? player-name player-tag params))
task-fn (fn []
(when header (println (millis->str (System/currentTimeMillis)) ": Player" (str player-name "#" player-tag)))
(let [playing (check-fn)]
(run! #(println (replace-map (or template "Playing :game? :playing") %)) playing)
playing))]
(if continuous
(run-every task-fn every-seconds max)
(let [playing (task-fn)]
(if (some :playing playing)
(System/exit 0)
(System/exit 1))))))
(comment
(reset! debug true)
(some? (is-playing? "Walid Georgey" "EUW"))
(cmd-check-playing "Walid Georgey" "EUW") ;; This kills the REPL
(cmd-check-playing "Walid Georgey" "EUW" :continuous true :max 2) ;; Infinite loop if max is nil
)
(defn print-table
[matches-data]
(let [no-participants (map #(dissoc % :participants) matches-data)
formatted (map (comp match-format-dates match-format-durations) no-participants)]
(pp/print-table [:id :game :type :start :end :duration :winner :champion :placement]
(reverse (sort-by :start formatted)))))
(defn print-pretty-table
[matches-data]
(let [no-participants (map #(dissoc % :participants) matches-data)
formatted (map (comp match-format-dates match-format-durations) no-participants)]
(table/print-table [:id
{:key :game :title "Game Type" :align :center :formatter (comp s/upper-case name)}
:type
:start
:end
:duration
:winner
:champion
{:key :placement :align :center}]
(reverse (sort-by :start formatted)))))
(defn print-edn
[matches-data]
(pp/pprint (mapv #(select-keys % [:id :game :type :start :end :duration :winner :champion :placement]) matches-data)))
(defn print-edn-full
[matches-data]
(pp/pprint matches-data))
(defn print-json
[matches-data]
(println (json/generate-string matches-data {:pretty true})))
(comment
(print-json [{:game :lol,
:id "EUW1_7673677826",
:start 1767732132677,
:end 1767733603865,
:duration 1471,
:type "MATCHED_GAME",
:result "GameComplete"
:winner true}
{:game :tft,
:id "EUW1_7674912532",
:start 1767819383643,
:end 1767821490357,
:duration 2106,
:type "pairs",
:result "GameComplete"
:winner false}])
)
(defn print-csv
([matches-data]
(print-csv matches-data ";"))
([matches-data separator]
(when-let [m-data (map #(dissoc % :participants) matches-data)]
(println (s/join separator (map name (keys (first m-data)))))
(run! #(println (s/join separator (vals %))) m-data))))
(comment
(print-csv [{:game :lol,
:id "EUW1_7673677826",
:start 1767732132677,
:end 1767733603865,
:duration 1471,
:type "MATCHED_GAME",
:result "GameComplete"
:winner true}
{:game :tft,
:id "EUW1_7674912532",
:start 1767819383643,
:end 1767821490357,
:duration 2106,
:type "pairs",
:result "GameComplete"
:winner false}]
":"))
(defn add-calculated-data
[puuid match-data]
(-> match-data
(assoc :winner (player-won? puuid match-data))
(assoc :champion (:champion (filter-by-player puuid match-data)))
(assoc :placement (:placement (filter-by-player puuid match-data)))
))
(defn get-matches
[player-name player-tag & {:keys [lol tft since until max]
:or {lol true
tft true}
:as params}]
(let [call-params (cond-> {}
max (assoc :count max)
since (assoc :startTime (/ (process-time-literal since) 1000))
until (assoc :endTime (/ (process-time-literal until) 1000)))
lol-puuid (get-lol-puuid player-name player-tag)
tft-puuid (get-tft-puuid player-name player-tag)]
(when @debug (println "Params") (pp/pprint call-params))
(cond-> []
lol (concat (map #(add-calculated-data lol-puuid (get-lol-match-data %))
(get-lol-matches lol-puuid call-params)))
tft (concat (map #(add-calculated-data tft-puuid (get-tft-match-data %))
(get-tft-matches tft-puuid call-params))))))
(comment
(reset! debug true)
(get-matches "Walid Georgey" "EUW" :max 3))
(defn cmd-show-matches
"Show basic info about matches played by player"
[player-name player-tag & {:keys [format]
:or {format :table}
:as params}]
(when @debug
(println "CMD show matches")
(pp/pprint params))
(let [matches-data (get-matches player-name player-tag params)]
;; Then, match data
(case format
:table (print-table matches-data)
:edn (print-edn matches-data)
:edn-full (print-edn-full matches-data)
:csv (print-csv matches-data)
:json (print-json matches-data)
:ptable (print-pretty-table matches-data)
(do (println "No valid format " format) (System/exit 1)))))
(comment
(set-riot-api-key :RIOT_DEV_KEY "RGAPI-bbbed5c8-d4d7-4de4-a7e7-b8366afee5a4")
(cmd-show-matches "Walid Georgey" "EUW")
(cmd-show-matches "Walid Georgey" "EUW" :since "yesterday" :max 2))
(defn print-stats
[player-name player-tag matches-data]
(println "Statistics")
(pp/pprint (create-basic-stats player-name player-tag matches-data)))
(defn cmd-show-stats
"Show basic info about matches played by player"
[player-name player-tag & {:as params}]
(when @debug
(println "CMD show stats")
(binding [pp/*print-right-margin* 80
pp/*print-miser-width* 40]
(pp/pprint params)))
(let [matches-data (get-matches player-name player-tag params)]
(print-stats player-name player-tag matches-data)))

646
src/riot/riot_api.clj Normal file
View File

@@ -0,0 +1,646 @@
(ns riot.riot-api
(:require [rcorral.api.core :refer :all])
(:gen-class))
;;
;; ADAPT API GENERIC METHODS TO RIOT API
;;
;; You need to adapt 3 things from the generic core:
;; - A method for setting (override) the API key
;; - A method for supply the API
;; - A wrapper for calling call-api with this API endpoint, and keyprovider
;;
;; Maps with keys and supported endpoints are in a separated file (riot-data.clj)
;;
;;
;; API KEYS
;;
(def RIOT-KEYS {:RIOT_DEV_KEY (atom nil)
:RIOT_LOL_KEY (atom nil)
:RIOT_TFT_KEY (atom nil)})
;;
;; KNOWN ENDPOINTS
;;
;; Each endpoint has a URL with path params (denoted as keywords, with ':') and
;; some informative metadata about parameters and some logical grouping.
;;
;; For now, metadata is optional and it is not used in the core, but is very
;; usefull for programmers like you.
(def ENDPOINTS
{;; account v1
:account-by-puuid
{:uri "https://europe.api.riotgames.com/riot/account/v1/accounts/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["account"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:lol-account-by-riot-id
{:uri "https://europe.api.riotgames.com/riot/account/v1/accounts/by-riot-id/:player-name/:player-tag"
:api-key :RIOT_LOL_KEY
:groups ["lol" "account"]
:path-params {:player-name {:required true :type :string}
:player-tag {:required true :type :string}}
:query-params {}
:header-params {}}
:account-active-shards
{:uri "https://europe.api.riotgames.com/riot/account/v1/active-shards/by-game/:game/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["account"]
:path-params {:game {:required true :type :string}
:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; LOL status v4
:lol-status
{:uri "https://euw1.api.riotgames.com/lol/status/v4/platform-data"
:api-key :RIOT_LOL_KEY
:groups ["lol" "status"]
:path-params {}
:query-params {}
:header-params {}}
;; champion-mastery-v4
:champion-mastery-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:champion-mastery-by-puuid-and-champion
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid/by-champion/:championId"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}
:championId {:required true :type :string}}
:query-params {}
:header-params {}}
:champion-mastery-by-puuid-top
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid/top"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}}
:query-params {:count {:required false :type :int :default 3}}
:header-params {}}
;; champion v3
:champion-rotations
{:uri "https://euw1.api.riotgames.com/lol/platform/v3/champion-rotations"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion"]
:path-params {}
:query-params {}
:header-params {}}
;; summoner v4
:summoner-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "summoner"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; leage v4
:league-challenger-by-queue
{:uri "https://euw1.api.riotgames.com/lol/league/v4/challengerleagues/by-queue/:queue"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:queue {:required true :type :string}}
:query-params {}
:header-params {}}
:league-all-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/league/v4/entries/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:league-all-queue-tier-division
{:uri "https://euw1.api.riotgames.com/lol/league/v4/entries/:queue/:tier/:division"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:queue {:required true :type :string}
:tier {:required true :type :string}
:division {:required true :type :string}}
:query-params {:page {:required false :type :int :default 1}}
:header-params {}}
:league-grandmaster-by-queue
{:uri "https://euw1.api.riotgames.com/lol/league/v4/grandmasterleagues/by-queue/:queue"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:queue {:required true :type :string}}
:query-params {}
:header-params {}}
:league-by-id
{:uri "https://euw1.api.riotgames.com/lol/league/v4/leagues/:league-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:league-id {:required true :type :string}}
:query-params {}
:header-params {}}
:league-by-queue
{:uri "https://euw1.api.riotgames.com/lol/league/v4/masterleagues/by-queue/:queue"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:queue {:required true :type :string}}
:query-params {}
:header-params {}}
;; league-exp v4
:league-exp-by-queue-tier-division
{:uri "https://euw1.api.riotgames.com/lol/league-exp/v4/entries/:queue/:tier/:division"
:api-key :RIOT_LOL_KEY
:groups ["lol" "league"]
:path-params {:queue {:required true :type :string}
:tier {:required true :type :string}
:division {:required true :type :string}}
:query-params {:page {:required false :type :int :default 1}}
:header-params {}}
;; clash v1
:clash-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/clash/v1/players/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "clash"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:clash-by-team
{:uri "https://euw1.api.riotgames.com/lol/clash/v1/teams/:team-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "clash"]
:path-params {:team-id {:required true :type :string}}
:query-params {}
:header-params {}}
:clash-tournaments
{:uri "https://euw1.api.riotgames.com/lol/clash/v1/tournaments"
:api-key :RIOT_LOL_KEY
:groups ["lol" "clash"]
:path-params {}
:query-params {}
:header-params {}}
:clash-tournaments-by-team
{:uri "https://euw1.api.riotgames.com/lol/clash/v1/tournaments/by-team/:team-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "clash"]
:path-params {:team-id {:required true :type :string}}
:query-params {}
:header-params {}}
:clash-tournaments-by-tournament
{:uri "https://euw1.api.riotgames.com/lol/clash/v1/tournaments/:tournament-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "clash"]
:path-params {:tournament-id {:required true :type :int}}
:query-params {}
:header-params {}}
;; Match v5
:lol-matches-by-puuid
{:uri "https://europe.api.riotgames.com/lol/match/v5/matches/by-puuid/:puuid/ids"
:api-key :RIOT_LOL_KEY
:groups ["lol" "match"]
:path-params {:puuid {:required true :type :string}}
:query-params {:startTime {:required false :type :long}
:endTime {:required false :type :long}
:queue {:required false :type :int}
:type {:required false :type :string}
:start {:required false :type :int :default 0}
:count {:required false :type :int :default 20 :min 0 :max 100}}
:header-params {}}
:lol-match-replays-by-puuid
{:uri "https://europe.api.riotgames.com/lol/match/v5/matches/by-puuid/:puuid/replays"
:api-key :RIOT_LOL_KEY
:groups ["lol" "match"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:lol-match-by-id
{:uri "https://europe.api.riotgames.com/lol/match/v5/matches/:match-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "match"]
:path-params {:match-id {:required true :type :string}}
:query-params {}
:header-params {}}
:lol-match-timeline-by-id
{:uri "https://europe.api.riotgames.com/lol/match/v5/matches/:match-id/timeline"
:api-key :RIOT_LOL_KEY
:groups ["lol" "match"]
:path-params {:match-id {:required true :type :string}}
:query-params {}
:header-params {}}
;; LOL challenges v1
:lol-challenges-config
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/challenges/config"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {}
:query-params {}
:header-params {}}
:lol-challenge-config-by-id
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/challenges/:challenge-id/config"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {:challenge-id {:required true :type :long}}
:query-params {}
:header-params {}}
:lol-challenges-percentiles
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/challenges/percentiles"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {}
:query-params {}
:header-params {}}
:lol-challenges-percentiles-by-id
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/challenges/:challenge-id/percentiles"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {:challenge-id {:required true :type :long}}
:query-params {}
:header-params {}}
:lol-challenge-leaderboard
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/challenges/:challenge-id/leaderboards/by-level/:level"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {:challenge-id {:required true :type :long}
:level {:required true :type #{"NONE" "IRON" "BRONZE" "SILVER" "GOLD" "PLATINUM" "DIAMOND" "MASTER" "GRANDMASTER" "CHALLENGER" "HIGHEST_NOT_LEADERBOARD_ONLY" "HIGHEST" "LOWEST"}}}
:query-params {:limit {:required false :type :int}}
:header-params {}}
:lol-challenge-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/challenges/v1/player-data/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "challenges"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; LOL champion mastery v4
:lol-champion-mastery-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:lol-champion-mastery-by-puuid-champion
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid/by-champion/:champion-id"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}
:champion-id {:required true :type :int}}
:query-params {}
:header-params {}}
:lol-champion-mastery-by-puuid-top
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-puuid/:puuid/top"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}}
:query-params {:count {:required false :type :int :default 3}}
:header-params {}}
:lol-champion-mastery-scores-by-puuid
{:uri "https://euw1.api.riotgames.com/lol/champion-mastery/v4/scores/by-puuid/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "champion-mastery"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; spectator v5
:lol-spectator
{:uri "https://euw1.api.riotgames.com/lol/spectator/v5/active-games/by-summoner/:puuid"
:api-key :RIOT_LOL_KEY
:groups ["lol" "spectator"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;;
;; TFT
;;
;; account v1
:tft-account-by-riot-id
{:uri "https://europe.api.riotgames.com/riot/account/v1/accounts/by-riot-id/:player-name/:player-tag"
:api-key :RIOT_TFT_KEY
:groups ["tft" "account"]
:path-params {:player-name {:required true :type :string}
:player-tag {:required true :type :string}}
:query-params {}
:header-params {}}
;; tft status v1
:tft-status
{:uri "https://euw1.api.riotgames.com/tft/status/v1/platform-data"
:api-key :RIOT_TFT_KEY
:groups ["tft" "status"]
:path-params {}
:query-params {}
:header-params {}}
;; TFT summoner v1
:tft-summoner-by-puuid
{:uri "https://euw1.api.riotgames.com/tft/summoner/v1/summoners/by-puuid/:puuid"
:api-key :RIOT_TFT_KEY
:groups ["tft" "summoner"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; TFT match v1
:tft-matches-by-puuid
{:uri "https://europe.api.riotgames.com/tft/match/v1/matches/by-puuid/:puuid/ids"
:api-key :RIOT_TFT_KEY
:groups ["tft" "match"]
:path-params {:puuid {:required true :type :string}}
:query-params {:startTime {:required false :type :long}
:endTime {:required false :type :long}
:queue {:required false :type :int}
:type {:required false :type :string}
:start {:required false :type :int :default 0}
:count {:required false :type :int :default 20 :min 0 :max 100}}
:header-params {}}
:tft-match-by-id
{:uri "https://europe.api.riotgames.com/tft/match/v1/matches/:match-id"
:api-key :RIOT_TFT_KEY
:groups ["tft" "match"]
:path-params {:match-id {:required true :type :string}}
:query-params {}
:header-params {}}
;; TFT espectator v5
:tft-spectator
{:uri "https://euw1.api.riotgames.com/lol/spectator/tft/v5/active-games/by-puuid/:puuid"
:api-key :RIOT_TFT_KEY
:groups ["tft" "spectator"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
;; TFT league v1
:tft-league-by-puuid
{:uri "https://euw1.api.riotgames.com/tft/league/v1/by-puuid/:puuid"
:api-key :RIOT_TFT_KEY
:groups ["tft" "spectator"]
:path-params {:puuid {:required true :type :string}}
:query-params {}
:header-params {}}
:tft-league-challenger
{:uri "https://euw1.api.riotgames.com/tft/league/v1/challenger"
:api-key :RIOT_TFT_KEY
:groups ["tft" "spectator"]
:path-params {}
:query-params {:ranked {:required false :type #{"RANKED_TFT" "RANKED_TFT_DOUBLE_UP"}}}
:header-params {}}
:tft-league-by-tier-division
{:uri "https://euw1.api.riotgames.com/tft/league/v1/entries/:tier/:division"
:api-key :RIOT_TFT_KEY
:groups ["tft" "league"]
:path-params {:tier {:required true :type :string}
:division {:required true :type :string}}
:query-params {:queue {:required false :type #{"RANKED_TFT" "RANKED_TFT_DOUBLE_UP"} :default "RANKED_TFT"}
:page {:required false :type int :default 1}}
:header-params {}}
:tft-league-grandmaster-by-queue
{:uri "https://euw1.api.riotgames.com/tft/league/v1/grandmaster"
:api-key :RIOT_TFT_KEY
:groups ["tft" "league"]
:path-params {}
:query-params {:queue {:required false :type #{"RANKED_TFT" "RANKED_TFT_DOUBLE_UP"} :default "RANKED_TFT"}}
:header-params {}}
:tft-league-master-by-queue
{:uri "https://euw1.api.riotgames.com/tft/league/v1/master"
:api-key :RIOT_TFT_KEY
:groups ["tft" "league"]
:path-params {}
:query-params {:queue {:required false :type #{"RANKED_TFT" "RANKED_TFT_DOUBLE_UP"} :default "RANKED_TFT"}}
:header-params {}}
:tft-league-top-rated-ladders
{:uri "https://euw1.api.riotgames.com/tft/league/v1/rated-ladders/:queue/top"
:api-key :RIOT_TFT_KEY
:groups ["tft" "league"]
:path-params {:queue {:required true :type :string}}
:query-params {}
:header-params {}}
;; END OF ENDPOINT DEFINITIONS
})
;;
;; KEY MANAGEMENT
;;
; Not needed, but useful
(def get-riot-api-key (partial get-api-key RIOT-KEYS :RIOT_DEV_KEY))
; Not used by core, but useful for frontends
(def set-riot-api-key (partial set-api-key RIOT-KEYS))
;;
;; MAIN API FUNCTIONS
;;
; Key provider for this API
(defn riot-key-provider
[url params headers] ;; Provider must accept params and headers, but we don't need them
(when @debug (println "Getting key for" url))
(if-let [api-key (get-riot-api-key (get-in ENDPOINTS [url :api-key]))]
;; Return params unmodified and headers with the api header
[params (assoc headers "X-Riot-Token" api-key)]
(println "No API key found for" url)))
(comment
(get-in ENDPOINTS [:lol-status :api-key])
(riot-key-provider :lol-status {:param1 "1" :param2 "two"} {"Accept" "data"})
)
; Wrapper for ease use of call-api
(defn call-riot-api
[url & {:as cfg-params}]
(call-api ENDPOINTS riot-key-provider url cfg-params))
;;
;; HELPERS
;;
(defn get-lol-puuid
"Get PUUID from player's name and tag for LOL endpoints"
[player-name player-tag]
(:puuid (:value (call-riot-api :lol-account-by-riot-id
:path-params {:player-name player-name :player-tag player-tag}))))
(defn get-tft-puuid
"Get PUUID from player's name and tag for TFT endpoints"
[player-name player-tag]
(:puuid (:value (call-riot-api :tft-account-by-riot-id
:path-params {:player-name player-name :player-tag player-tag}))))
;;
;; USAGE EXAMPLES
;;
(comment
(set-riot-api-key :RIOT_DEV_KEY "RGAPI-bbbed5c8-d4d7-4de4-a7e7-b8366afee5a4")
(deref (:RIOT_DEV_KEY RIOT-KEYS))
(call-riot-api :lol-account-by-riot-id :path-params {:player-name "Walid Georgey"
:player-tag "EUW"})
(get-lol-puuid "Walid Georgey" "EUW")
(get-tft-puuid "Walid Georgey" "EUW")
;; Examples calling the API
(call-riot-api :lol-account-by-puuid
:path-params {:puuid "Walid Georgey" :player-tag "EUW"})
(:value (call-riot-api :lol-status))
(call-riot-api :champion-mastery-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :champion-mastery-by-puuid-and-champion
:path-params {:championId 45
:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :champion-mastery-by-puuid-top
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")}
:query-params {:count 1})
(call-riot-api :champion-rotations)
(call-riot-api :summoner-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :league-all-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :league-by-id
:path-params {:league-id "35642608-b436-48a0-ac0f-b64eb6dfc14e"})
(call-riot-api :clash-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :clash-tournaments)
(call-riot-api :lol-matches-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")}
:query-params {:count 5})
(call-riot-api :lol-match-by-id
:path-params {:match-id "EUW1_7673677826"})
(call-riot-api :lol-match-timeline-by-id
:path-params {:match-id "EUW1_7673677826"})
(call-riot-api :lol-challenges-config)
(call-riot-api :lol-challenge-config-by-id
:path-params {:challenge-id 402109})
(call-riot-api :lol-challenges-percentiles)
(call-riot-api :lol-challenges-percentiles-by-id
:path-params {:challenge-id 402109})
(call-riot-api :lol-challenge-leaderboard
:path-params {:challenge-id 402109
:level "CHALLENGER"})
(call-riot-api :lol-challenge-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :lol-champion-mastery-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :lol-champion-mastery-by-puuid-champion
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")
:champion-id 777})
(call-riot-api :lol-champion-mastery-by-puuid-top
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")}
:query-params {:count 5})
(call-riot-api :lol-champion-mastery-scores-by-puuid
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
(call-riot-api :lol-spectator
:path-params {:puuid (get-lol-puuid "Walid Georgey" "EUW")})
;; TFT
(call-riot-api :tft-summoner-by-puuid
:path-params {:puuid (get-tft-puuid "Walid Georgey" "EUW")})
(call-riot-api :tft-status)
(call-riot-api :tft-matches-by-puuid
:path-params {:puuid (get-tft-puuid "Walid Georgey" "EUW")})
(call-riot-api :tft-match-by-id
:path-params {:match-id "EUW1_7666482502"})
(call-riot-api :tft-spectator
:path-params {:puuid (get-tft-puuid "Walid Georgey" "EUW")})
(call-riot-api :tft-league-by-puuid
:path-params {:puuid (get-tft-puuid "Walid Georgey" "EUW")})
(call-riot-api :tft-league-challenger)
(call-riot-api :tft-league-grandmaster-by-queue)
(call-riot-api :tft-league-master-by-queue)
(call-riot-api :tft-league-top-rated-ladders
:path-params {:queue "RANKED_TFT_TURBO"})
(call-riot-api :tft-league-by-league-id
:path-params {:league-id "abc"})
)

339
src/riot/riot_data.clj Normal file
View File

@@ -0,0 +1,339 @@
(ns riot.riot-data
(:require [rcorral.api.core :refer :all]
[rcorral.date-util :refer :all]
[riot.riot-api :refer :all]
[tick.core :as t])
(:import [java.util Date])
(:gen-class))
;;
;; High functions to work with RIOT API.
;;
;; This file has a lot of utilities for working with data obtained with the help
;; of riot-api's functions
(def GAME-TYPES #{:lol :tft})
;; Get matches list
(defn get-lol-matches
"Get LOL matches for player"
[puuid & {:as params}]
;(println "Params:" params)
(let [response (call-riot-api :lol-matches-by-puuid
:path-params {:puuid puuid}
:query-params params)]
(if (:success response)
(:value response)
(do (println "No LOL matches found") response))))
(defn get-tft-matches
"Get TFT matches for player"
[puuid & {:as params}]
;(println "Params:" params)
(let [response (call-riot-api :tft-matches-by-puuid
:path-params {:puuid puuid}
:query-params params)]
(if (:success response)
(:value response)
(do (println "No TFT matches found") response))))
(comment
(get-lol-matches (get-lol-puuid "Walid Georgey" "EUW") :count 5)
(get-lol-matches (get-lol-puuid "Errepunto" "4595") :count 5)
(get-tft-matches (get-tft-puuid "Walid Georgey" "EUW") :count 5)
(get-tft-matches (get-tft-puuid "Errepunto" "4595") :count 5)
)
;; Get matches data
(defn filter-by-game
[game matches]
(filter #(= game (:game %)) matches))
(defn filter-by-player
[puuid match-data]
(first (filter #(= (:puuid %) puuid) (:participants match-data))))
(defn player-won?
[puuid match-data]
(if (and puuid match-data)
(:win (filter-by-player puuid match-data))
(do (println "Invalid puuid or match data") false)))
(defn player-alive?
[puuid participants]
(let [player-data (first (filter #(= (:puuid %) puuid) participants))
max-round (apply max (mapv :last_round participants))]
(= (:last_round player-data) max-round)))
(defn create-match-data
[game matchId participants gameStartTimestamp gameEndTimestamp gameDuration gameType endOfGameResult]
{:game game
:id matchId
:participants participants
:start gameStartTimestamp
:end gameEndTimestamp
:duration gameDuration
:type gameType
:result endOfGameResult})
(defn create-participant
[puuid championName deaths kills win placement]
{:puuid puuid
:champion championName
:deaths deaths
:kills kills
:win win
:placement placement})
(defn get-lol-match-data
"Get data from a LOL match"
[id]
(let [response (call-riot-api :lol-match-by-id :path-params {:match-id id})
info (get-in response [:value :info])
meta (get-in response [:value :metadata])]
;(println "Value:" info)
(if (:success response)
(create-match-data :lol
(:matchId meta)
(map #(create-participant (:puuid %)
(:championName %)
(:deaths %)
(:kills %)
(:win %)
(if (:win %) 1 2))
(:participants info))
(:gameStartTimestamp info)
(:gameEndTimestamp info)
(:gameDuration info)
(:gameType info)
(:endOfGameResult info))
(do (println "No match data found") response))))
(defn get-tft-match-data
"Get data from a TFT match"
[id]
(let [response (call-riot-api :tft-match-by-id :path-params {:match-id id})
info (get-in response [:value :info])
meta (get-in response [:value :metadata])]
;(println "Value:" info)
(if (:success response)
(create-match-data :tft
(:match_id meta)
(map #(create-participant (:puuid %)
(get-in % [:companion :species] nil)
(if (player-alive? (:puuid %) (:participants info)) 0 1)
(:players_eliminated %)
(:win %)
(:placement %))
(:participants info))
(:game_datetime info)
(+ (:game_datetime info) (int (* 1000 (:game_length info))))
(int (:game_length info))
(:tft_game_type info)
(:endOfGameResult info))
(do (println "No match data found") response))))
(comment
(get-lol-match-data "EUW1_7673677826")
(get-tft-match-data "EUW1_7674912532")
(player-won? (get-lol-puuid "Walid Georgey" "EUW")
(get-lol-match-data "EUW1_7673677826"))
(player-won? (get-tft-puuid "Walid Georgey" "EUW")
(get-tft-match-data "EUW1_7695774670"))
(get-tft-match-data "EUW1_7695029198")
(player-alive? "aWw6WWUdssl6KXMqTsgVZn4iyPfC3J2sa0sdQ7lFzQX0zzV5dzivGE-ihn3OIARf5alUkO2pPu4ztA";(get-tft-puuid "Walid Georgey" "EUW")
(:participants (:info (:value (call-riot-api :tft-match-by-id :path-params {:match-id "EUW1_7695774670"})))))
(call-riot-api :tft-match-by-id :path-params {:match-id "EUW1_7695774670"})
(System/currentTimeMillis)
1767819383643
1767819383643
)
;; Check if it's playing
(defn is-playing-lol?
[puuid]
(let [response (call-riot-api :lol-spectator
:path-params {:puuid puuid})
http-code (:http-code response)]
(= 200 http-code)))
(defn is-playing-tft?
[puuid]
(let [response (call-riot-api :tft-spectator
:path-params {:puuid puuid})
http-code (:http-code response)]
(= 200 http-code)))
(defn create-playing
[timestamp game playing player-name player-tag]
{:timestamp timestamp
:formatted-ts (millis->str timestamp)
:game game
:playing playing
:player-name player-name
:player-tag player-tag})
(comment
(is-playing-lol? (get-lol-puuid "Walid Georgey" "EUW"))
(is-playing-tft? (get-tft-puuid "Walid Georgey" "EUW"))
)
(defn is-playing?
[player-name player-tag & {:keys [lol tft]
:or {lol true
tft true}}]
(let [now (System/currentTimeMillis)]
(cond-> []
lol (conj (create-playing now :lol (is-playing-lol? (get-lol-puuid player-name player-tag)) player-name player-tag))
tft (conj (create-playing now :tft (is-playing-tft? (get-tft-puuid player-name player-tag)) player-name player-tag)))))
(comment
(is-playing? "Walid Georgey" "EUW")
)
;; Format dates and times
(defn match-format-dates
([data]
(match-format-dates "yyyy-MM-dd HH:mm:ss" data))
([date-format data]
(-> data
(update-in [:start] (partial millis->str date-format))
(update-in [:end] (partial millis->str date-format)))))
(comment
(t/format (t/formatter "yyyy-MM-dd HH:mm:ss")
(t/zoned-date-time (t/instant (System/currentTimeMillis))))
(millis->str 1769153258168)
(t/format (t/formatter "yyyy-MM-dd HH:mm:ss")
(t/in (t/instant 1769153258168) (t/zone "UTC")))
(t/format (t/formatter "yyyy-MM-dd HH:mm:ss")
(t/zoned-date-time (t/instant 1769153258168)))
(t/format (t/formatter "yyyy-MM-dd HH:mm:ss")
(t/zoned-date-time (t/in (t/instant 1769153258168) (t/zone "UTC"))))
(t/zone)
(match-format-dates (get-lol-match-data "EUW1_7673677826"))
(match-format-dates (get-tft-match-data "EUW1_7674912532"))
)
(defn match-format-durations
[data]
(update-in data [:duration] duration->str))
(comment
(t/format (t/formatter "HH:mm:ss") (t/new-duration 2106 :seconds))
(t/hours (t/new-duration 2106 :seconds))
(t/minutes (t/new-duration 2106 :seconds))
(t/seconds (t/new-duration 2106 :seconds))
(duration->str (+ (* 60 35) 6))
(duration->str (+ (* 60 60 17) (* 60 35) 6))
(duration->str (+ (* 24 60 60 3) (* 60 60 17) (* 60 35) 6))
(match-format-durations (get-lol-match-data "EUW1_7673677826"))
(match-format-durations (get-tft-match-data "EUW1_7674912532"))
)
;; Stats
(defn play-time-statistics
[matches]
(let [num-matches (count matches)
total (reduce + 0 (map :duration matches))
average (if (zero? num-matches) 0 (float (/ total num-matches)))]
{:total total
:total-formatted (duration->str total)
:average average
:average-formatted (duration->str (int average))}))
(defn win-statistics
[matches]
(let [total (count matches)
win (count (filter :winner matches))
loss (- total win)]
{:total total
:win win
:loss loss
:win-percent (if (zero? total) 0 (float (* 100 (/ win total))))
:loss-percent (if (zero? total) 0 (float (* 100 (/ loss total))))}))
(defn kd-ratio
[p-data]
(let [kills (or (:kills p-data) 0)
deaths (or (:deaths p-data) 0)]
(if (> deaths 0) (float (/ kills deaths)) 0)))
(defn kill-statistics
[puuid matches]
(let [total (count matches)
; extracts player's data from participands, and adds match's id and k/d ratio
player-data (map (fn [p m] (-> p
(assoc :match-id (:id m))
(assoc :k-d (kd-ratio p))))
(map #(filter-by-player puuid %) matches)
matches)
total-kills (reduce + (map #(:kills %) player-data))
total-deaths (reduce + (map #(:deaths %) player-data))
ratio-total (if (and total-deaths total-kills (> total-kills 0)) (float (/ total-kills total-deaths)) 0)]
{:total total
:deads (or total-deaths 0)
:kills (or total-kills 0)
:ratio ratio-total
:best-ratio (:k-d (last (sort-by :k-d player-data)))
:worst-ratio (:k-d (first (sort-by :k-d player-data)))
:best-champion (:champion (last (sort-by :k-d player-data)))
:worst-champion (:champion (first (sort-by :k-d player-data)))
:best-match (:match-id (last (sort-by :k-d player-data)))
:worst-match (:match-id (first (sort-by :k-d player-data)))}))
(defn create-basic-stats
[player-name player-tag matches]
(let [lol-games (filter-by-game :lol matches)
tft-games (filter-by-game :tft matches)]
{:win-stats {:all (win-statistics matches)
:lol (win-statistics lol-games)
:tft (win-statistics tft-games)}
:play-time {:all (play-time-statistics matches)
:lol (play-time-statistics lol-games)
:tft (play-time-statistics tft-games)}
:kill-stats {:lol (kill-statistics (get-lol-puuid player-name player-tag) lol-games)
:tft (kill-statistics (get-tft-puuid player-name player-tag) tft-games)}}))
(comment
(create-basic-stats "Walid Georgey" "EUW"
(concat (map get-lol-match-data (get-lol-matches (get-lol-puuid "Walid Georgey" "EUW") :count 5))
(map get-tft-match-data (get-tft-matches (get-tft-puuid "Walid Georgey" "EUW") :count 5))))
)

6
tests.edn Normal file
View File

@@ -0,0 +1,6 @@
#kaocha/v1
{:tests [{:test-paths ["src" "test"]}]
:plugins [:kaocha.plugin/cloverage]
:cloverage/opts {:src-ns-path ["src"]
;;:ns-regex ["totp\\..*(?<!test)$"] ;; All starting with "totp" but not ending by "test"
}}