IGDB API
Mix.install([
{:req, "~> 0.5.0"},
{:explorer, "~> 0.8.0"},
{:kino_explorer, "~> 0.1.19"},
{:jason, "~> 1.4"}
])
Description
We use IGDB API as the single source of data about games, platforms, game developers, etc.
Resources
- Twitch dev account - client id and client secret here
- Docs
- Dashboard
Auth token
Twitch auth token is required for all subsequent requests.
{:ok, %{body: resp}} =
Req.post("https://id.twitch.tv/oauth2/token",
form: [
client_id: System.get_env("LB_TWITCH_CLIENT_ID"),
client_secret: System.get_env("LB_TWITCH_CLIENT_SECRET"),
grant_type: "client_credentials"
]
)
auth_token = resp["access_token"]
base_request =
Req.new(
base_url: "https://api.igdb.com/v4/",
method: :post,
headers: %{
"Accept" => "application/json",
"Client-ID" => System.get_env("LB_TWITCH_CLIENT_ID")
},
auth: {:bearer, auth_token}
)
Fetch single game
We use this endpoint to render game page in Igroteka. Requires game’s ID (integer, defined by IGDB).
game_by_id_query = fn id ->
"""
where id = #{id};
fields aggregated_rating,aggregated_rating_count,
first_release_date,name,summary,url,
category,status,
storyline,cover.image_id,
platforms.id,platforms.name,
franchises.id,franchises.name,
websites.id,websites.category,websites.url,
videos.video_id,
videos.name,
themes.id,themes.name,
genres.id,genres.name,
involved_companies.developer,
involved_companies.publisher,
involved_companies.company.id,
involved_companies.company.name,
involved_companies.company.url,
involved_companies.company.country,
involved_companies.company.logo.url;
"""
end
fetch_game = fn id ->
Req.request!(base_request, url: "/games", body: game_by_id_query.(id)).body
end
frame = Kino.Frame.new()
form =
Kino.Control.form(
[
game_id: Kino.Input.text("Game id")
],
submit: "Submit"
)
Kino.listen(form, fn %{data: %{game_id: game_id}} ->
loading = Kino.Markdown.new("Loading #{game_id}...")
Kino.Frame.render(frame, loading)
[resp] = fetch_game.(game_id)
content =
Kino.Markdown.new("""
### #{resp["name"]} (#{resp["id"]})
*[IGDB](#{resp["url"]}) | [Igroteka](https://igroteka.cc/games/#{resp["id"]}/info)*
**Release date:** #{DateTime.from_unix!(resp["first_release_date"])}
**IGDB rating:** #{resp["aggregated_rating"]} (#{resp["aggregated_rating_count"]})
**Platforms:** #{Enum.map(resp["platforms"], fn o -> o["name"] end) |> Enum.join(", ")}
**Genres:** #{Enum.map(resp["genres"], fn o -> o["name"] end) |> Enum.join(", ")}
**Themes:** #{Enum.map(resp["themes"], fn o -> o["name"] end) |> Enum.join(", ")}
**Creators:** #{Enum.map(resp["involved_companies"], fn o -> o["company"]["name"] end) |> Enum.join(", ")}
#### Summary
#{resp["summary"]}
#### Storyline
#{resp["storyline"]}
### Raw result
```json
#{Jason.encode!(resp, pretty: true)}
```
""")
Kino.Frame.render(frame, content)
end)
Kino.Layout.grid([form, frame], boxed: true, gap: 16)
Example ids to use:
- 254339 (Super Mario Wonder)
- 31551 (FF XVI)
- 119388 (Zelda TOTK)
- 4 (Thief)
- 13 (Fallout)
Search
Provide search term, get list of games in return.
search_query = fn term ->
"""
search "#{term}";
where game != null & game.first_release_date != null;
fields game.aggregated_rating,game.aggregated_rating_count,game.first_release_date,game.name,game.summary,
game.url,game.cover.image_id,
game.involved_companies.developer,game.involved_companies.publisher,
game.involved_companies.company.id,game.involved_companies.company.name,game.involved_companies.company.url,
game.involved_companies.company.country, game.platforms.id, game.platforms.name;
"""
end
search_games = fn term ->
Req.request!(base_request, url: "/search", body: search_query.(term)).body
end
frame = Kino.Frame.new()
render_game = fn g ->
"""
---
#### #{g["game"]["name"]}
*[IGDB](#{g["game"]["url"]}) | [Igroteka](https://igroteka.cc/games/#{g["game"]["id"]}/info)*
"""
end
form =
Kino.Control.form(
[
term: Kino.Input.text("Search term")
],
submit: "Submit"
)
Kino.listen(form, fn %{data: %{term: term}} ->
loading = Kino.Markdown.new("Searching \"#{term}\"...")
Kino.Frame.render(frame, loading)
resp = search_games.(term)
content =
Kino.Markdown.new("""
### Games
#{Enum.map(resp,
fn game_json -> render_game.(game_json) end) |> Enum.join("\n\n")}
### Raw result
```json
#{Jason.encode!(resp, pretty: true)}
```
""")
Kino.Frame.render(frame, content)
end)
Kino.Layout.grid([form, frame], boxed: true, gap: 16)
Top games list
Populates “Top games” view (filtered by year and/or platform)
top_games_query =
"""
where first_release_date != null & aggregated_rating != null & aggregated_rating_count > 5 & aggregated_rating > 79 & name != "The Witness";
sort aggregated_rating desc;
limit 20;
fields name,aggregated_rating,aggregated_rating_count,first_release_date,summary,url,cover.image_id,
involved_companies.developer,involved_companies.publisher,
involved_companies.company.id,involved_companies.company.name,involved_companies.company.url,
involved_companies.company.country;
"""
fetch_top_games = fn ->
Req.request!(base_request, url: "/games", body: top_games_query).body
end
resp = fetch_top_games.()
render_game = fn g ->
"""
---
#### #{g["name"]}
*[IGDB](#{g["url"]}) | [Igroteka](https://igroteka.cc/games/#{g["id"]}/info)*
"""
end
Kino.Markdown.new("""
### Games
#{Enum.map(resp,
fn game_json -> render_game.(game_json) end) |> Enum.join("\n\n")}
```
""")
New games list
Populates “New games” view
new_games_query =
"""
where first_release_date != null & aggregated_rating != null & aggregated_rating_count > 5 & aggregated_rating > 79;
sort first_release_date desc;
limit 30;
fields name,aggregated_rating,aggregated_rating_count,first_release_date,summary,url,cover.image_id,
involved_companies.developer,involved_companies.publisher,
involved_companies.company.id,involved_companies.company.name,involved_companies.company.url,
involved_companies.company.country;
"""
fetch_new_games = fn ->
Req.request!(base_request, url: "/games", body: new_games_query).body
end
resp = fetch_new_games.()
render_game = fn g ->
"""
---
#### #{g["name"]}
*[IGDB](#{g["url"]}) | [Igroteka](https://igroteka.cc/games/#{g["id"]}/info)*
"""
end
Kino.Markdown.new("""
### Games
#{Enum.map(resp,
fn game_json -> render_game.(game_json) end) |> Enum.join("\n\n")}
```
""")
Company
Fetch single company
company_by_id_query = fn id ->
"""
where id = #{id};
fields id,name,country,description,logo.*,start_date,url,websites.*;
"""
end
fetch_company = fn id ->
Req.request!(base_request, url: "/companies", body: company_by_id_query.(id)).body
end
frame = Kino.Frame.new()
form =
Kino.Control.form(
[
id: Kino.Input.text("Company id")
],
submit: "Submit"
)
Kino.listen(form, fn %{data: %{id: id}} ->
loading = Kino.Markdown.new("Loading #{id}...")
Kino.Frame.render(frame, loading)
[resp] = fetch_company.(id)
content =
Kino.Markdown.new("""
### #{resp["name"]} (#{resp["id"]})
*[IGDB](#{resp["url"]}) | [Igroteka](https://igroteka.cc/companies/#{resp["id"]}/developed)*
#### Description
#{resp["description"]}
### Raw result
```json
#{Jason.encode!(resp, pretty: true)}
```
""")
Kino.Frame.render(frame, content)
end)
Kino.Layout.grid([form, frame], boxed: true, gap: 16)