summaryrefslogtreecommitdiff
path: root/steam.lisp
blob: 6d47153f2bbf4d9bec31d474efe5fdb2cecaa3c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(in-package :arclade)

(defvar steam-host "api.steampowered.com")

;; JSON
;; * The API returns an object containing the named object with the result data.
;; * Arrays are represented as an array with the name of the type of the objects
;;   in the array (ie. an object named "items" containing an array of objects
;;  of type "item" would be represented as an object named "items" containing
;;  an array named "item" containing several objects following the "item" structure).
;; * Null is represented as JSON's null.

(defun steam-games-uri ()
  "Builds the uri & query params to get owned games for the configured steam id."
  (quri:render-uri
   (quri:make-uri-http
    :host steam-host
    :path "IPlayerService/GetOwnedGames/v0001/"
    :query (quri:url-encode-params `(("key" . ,(steam-key *config*))
                                     ("steamid" . ,(steam-user-id *config*))
                                     ("include_appinfo" . "true"))))))

(defun fetch-steam-games ()
  "Fetch configured user's steam games and return parsed JSON."
  (derrida:with-keypaths ((games :|response| :|games|))
      (json:parse
       (flexi-streams:octets-to-string
        (drakma:http-request (steam-games-uri))))
    games))

(defun make-steam-game (data)
  "Make a STEAM-GAME instance from DATA, a parsed JSON form from Steam's API."
  (with-plist ((id :|appid|) (playtime :|playtime_forever|) (name :|name|)
               (icon-url :|img_icon_url|) (last-played :|rtime_last_played|))
      data
    (let ((game (make-instance 'steam-game :name name :appid id)))
      (setf (playtime game) playtime
            (icon-url game) icon-url
            (last-played game) last-played)
      game)))

(defun steam-game-schema-uri (game)
  "Returns URI and query params to get detailed info about a game.

RESULT DATA
game
  gameName (string)
    Steam internal (non-localized) name of game.
  gameVersion (int)
    Steam release version number currently live on Steam.
  availableGameStats
    achievements (Optional) (array)
      name (string)
        API Name of achievement.
      defaultvalue (int)
        Always 0 (player's default state is unachieved).
      displayName (string)
        Display title string of achievement.
      hidden (int)
        If achievement is hidden to the user before earning
        achievement, value is 1. 0 if public.
      description (string)
        Display description string of achievement.
      icon (string)
        Absolute URL of earned achievement icon art.
      icongray (string)
        Absolute URL of un-earned achievement icon art."
  (quri:render-uri
   (quri:make-uri-http
    :host steam-host
    :path "ISteamUserStats/GetSchemaForGame/v2/"
    :query (quri:url-encode-params `(("key" . ,(steam-key *config*))
                                     ("appid" . ,(appid game))
                                     ("l" . "en"))))))

(defun fetch-steam-game-schema (game)
  (derrida:with-keypaths ((achievements :|game| :|availableGameStats| :|achievements|))
      (json:parse
       (flexi-streams:octets-to-string
        (drakma:http-request (steam-game-schema-uri game))))
    achievements))

(defun steam-achievements-uri (game)
  "Builds uri & query params for configured steam id's achievements for GAME."
  (quri:render-uri
   (quri:make-uri-http
    :host steam-host
    :path "ISteamUserStats/GetPlayerAchievements/v0001/"
    :query (quri:url-encode-params `(("key" . ,(steam-key *config*))
                                     ("steamid" . ,(steam-user-id *config*))
                                     ("appid" . ,(appid game))
                                     ("l" . "en"))))))

(defun fetch-steam-achievements (steam-game)
  (derrida:with-keypaths ((success :|playerstats| :|success|)
                          (achievements :|playerstats| :|achievements|))
      (json:parse
       (flexi-streams:octets-to-string
        (drakma:http-request (steam-achievements-uri steam-game))))
    (when success achievements)))

(defun make-steam-achievement (game schema stats)
  (with-plist ((icon :|icon|) (icongray :|icongray|))
      schema
    (with-plist ((name :|name|) (desc :|description|) (time :|unlocktime|)
		 (achieved :|achieved|) (apiname :|apiname|))
	stats
      (db:with-transaction ()
	(let ((rec (make-instance 'steam-achievement
				  :game game :name name :description desc)))
	  (setf (icon rec) icon
		(icongray rec) icongray
		(apiname rec) apiname)
	  (unless (zerop achieved)
	    (setf (fulfillment rec) (+ time *epoch*)))
	  rec)))))

(defun load-steam-games ()
  "Fetch and wrap STEAM-GAMEs as persistent objects.
WARNING! Not idempotent!"
  (db:with-transaction ()
    (loop for data in (fetch-steam-games)
	  do (make-steam-game data))))

(defun load-achievement-data (game)
    "Fetch and wrap STEAM-ACHIEVEMENTs as persistent objects.
WARNING! Not idempotent!"
  (loop for schema in (fetch-steam-game-schema game)
	for achievements in (fetch-steam-achievements game)
	do (make-steam-achievement game schema achievements)))