This repository has been archived by the owner on Jul 22, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
py_gjapi.py
364 lines (326 loc) · 13.8 KB
/
py_gjapi.py
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# Game Jolt Trophy for Python
# by viniciusepiplon - [email protected]
# version 1.1
# Python 3.x stable
# Python 2.7 unstable
# This is a general Python module for manipulating user data and
# trophies (achievments) on GameJolt.
# Website: www.gamejolt.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
import sys
import hashlib
import json
if sys.hexversion > 0x03000000:
try:
import urllib.request
except:
raise ImportError
else:
try:
import urllib
except:
raise ImportError
class GameJoltTrophy(object):
"""
The Class constructors.
The class requires four essential parameters: user name, user token, game ID
and private code. Check the API documentation on Game Jolt's website to see
what those parameters they are. In this code, I used the same names on the
site. If you read it, you can understand what's going on here.
Note that *username* and *user token* can be changed later, but the game id
and the private key must be defined first, as they won't change.
"""
def __init__(self, username, user_token, game_id, private_key):
super(GameJoltTrophy, self).__init__()
self.username = username
self.user_token = user_token
self.game_id = game_id
self.private_key = private_key
self.URL = 'http://gamejolt.com/api/game/v1'
self.nativeTraceback = False
#====== TOOLS ======#
# Used for changing users, setting and/or fixing authentications
def changeUsername(self, username):
"""
Changes the *username* contained on the object
Used for changing, setting and/or fixing authentications
"""
self.username = username
#
def changeUserToken(self, user_token):
"""
Changes the *user token* contained on the object
Used for changing, setting and/or fixing authentications
"""
self.user_token = user_token
def setSignatureAndgetJSONResponse(self, URL):
"""
Generates a signature from the url and returns the same address, with the
signature added to it.
All singatures are generated with md5, but can be modified below.
This is the only function that generates the signature, so changing the
encoding to SHA1 or other format will affect all URL requests.
"""
if sys.hexversion > 0x03000000:
try:
link = URL + str(self.private_key)
link = link.encode('ascii')
signature = hashlib.md5(link).hexdigest()
URL += '&'+'signature='+str(signature)
response = urllib.request.urlopen(URL)
output = response.read().decode('utf8')
return json.loads(output)['response']
except Exception as error:
if not self.nativeTraceback:
return {'success': 'false', 'message': str(error)}
else:
raise error
else:
try:
link = URL + str(self.private_key)
link = link.encode('ascii')
signature = hashlib.md5(link).hexdigest()
URL += '&'+'signature='+str(signature)
response = urllib.urlopen(URL)
output = response.read().decode('utf8')
return json.loads(output)['response']
except Exception as error:
if not self.nativeTraceback:
return {'success': 'false', 'message': str(error)}
else:
raise error
def setNativeTraceback(self, value):
if not type(value) == bool: self.nativeTraceback = value
else: raise TypeError
#====== USERS ======#
def fetchUserInfo(self):
"""
Fetches the infos of a user as a dictionary type.
**ATTENTION**: it returns a dictionary type value with the key *users*,
containing the user being fetched.
Right now it only fetches the user stored in the object, but can retrive a
list of users. This is not available now, will be implemented later.
"""
URL = self.URL+'/users/?format=json&game_id='+str(self.game_id)+'&'+'username='+str(self.username)
return self.setSignatureAndgetJSONResponse(URL)
def authenticateUser(self):
"""
Authenticate a user defined in the object variable.
The purpose of this method is to check if the user's credential
(name and token) are valid. Then, you're safe to call the other methods
Return a boolean type value.
"""
URL = self.URL+'/users/auth/?format=json&game_id='+str(self.game_id)+'&'+'username='+str(self.username)+\
'&'+'user_token='+str(self.user_token)
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
#====== TROPHIES ======#
def fetchTrophy(self, achieved=None, trophy=None):
"""
The 'trophy' argument receives a list of one or more ID of trophies to be
returned. It ignores the 'achieved' argument, so pass a 'None' value to it.
where you pass the desired number between the braces, separating each trophy
ID with commas.
If 'achieved' is:
> set to True, only the achieved trophies will be returned
> set to False, only trophies that the user hasn't achieved yet will be
returned
> set to None (no argument is passed), then all trophies will be retrieved
"""
URL = self.URL+'/trophies/?format=json&'+\
'game_id='+str(self.game_id)+'&'+'username='+str(self.username)+'&'+'user_token='+str(self.user_token)
if achieved != None:
URL += '&achieved='
if achieved == True: URL += 'true'
if achieved == False: URL += 'false'
else:
if trophy != None:
if type(trophy) == int:
URL += '&trophy_id='+str(trophy)+'&'
elif type(trophy) == list:
miniurl = '&trophy_id='
for t in trophy:
miniurl += str(t)+','
miniurl = miniurl[:1]
URL += miniurl
else:
raise 'Invalid type for trophy: must be int or list.'
return self.setSignatureAndgetJSONResponse(URL)
def addAchieved(self, trophy_id):
"""
Sets a winning trophy for the user.
If the parameters are valid, returns True. Otherwise, it returns False.
"""
URL = self.URL+'/trophies/add-achieved/?'+\
'game_id='+str(self.game_id)+'&'+'user_token='+str(self.user_token)+'&'+'username='+str(self.username)+\
'&'+'trophy_id='+str(trophy_id)
try:
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
except Exception as error:
return {'success': 'false', 'message': str(error)}
#====== SCORES ======#
def fetchScores(self, limit=10, table_id=None, user_info_only=False):
"""
The *limit* argument is set to 10 by default, but can't be more than 100. If
you pass a higher number, the method will automatically set to the maximum
size.
*table_id* if for returning scores for a specific table. If no arguments are
passed (None), it will return all the tables avaliable.
If *user_info_only* is set to True, only scores for the player stored on the
object will be returned.
"""
URL = self.URL+'/scores/?format=json&game_id='+str(self.game_id)
if user_info_only:
URL += '&username='+str(self.username)+'&user_token='+str(self.user_token)
# ID of the score table
if table_id:
URL += '&table_id='+str(table_id)
# Maximum number of scores should be 100 according with the GJAPI
if limit > 100:
limit = 100
URL += '&limit='+str(limit)
return self.setSignatureAndgetJSONResponse(URL)
def addScores(self, score, sort, table_id=None, extra_data='', guest=False, guestname=''):
"""
This method adds a score to the player or guest.
*score* is a string value describing the score value.
*sort* is the actual score value, a number value. But can be a string too.
For *table_id*, check the fetchScores method.
*extra_data* is a string value with any data you would like to store. It
doesn't appear on the site.
If you want to store a score for a guest instead of the user, you:
> set True to 'guest' parameter.
> set a string value with the name of the guest on 'guestname'
"""
URL = self.URL+'/scores/add/?format=json&game_id='+str(self.game_id)+\
'&score='+str(score)+'&sort='+str(sort)
if not guest:
URL += '&username='+str(self.username)+'&user_token='+str(self.user_token)
else:
URL += '&guest='+str(guestname)
if extra_data:
URL += '&extra_data='+extra_data
if table_id:
URL += '&table_id='+str(table_id)
return self.setSignatureAndgetJSONResponse(URL)
def scoreTable(self):
""" Returns the tables containing the high scores for the game."""
URL = self.URL+'/scores/tables/?format=json&game_id='+str(self.game_id)
return self.setSignatureAndgetJSONResponse(URL)
#====== SESSIONS ======#
def openSession(self):
"""
Opens a game session for a particular user. Allows you to tell Game Jolt
that a user is playing your game. You must ping the session
(**pingSession** method) to keep it active and you must close it when you're
done with it. Note that you can only have one open session at a time.
If you try to open a new session while one is running, the system will close
out your current session before opening a new one.
Return a boolean value: True if a session opened with sucess, False otherwise.
"""
URL = self.URL+'/sessions/open/?format=json&game_id='+str(self.game_id)+\
'&username='+str(self.username)+'&user_token='+str(self.user_token)
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
def closeSession(self):
"""
Closes the active section.
Return a boolean value: True if a session closed with sucess, False otherwise.
"""
URL = self.URL+'/sessions/close/?format=json&game_id='+str(self.game_id)+\
'&username='+str(self.username)+'&user_token='+str(self.user_token)
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
def pingSession(self, active=True):
"""
Pings an open session to tell the system that it's still active. If the
session hasn't been pinged within 120 seconds, the system will close the
session and you will have to open another one. It's recommended that you
ping every 30 seconds or so to keep the system from cleaning up your session.
You can also let the system know whether the player is in an "active" or
"idle" state within your game through this call. To do this, you pass an
argument to the *active* variable. If it is set to True, then the player
state will be set to **active**. If False, it will be set to **idle**.
Return a boolean value: True if a session pinged with sucess, False otherwise.
"""
URL = self.URL+'/sessions/ping/?format=json&game_id='+str(self.game_id)+\
'&username='+str(self.username)+'&user_token='+str(self.user_token)
if active: URL += '&status=active'
else: URL += '&status=idle'
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
#====== DATA STORAGE ==#
def fetchData(self, key, user_info_only=False):
"""
Return the data stored.
The *key* variable is the identification value for the particular data you
want to retrieve.
If you want to return data only for the user stored in the object, the last
argument is set to True.
Returns a dictionary containing the data.
"""
URL = self.URL+'/data-store/?format=json&game_id='+str(self.game_id)+\
'&key='+str(key)
if user_info_only:
URL += '&username='+str(self.username)+'&user_token='+str(self.user_token)
return self.setSignatureAndgetJSONResponse(URL)
def UpdateData(self, key, operation, value, user_info_only=False, return_data=False):
"""
Updates the data stored.
The *key* variable is the identification value for the particular data you
want to retrieve.
The *value* variable is the string or number to be operated.
The mathematic operations are add, subtract, multiply and divide. The string operations are append and prepend.
If you want to update data only for the user stored in the object, the last
argument is set to True.
Returns a boolean value or the resulting data: True if the data was updated with sucess, False
otherwise; if you want to know the actual value of the updated data "return_data" is set to true.
"""
URL = self.URL+'/data-store/update/?format=json&game_id='+str(self.game_id)+\
'&key='+str(key)+'&operation='+operation+'&value'+str(value)
if user_info_only:
URL += '&username='+str(self.username)+'&user_token='+str(self.user_token)
if not return_data:
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
if return_data:
return (self.setSignatureAndgetJSONResponse(URL)['data'])
def storeData(self, key, data, user_info_only=False):
"""
Set a data to be stored.
The *key* argument is to define the identifier of the data.
The *data* argument is the data itself, of string type.
If you wish to pass the data only for this stored in the object, the last
argument is set to True.
Return a boolean value: True if the data was stored with sucess, False
otherwise.
"""
URL = self.URL+'/data-store/set/?format=json&game_id='+str(self.game_id)+\
'&key='+str(key)+'&data='+str(data)
if user_info_only:
URL += '&username='+str(self.username)+'&user_token='+str(self.user_token)
return (self.setSignatureAndgetJSONResponse(URL)['success']) == 'true'
def removeData(self, key):
"""
Remove a data with the given key.
*key* is the data identification.
Return a boolean value: True if the data was removed with sucess, False
otherwise.
"""
URL = self.URL+'/data-store/remove/?format=json'+'&game_id='+str(self.game_id)+'&key='+str(key)
return self.setSignatureAndgetJSONResponse(URL) == 'true'
def getDataKeys(self):
"""
Return all the keys avaliable.
The return type is a dictionary object with a list of dictionaries. Each of
those dictionaries contains a key with the name **key** and contain it's value.
Exemple:
{'keys': [{'key': '1'}, {'key': '2'}, ...], 'success': 'true' }
"""
URL = self.URL+'/data-store/get-keys/?format=json'+'&game_id='+str(self.game_id)
return self.setSignatureAndgetJSONResponse(URL)