From 34388e59f28500ebb94f210f30295e02e53bdefd Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Thu, 16 Apr 2020 03:52:33 -0400 Subject: [PATCH] Improve and document replay search API --- WEB-API.md | 58 +++++++++++++++++++++++++++++++++++++++++ replays/README.md | 12 +++++++++ replays/replays.lib.php | 11 ++++---- replays/search.json.php | 23 ++++++++++------ 4 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 WEB-API.md create mode 100644 replays/README.md diff --git a/WEB-API.md b/WEB-API.md new file mode 100644 index 000000000..a673f2eab --- /dev/null +++ b/WEB-API.md @@ -0,0 +1,58 @@ +Pokémon Showdown website APIs +============================= + +Most PS APIs that you would want to access programmatically are available by adding `.json` to the URL. + + +Replays +------- + +Getting a replay: + +https://replay.pokemonshowdown.com/gen8doublesubers-1097585496.json + +https://replay.pokemonshowdown.com/gen8doublesubers-1097585496.log + + +Replay search +------------- + +List recent replays: + +https://replay.pokemonshowdown.com/search.json + +Search by user: + +https://replay.pokemonshowdown.com/search.json?user=zarel + +Search by multiple users: + +https://replay.pokemonshowdown.com/search.json?user=zarel&user2=yuyuko + +Search by format: + +https://replay.pokemonshowdown.com/search.json?format=gen8ou + +Combined searching: + +https://replay.pokemonshowdown.com/search.json?user=zarel&user2=yuyuko&format=gen7randombattle + +Paginate searches: + +https://replay.pokemonshowdown.com/search.json?user=zarel&page=2 + +Searches are limited to 51 results, and pages are offset by 50 each, so the existence of a 51st result means that there's at least one more page available. + +Pagination is not supported for the recent replays list, but is supported for everything else. + + +Users (including ladder information) +------------------------------------ + +https://pokemonshowdown.com/users/zarel.json + + +Ladders +------- + +https://pokemonshowdown.com/ladder/gen8ou.json diff --git a/replays/README.md b/replays/README.md new file mode 100644 index 000000000..36ce942fc --- /dev/null +++ b/replays/README.md @@ -0,0 +1,12 @@ +PS replays database +=================== + +This is the code powering https://replay.pokemonshowdown.com/ + + +JSON API +-------- + +The replays database has a JSON API, documented at: + +https://github.com/smogon/pokemon-showdown-client/blob/master/WEB-API.md diff --git a/replays/replays.lib.php b/replays/replays.lib.php index 760700f89..5add58926 100644 --- a/replays/replays.lib.php +++ b/replays/replays.lib.php @@ -94,13 +94,14 @@ class Replays { $isPrivate = ($args["isPrivate"] ?? null) ? 1 : 0; $byRating = $args["byRating"] ?? null; + $format = ($args["format"] ?? null) ? $this->toId($args["format"]) : null; + if ($args["username"] ?? null) { $order = $byRating ? "rating" : "uploadtime"; $userid = $this->toId($args["username"]); if ($args["username2"] ?? null) { $userid2 = $this->toId($args["username2"]); - if ($args["format"] ?? null) { - $format = $this->toId($args["format"]); + if ($format) { $res = $this->db->prepare("(SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p1) WHERE private = ? AND p1id = ? AND p2id = ? AND format = ? ORDER BY $order DESC) UNION (SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p1) WHERE private = ? AND p1id = ? AND p2id = ? AND format = ? ORDER BY $order DESC) ORDER BY $order DESC LIMIT $limit1, 51;"); $res->execute([$isPrivate, $userid, $userid2, $format, $isPrivate, $userid2, $userid, $format]); } else { @@ -108,9 +109,9 @@ class Replays { $res->execute([$isPrivate, $userid, $userid2, $isPrivate, $userid2, $userid]); } } else { - if ($args["format"] ?? null) { + if ($format) { $res = $this->db->prepare("(SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p1) WHERE private = ? AND p1id = ? AND format = ? ORDER BY $order DESC) UNION (SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p2) WHERE private = ? AND p2id = ? AND format = ? ORDER BY uploadtime DESC) ORDER BY $order DESC LIMIT $limit1, 51;"); - $res->execute([$isPrivate, $userid, $this->toId($args["format"]), $isPrivate, $userid, $format]); + $res->execute([$isPrivate, $userid, $format, $isPrivate, $userid, $format]); } else { $res = $this->db->prepare("(SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p1) WHERE private = ? AND p1id = ? ORDER BY $order DESC) UNION (SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (p2) WHERE private = ? AND p2id = ? ORDER BY uploadtime DESC) ORDER BY $order DESC LIMIT $limit1, 51;"); $res->execute([$isPrivate, $userid, $isPrivate, $userid]); @@ -125,7 +126,7 @@ class Replays { } else { $res = $this->db->prepare("SELECT uploadtime, id, format, p1, p2, password FROM ps_replays FORCE INDEX (format) WHERE private = ? AND formatid = ? ORDER BY uploadtime DESC LIMIT $limit1, 51"); } - $res->execute([$isPrivate, $this->toId($args["format"])]); + $res->execute([$isPrivate, $format]); return $res->fetchAll(); } diff --git a/replays/search.json.php b/replays/search.json.php index 99edcb7a3..3e2a3b6d7 100644 --- a/replays/search.json.php +++ b/replays/search.json.php @@ -1,7 +1,8 @@ userid($username); $isPrivateAllowed = ($username === $curuser['userid'] || $curuser['userid'] === 'zarel'); +if ($isPrivate && !$isPrivateAllowed) { + die('"ERROR: access denied"'); +} $page = intval($_REQUEST['page'] ?? 0); $replays = null; if ($page > 25) { - // no -} else if ($username) { - if (!$isPrivate || $isPrivateAllowed) { - $replays = $Replays->search(["username" => $username, "isPrivate" => $isPrivate, "page" => $page]); - } + die('"ERROR: page limit is 25"'); +} else if ($username || $format) { + $replays = $Replays->search([ + "username" => $username, + "username2" => $username2, + "format" => $format, + "byRating" => $byRating, + "isPrivate" => $isPrivate, + "page" => $page + ]); } else if ($contains) { $replays = $Replays->fullSearch($contains, $page); -} else if ($format) { - $replays = $Replays->search(["format" => $format, "byRating" => $byRating, "page" => $page]); } else { $replays = $Replays->recent(); }