« Documentation de l'API Jeuxvideo.com » : différence entre les versions

De JVFlux
(Ajout des requêtes GET)
 
(83 versions intermédiaires par 9 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
{{CadreTravaux}}
L''''API de [[jeuxvideo.com]]''', utilisée à l'origine par les applications mobiles de jeuxvideo.com, permet de développer plus facilement des applications, sites Web, et autres scripts en rapport avec jeuxvideo.com.
L''''API de [[Jeuxvideo.com]]''', utilisée à l'origine par les applications mobiles de Jeuxvideo.com, permet de développer plus facilement des applications, sites web, et autres scripts en rapport avec Jeuxvideo.com.


Elle est au format JSON<ref>https://fr.wikipedia.org/wiki/JavaScript_Object_Notation</ref>, ce qui fait qu'elle est plus facile à parser<ref>[https://fr.wiktionary.org/wiki/parser#:~:text=Verbe,-parser%20%5Cpa%CA%81.se&text=(Anglicisme%20informatique)%20(Programmation),ou%20en%20extraire%20des%20%C3%A9l%C3%A9ments. https://fr.wiktionary.org/wiki/parser#:~:text=Verbe,-parser%20%5Cpa%CA%81.se&text=(Anglicisme%20informatique)%20(Programmation),ou%20en%20extraire%20des%20%C3%A9l%C3%A9ments.]</ref>, mais aussi plus rapide à charger qu'une page web normale.
Actuellement, deux API sont utilisées : l'api ''v4'' et l'api ''jvc.gg'' :
* L'API ''v4'' est hébergée sur le sous-domaine ''api'', et est actuellement à sa quatrième version (''v4''). Elle est utilisée par l'application mobile JVC. Cette API est privée et ne peut être documentée que par rétro-ingénierie de l'application mobile.
* L'API ''jvc.gg'', d'importance moindre, est hébergée à ''api.jvc.gg''. C'est une API utilisée par les pages Phoenix du site qui sert notamment à la recherche des jeux.


L'ancienne API était hébergée sur le sous-domaine "ws". Désormais, l'API est hébergée sur le sous-domaine "api", et est actuellement à sa quatrième version (v4).
Les anciennes versions sont toujours exploitables avec les ''tokens'' qui leur sont propres{{Commentaire|Autre=potentiellement obsolète}}, mais certains ''endpoints'' renvoient des erreurs HTTP 403.
 
=Rétro-ingénierie de l'application mobile JVC=
Pour connaître les endpoints de l'API, il faut décompiler l'[https://apkpure.fr/fr/jeuxvideo-com-pc-et-consoles/com.jeuxvideo application mobile JVC] ou intercepter les requêtes qu'elle envoie.
 
== Décompilation ==
=== Mise en place du processus de décompilation ===
Pour pouvoir décompiler l'application mobile, vous aurez besoin des outils suivants :
* [https://www.java.com/en/download/ Java 8 ou plus]
* [https://apktool.org/ apktool]
* [https://github.com/skylot/jadx JADX]
 
Il faut également le fichier ''.apk'' de l'application mobile, téléchargeable [https://apkpure.fr/fr/jeuxvideo-com-pc-et-consoles/com.jeuxvideo ici] (privilégiez les versions supérieures à la 5.4.3, sans quoi vous devrez extraire le fichier ''.xapk'' et appliquer le processus de décompilation sur le fichier ''.apk'' principal).
 
Il vaut mieux renommer le fichier ''.apk'' obtenu pour éviter d'avoir à être embarrassé par de trops longs noms de fichiers. Ici, nous l'avons enregistré sous ''jeuxvideo.apk''.
 
Suivez les étapes ci-dessous (en remplaçant ''jeuxvideo.apk'' par le nom du fichier ''.apk'' à votre disposition) :
# Dans un terminal ouvert au dossier de l'APK, exécutez la commande suivante :
$ apktool d jeuxvideo.apk jeuxvideo-decompiled
# Ouvrez JADX, activez l'option ''Show flatten packages'' (dans la barre d'outils), dans les préférences du logiciel veillez à avoir changé l'option ''Code comments level'' sur ''DEBUG''.
# Importez les fichiers ''.dex'' de ''jeuxvideo-decompiled'' dans JADX.
 
=== JvApiService ===
Une fois le code Java disponible, on peut ouvrir l'interface '''JvApiService''' (située dans ''com.jeuxvideo.api.web''), qui contient tous les ''endpoints''. L'application utilise le bibliothèque [https://square.github.io/retrofit/ Retrofit 2.x] pour gérer l'API, et la classe contient tous les ''endpoints'' formels de l'API.
 
''Extrait de l'interface JvApiService''
<pre>
    @POST("contents/{contentID}/comments")
    Call<UserComment> addComment(@Path("contentID") String str, @Body UserComment.Body body);
 
    @POST("contents/{contentID}/comments/{commentID}/vote")
    Call<UserComment> addCommentVote(@Path("contentID") String str, @Path("commentID") String str2, @Body UserComment.Vote vote);
</pre>
 
Interface complète disponible [https://pastebin.com/WiQiutAf ici].
 
=== SigningInterceptor ===
La classe '''SigningInterceptor''' (du package ''com.jeuxvideo.api.utils''), permet de déduire la valeur du ''header Jvc-Authorization'' à envoyer dans les requêtes à l'API.
<pre>
    public static String m27149f(String str, String str2, String str3, String str4, String str5) {
        StringBuilder sb2 = new StringBuilder();
        sb2.append("550c04bf5cb2b\n");
        sb2.append(str);
        sb2.append("\n");
        sb2.append(str2);
        sb2.append("\n");
        sb2.append(str3);
        sb2.append("\n");
        sb2.append(str4);
        sb2.append("\n");
        if (str5 == null) {
            str5 = "";
        }
        sb2.append(str5);
        String m14428b = CryptoUtils.m14428b("d84e9e5f191ea4ffc39c22d11c77dd6c", sb2.toString());
        return "PartnerKey=550c04bf5cb2b, Signature=" + m14428b + ", Timestamp=" + str;
    }
</pre>
 
Classe complète disponible [https://pastebin.com/sMpiYDqQ ici].
 
=== JVApi et UserAgentInterceptor ===
'''JVApi''' (du package arbitrairement nommé ''p379s3'' car nom perdu lors de la compilation) et '''UserAgentInterceptor''' (du package ''com.jeuxvideo.api.utils'') permettent de déduire le ''user-agent'' et la valeur des ''headers'' contenant des informations sur la version et la plateforme.
 
=== JVApiManager ===
La classe '''JVApiManager''' (du package arbitrairement nommé ''p379s3'' car nom perdu lors de la compilation) est celle qui envoie les requêtes à l'API. Elle permet de savoir quelles variables sont passées à la requête vers tel ''endpoint''.
 
Classe complète disponible [https://pastebin.com/hDWMZwbq ici].
 
<div id="jvc-auth-header"></div>
=== Le cas Jvc-Auth-Header ===
'''Jvc-Auth-Header''' est le ''header'' envoyé à l'''endpoint'' nommé ''accounts/register''. Cet ''endpoint'' est crucial en ce qu'il représente l''''unique manière de créer un compte JVC sans le système reCaptcha'''.
 
Il est actuellement impossible que la requête soit validée par l'API sans ce ''header''.
 
Il s'avère que ''Jvc-Auth-Header'' contient le [https://stackoverflow.com/questions/37671380/what-is-fcm-token-in-firebase token FCM], un token créé par la plateforme ''FireBase'' de Google et utilisé par l'application mobile pour accéder aux bases de données. Ce token est unique à chaque appareil et est créé à chaque installation de l'application mobile. En outre, il permet à l'API de savoir si l'application a déjà effectué une requête d'inscription dans les dernières 24 heures et, le cas échéant, de bloquer les requêtes d'inscription.
 
Il semble malheureusement impossible de générer de tels tokens « à la demande », en tout cas sans connaître les noms et clés des services FireBase dédiés à l'application.
 
<div id="interception"></div>
 
== Interception du trafic ==
 
Il est possible d'intercepter le trafic émis par l'application mobile lorsqu'elle est exécutée sur un appareil afin de connaître en détail les requêtes envoyées à l'API.
 
Cette méthode est indépendante de la précédente qui nécessitait de décompiler l'APK. Ici, vous aurez besoin du fichier APK original.
 
=== Mise en place du processus d'interception ===
Pour pouvoir intercepter le trafic de l'application mobile, vous aurez besoin des outils suivants :
# [https://developer.android.com/studio?hl=fr Android Studio]
# [https://httptoolkit.com/ HTTP Toolkit]
 
Il faut également le fichier ''.apk'' de l'application mobile, téléchargeable [https://apkpure.fr/fr/jeuxvideo-com-pc-et-consoles/com.jeuxvideo ici].
 
Pour commencer, il vous faut créer un appareil virtuel avec Android Studio : suivez pour cela le tutoriel disponible à [https://developer.android.com/studio/run/managing-avds?hl=fr cette adresse]. Pour que HTTP Toolkit puisse intercepter le trafic de l'application, il faut choisir un appareil ne disposant pas de ''Google Play''. Nous vous conseillons de choisir le téléphone Pixel 6 Pro et l'image système Tiramisu (niveau d'API 33, Android 13.0). Une fois l'appareil virtuel créé, lancez-le.
 
Une fois ceci fait, ouvrez le logiciel HTTP Toolkit puis dans le menu principal choisissez l'option ''Android Device via ADB'' qui devrait désormais être accessible. HTTP Toolkit va automatiquement installer tous les logiciels nécessaires sur l'appareil ; vous serez invités, sur l'appareil, à autoriser une connextion VPN à HTTP Toolkit : appuyez sur « OK ».
 
Ensuite, vous devrez utiliser ADB pour installer l'APK sur l'appareil virtuel. Premièrement, il vous faut ajouter ADB à la variable PATH de votre ordinateur ; consultez la section « How to use ADB from any directory on your PC or Mac » de [https://www.xda-developers.com/install-adb-windows-macos-linux/ cet article], qui contient les étapes à suivre sur Windows, MacOS et Linux.
 
Ouvrez un terminal de commande dans le dossier où se trouve l'APK et exécutez <code>adb install jeuxvideo.apk</code> (remplacez ''jeuxvideo.apk'' par le nom du fichier APK téléchargé). Une fois la commande exécutée, vous n'avez plus qu'à lancer l'application mobile et observer quelles requêtes sont envoyées dans le panel ''View'' de HTTP Toolkit.
 
Vous pouvez désormais voir quelles requêtes sont passées à l'API, ainsi que les ''headers'' et ''body'' associés. Par exemple lorsque l'on se connecte :
[[File:http_toolkit.png|center|500px]]


=API v4=
=API v4=
L'API v4 utilise un système de sécurité afin que même personne ne puisse accéder à l'API. Pour pouvoir accéder y accéder, l'''Header'' de vos requêtes devra toujours être de la forme :
Jvc-Authorization: "'''''<u>header</u>'''''"


"User-Agent": "JeuxVideo-Android/267"
== Préambule ==
L'API v4 utilise une sécurité, l'en-tête HTTP <code>Jvc-Authorization</code> , sa valeur dépend d'une clé fixe <code>partner_key</code>, d'une <code>signature</code>  et de la <code>date</code> .


"Content-Type" : "application/json"
Le ''header'' de vos requêtes devra toujours être de la forme :
'''''header''''' est égal à une chaîne de charactère de cette forme : ''"PartnerKey='''<u>partner_key</u>''', Signature='''<u>signature</u>''', Timestamp='''<u>date</u>'''".'' (Voir section rétro-ingénierie pour plus d'information)
<pre>
"Jvc-Authorization" : header,
"User-Agent" : "JeuxVideo-Android/338",
"Jvc-App-Platform" : "Android",
"Jvc-App-Version" : 338,
"Content-Type" : "application/json"
</pre>
Où ''header'' est égal à une chaîne de caractères de la forme ''"PartnerKey=partner_key, Signature=signature, Timestamp=timestamp".


====== Voici un script [https://www.python.org/ python] qui construit la signature ======
Veuillez consulter l'[[#annexe|annexe]] pour trouver un script Python permettant de créer ce ''header''.
import hashlib
 
from datetime import datetime
===Authentification===
Comme pour l'ancienne API, le cookie ''coniunctio'' est toujours le cookie de session utilisateur. Il doit être récupéré avec l'appel au point d'entrée ''accounts/login'' (voir tableau POST) puis il doit être envoyé dans une en-tête HTTP <code>"Cookie" : "coniunctio={coniunctio}"</code> pour chaque appel.
api_version = 4
 
partner_key = "550c04bf5cb2b"
<div id="sous-domaine"></div>
date = datetime.utcnow().isoformat()
== Comportement du sous-domaine ==
method = "POST"
Il est intéressant de remarquer que l'API se comporte souvent en véritable sous-domaine, de manière similaire au site www.jeuxvideo.com de base : par exemple, si vous êtes sur un [[topic]] ou un [[forum]], il vous suffit de remplacer dans l'URL « www » par « api » et vous vous trouverez sur une page aux informations identiques quoique présentées de manière différente. Ainsi, les URL seront de la forme <code>https://api.jeuxvideo.com/forums/0-51-0-1-0-1-0-blabla-18-25-ans.htm</code>.
 
signature = hashlib.sha256(f"{self.partner_key}\n{date}\n{method}\napi.jeuxvideo.com\n/v{self.api_version}/{path}".encode()).hexdigest()
[[File:forum_api.png|vignette|350px|Le forum [[Blabla 18-25 ans]] sur le site de l'API.]]
 
header = f"PartnerKey={partner_key}, Signature={signature}, Timestamp={date}"
L'API ne fonctionne donc pas que par ''endpoints''.
 
{{clear}}
 
==Endpoints « formels » ==
Il s'agit des ''endpoints'' « documentés » dans le code de l'application mobile et qui se comportent différemment des services de l'API qui ne se contentent que de « copier » le contenu JVC (voir sous-section précédente).


== API endpoints ==
'''URL de base''': ''<nowiki>https://api.jeuxvideo.com/v4/</nowiki>''  
Url de base : ''<nowiki>https://api.jeuxvideo.com/v4/</nowiki>''


* URL : endpoint
*'''PATH''': chemin d'accès de l'''endpoint''
* PARAMS : variable entre accolades à remplacer dans l'URL.  
*'''PARAMS, HEADERS''':
* Exemple : <code>contents/{contentID}/comments</code> doit être appelé comme suivant  <code>contents/someID1234/comments</code>
** '''param''' : variable entre accolades à remplacer dans l'URL, suivie dans la documentation de son type (''str'' ou ''int''). Par exemple, <code>contents/{contentID}/comments</code> doit être appelé comme suit : <code>contents/123456/comments</code>. <br> Note : dans la plupart des cas, le paramètre <code>accountId</code> peut être remplacé par la valeur <code>me</code> lorsque la requête doit pointer sur le compte connecté.
* BODY : corps de la requête, au format JSON donc "content": "commentaire" sera en réalité {
** '''header''' : ''header'' particulier à inclure dans l'en-tête de la requête. Si pas de spécification, l'en-tête ne contient que les ''headers'' de base spécifiés plus haut (''User-Agent'', ''Jvc-Authorization'', etc.).
* "content": "commentaire"
* }


{| class="wikitable mw-collapsible"
*'''QUERY''': par exemple pour les deux query ''page'', ''perPage'' int, l'URL devra être: <code>api.jeuxvideo.com/v4/contents/1234ID/comments?page=1&perPage=30</code>
|+''POST''
*'''BODY''': Corps de la requête au format JSON
Le fichier JSON contenant les <code>id</code> des ''params'' et des ''query strings'' <code>chronicles</code> <code>modes</code> <code>genres</code> <code>events</code> <code>machine</code> <code>categories</code> (obtenu via la requête ''GET config'') est disponible [https://pastebin.com/bnAu6tFL ici].
{| class="wikitable sortable"
|+POST
!NOM
!NOM
!URL
!PATH
!PARAMS
!PARAMS, HEADERS
!BODY
!BODY
|-
|-
|addComment
|addComment
|contents/{contentID}/comments
|contents/{contentID}/comments
|'''contentID''' str
|'''contentID''' int
|<pre>"content": "commentaire"</pre>
|<pre>"content": "commentaire"</pre>
|-
|-
|addCommentVote
|addCommentVote
|contents/{contentID}/comments/{commentID}/vote
|contents/{contentID}/comments/{commentID}/vote
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|<pre>"type": 1 ou -1</pre>
|<pre>"type": 1 ou -1</pre>
|-
|addFavorisGames
|accounts/{accountId}/favorites/games
|'''accountId''' str
|<pre>"games": [{
    "id": 1,</code>
    "machine": 100
]}
</pre>
|-
|-
|addReply
|addReply
|contents/{contentID}/comments/{commentID}/answers
|contents/{contentID}/comments/{commentID}/answers
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|<code>"content": "Super jeu !"</code>
|<pre>"content": "Super jeu !"</pre>
|-
|-
|addReview
|addReview
|games/{id}/{machine}/reviews/users
|games/{id}/{machine}/reviews/users  
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' int
|<pre>"content": "Super jeu !",
|<pre>"content": "Super jeu !",
"mark": 4,</code>
"mark": 4,
 
"onProfile": true
"onProfile": true
</pre>
</pre>
|-
|-
|login  
|login
|accounts/login
|accounts/login
|
|
|"alias": "pseudo",
|<pre>"alias": "pseudo",
 
"password": "mot_de_passe"</pre>
"password": "mot_de_passe"
|-
|-
|logout
|logout
Ligne 94 : Ligne 197 :
|
|
|-
|-
|register  
|register
|accounts/register  
|accounts/register  
''l'<nowiki/>'''Header''' doit inclure'' : <code>"Jvc-Auth-Token" : str</code>
| '''header''' : <code>"Jvc-Auth-Token" : str</code>
|
|<pre>"email": "email",
|<pre>"email": "email",
"alias": "pseudo",
"alias": "pseudo",
Ligne 104 : Ligne 206 :
</pre>
</pre>
|-
|-
|reinitPassword  
|reinitPassword
|accounts/reset
|accounts/reset
|
|
|<pre>"alias": "pseudo",
|<pre>"alias": "pseudo",
"password": "nouveau",</code>
"password": "nouveau",
 
"session": "",
"session": "",</code>
"captcha": {
 
    "imageKey": "",
"captcha": {</code>
    "imageName": "",
    "imageKey": "",</code>
    "imageValues": [
    "imageName": "",</code>
        "val1",
    "imageValues": [</code>
        "val2"
        "val1",</code>
    ]
        "val2" ]}
}
</pre>
</pre>
|-
|-
|reportAccount
|reportAccount
|accounts/{accountId}/report
|accounts/{accountId}/report
|'''accountId''' str
|'''accountId''' int
|<code>"reason": 1,</code>
|<pre>"reason": 1,
 
"message": "Raison"</pre>
<code>"message": "Raison"</code>
|-
|-
|reportComment
| reportComment
|contents/{contentID}/comments/{commentID}/report
|contents/{contentID}/comments/{commentID}/report
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|<pre>"reason": 1,
|<pre>"reason": 1,
"message": "Raison",
"message": "Raison",
Ligne 143 : Ligne 244 :
|games/{id}/{machine}/reviews/users/{reviewId}/report
|games/{id}/{machine}/reviews/users/{reviewId}/report
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' int
'''reviewId''' int
'''reviewId''' int
|<pre>"reason": 1,  
|<pre>"reason": 1,  
Ligne 155 : Ligne 256 :
|restoreComment
|restoreComment
|contents/{contentID}/comments/{commentID}
|contents/{contentID}/comments/{commentID}
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|"content" : "comment"
|<pre>"content" : "comment"</pre>
|-
|-
|validateAccount
|validateAccount
Ligne 174 : Ligne 275 :
"signed_data": "signed_data"
"signed_data": "signed_data"
</pre>
</pre>
|-
|addFavoritesForums
|accounts/me/favorites/forums
|
|<code>"forums" : [50,51,52] # ID des forums</code>
|-
|addFavoritesGames
|accounts/me/favorites/games
|
|<code>"games" : [{"id":"ID du jeu", "machine":"ID de la machine"}]</code>
|-
|addFavoritesTopics
|accounts/me/favorites/topics
|
|<code>"topics" : [74229153, 74229154] # ID des forums</code>
|}
|}
{| class="wikitable mw-collapsible"
Ci-dessous figurent les requêtes ''GET'', qui se comportent comme des recherches selon les filtres passés en ''queries''.
Les ''queries'' de type <code>str</code> (chaînes de caractère) représentent des ID répertoriées dans la réponse à la requête <code>general/config</code> (sous forme de fichier JSON, disponible [https://pastebin.com/bnAu6tFL ici]).
Si l'on souhaite filtrer les résultats selon leur nature (''news'', vidéos, ''previews'', etc.), on dispose du dictionnaire suivant (provenant de l'application mobile) qui à chaque nature de document associe l'ID des catégories associées :
<pre>
{'videos': '6,13,14,15,16,17,145,19,20',
'news': '50',
'previews': '55',
'tests': '56,57',
'all': '6,13,14,15,16,17,145,50,19,20,53,55,56,57'}
</pre>
Pour plus de détails, veuillez consulter le fichier JSON de configuration.
 
Comment vous pouvez le constater, contrairement aux variables de type <code>int</code>, les <code>str</code> peuvent contenir plusieurs valeurs, séparées par des virgules : dans ce cas, elles devront être ''parsées'' selon l'encodage URL (voir l'[[#Annexe|annexe]] où un script Python réalisant cette tâche est mis à disposition).
 
Les ''queries page'' et ''perPage'', toujours des entiers, représentent respectivement : le numéro de la page de recherche et le nombre d'objets à afficher par page.
Les ID comme ''accountID'' et ''commentID'' sont des entiers. Il est à noter que dans la plupart des cas, ''accountID'' peut être remplacé par la chaîne de caractères ''me'' qui représente l'utilisateur connecté (connu grâce au cookie ''coniunctio'' passé en en-tête de la requête).
 
{| class="wikitable"
|+GET
|+GET
!NOM
!NOM
!URL
!PATH
!PARAMS
!PARAMS, HEADERS
!QUERY
!QUERY  
!HEADER
|-
|-
|config
|config
Ligne 187 : Ligne 319 :
|
|
|
|
|'''If-None-Match''' str
|-
|-
|getAccount
|getAccount
|accounts/{accountId}
|accounts/{accountId}
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
|getAllFavorisGames
|getAllFavorisGames
|accounts/{accountId}/favorites/games/all
|accounts/{accountId}/favorites/games/all
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
Ligne 204 : Ligne 333 :
|contents/{id}
|contents/{id}
|'''id''' int
|'''id''' int
|
|
|
|-
|getArticleList
|''rétro-ingénierie à faire''
|
|'''page''' int
'''perPage''' int
'''types''' str
|'''If-None-Match''' str
|-
|-
|getCaptcha
|getCaptcha
Ligne 219 : Ligne 339 :
|
|
|'''nb''' int
|'''nb''' int
|
|-
|-
|getChroniclesSummary
|getChroniclesSummary
Ligne 225 : Ligne 344 :
|
|
|'''machines''' str
|'''machines''' str
|
|-
|-
|getComment
|getComment
|contents/{contentID}/comments/{commentID}
|contents/{contentID}/comments/{commentID}
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|
|
|
|-
|-
|getCommentAnswers
|getCommentAnswers
|contents/{contentID}/comments/{commentID}/answers
|contents/{contentID}/comments/{commentID}/answers
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|
|
|
|-
|-
|getComments
|getComments
|contents/{contentID}/comments
|contents/{contentID}/comments
|'''contentID''' str
|'''contentID''' int
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getContentBean
|getContentBean
|contents/{id}
|contents/{id}
|'''id''' int
|'''id''' int
|
|
|
|-
|-
|getContentList
|getContentList
|''rétro-ingénierie à faire''
|contents
|
|
|'''categories''' str
|'''categories''' str
Ligne 263 : Ligne 377 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getCurrentAccountReport
|getCurrentAccountReport
|accounts/{accountId}/report
|accounts/{accountId}/report
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
|getCurrentCommentReport
|getCurrentCommentReport
|contents/{contentID}/comments/{commentID}/report
|contents/{contentID}/comments/{commentID}/report
|'''contentID''' str
|'''contentID''' int
'''commentID''' str
'''commentID''' int
|
|
|
|-
|-
Ligne 283 : Ligne 394 :
'''machine''' str
'''machine''' str
'''reviewId''' int
'''reviewId''' int
|
|
|
|-
|-
|getFavoris
|getFavorites
|accounts/{accountId}/favorites
|accounts/{accountId}/favorites
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
|getFavorisForum
|getFavoritesForum
|accounts/{accountId}/favorites/forums
|accounts/{accountId}/favorites/forums
|'''accountId''' str
|'''accountId''' int
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getFavorisGames
|getFavoritesGames
|accounts/{accountId}/favorites/games
|accounts/{accountId}/favorites/games  
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
|getFavorisTopics
|getFavoritesTopics
|accounts/{accountId}/favorites/topics
|accounts/{accountId}/favorites/topics
|'''accountId''' str
|'''accountId''' int
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getFolder
|getFolder
|contents/{id}
|contents/{id}
|'''id''' int
|'''id''' int  
|
|
|
|-
|-
Ligne 322 : Ligne 427 :
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|
|
|
|-
|-
|getGameDetails
| getGameDetails
|games/{id}/{machine}/details
|games/{id}/{machine}/details
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|
|
|
|-
|-
Ligne 338 : Ligne 441 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getGameList
|getGameList
Ligne 349 : Ligne 451 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getGameNews
|getGameNews
Ligne 356 : Ligne 457 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getGameReleaseList
|getGameReleaseList
|games/releases
| games/releases
|
|
|'''month''' str
| '''month''' str
'''year''' str
'''year''' str
'''machines''' str
'''machines''' str
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getGameReview
|getGameReview
Ligne 373 : Ligne 472 :
'''machine''' str
'''machine''' str
'''review''' str
'''review''' str
|
|
|
|-
|-
Ligne 379 : Ligne 477 :
|games/{id}/any/reviews
|games/{id}/any/reviews
|'''id''' int
|'''id''' int
|
|
|
|-
|-
Ligne 386 : Ligne 483 :
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|
|
|
|-
|-
|getGameSummary
|getGameSummary  
|contents/games
|contents/games
|
|
|'''machines''' str
|'''machines''' str
|
|-
|-
|getGameUserReviews
|getGameUserReviews
Ligne 401 : Ligne 496 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getGameVideos
|getGameVideos
|games/{id}/{machine}/videos
|games/{id}/{machine}/videos  
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getGameWikis
|getGameWikis
Ligne 417 : Ligne 510 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getHeadlineList
|getHeadlineList
|contents/trending
| contents/trending
|
|
|'''machines''' str
|'''machines''' str
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getHighTechSummary
| getHighTechSummary
|contents/hightech
|contents/hightech
|
|
|'''machines''' str
|'''machines''' str
|
|-
|-
|getLightGame
|getLightGame
Ligne 437 : Ligne 527 :
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|
|
|
|-
|-
Ligne 443 : Ligne 532 :
|contents/{id}
|contents/{id}
|'''id''' int
|'''id''' int
|
|
|
|-
|-
Ligne 451 : Ligne 539 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getPageContents
|getPageContents  
|accounts/{accountId}/page/contents
|accounts/{accountId}/page/contents
|'''accountId''' str
|'''accountId''' int
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getPageReviews
|getPageReviews
|accounts/{accountId}/page/reviews
|accounts/{accountId}/page/reviews
|'''accountId''' str
|'''accountId''' int
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getProfile
|getProfile
|accounts/{accountId}/profile
|accounts/{accountId}/profile
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
|getProfilePage
|getProfilePage
|accounts/{accountId}/page
|accounts/{accountId}/page
|'''accountId''' str
|'''accountId''' int
|
|
|
|-
|-
Ligne 484 : Ligne 567 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getRelatedVideos
|getRelatedVideos
Ligne 491 : Ligne 573 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|getRelatedWikis
|getRelatedWikis
Ligne 498 : Ligne 579 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|getSpecificNews
|''rétro-ingénierie à faire''
|
|
|
|-
|-
|getStores
|getStores
Ligne 510 : Ligne 584 :
|'''id''' int
|'''id''' int
'''machine''' str
'''machine''' str
|
|
|
|-
|-
Ligne 518 : Ligne 591 :
|'''page''' int
|'''page''' int
'''perPage''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getTopWikis
|getTopWikis
Ligne 524 : Ligne 596 :
|
|
|
|
|'''If-None-Match''' str
|-
|-
|getTopsComments
|getTopsComments
|contents/{contentID}/comments/tops
|contents/{contentID}/comments/tops
|'''contentID''' str
|'''contentID''' int
|
|
|
|-
|-
Ligne 536 : Ligne 606 :
|'''id''' int
|'''id''' int
|
|
|
|-
|getVideoList
|''rétro-ingénierie à faire''
|
|'''categories''' str
'''chronicles''' str
'''machines''' str
'''types''' str
'''page''' int
'''perPage''' int
|'''If-None-Match''' str
|-
|-
|getVideosSummary
|getVideosSummary
|contents/videos
|contents/videos
|
|
|'''machines''' str
|'''machines''' str  
|
|-
|-
|search
|search
|''rétro-ingénierie à faire''
|search
|
|
|'''q''' str
|'''q''' str
|
|-
|-
|searchArticles
|searchArticles
Ligne 567 : Ligne 623 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|searchAutocomplete
|searchAutocomplete
Ligne 573 : Ligne 628 :
|
|
|'''q''' str
|'''q''' str
|
|-
|-
|searchGames
|searchGames
Ligne 581 : Ligne 635 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|searchNews
|searchNews
Ligne 589 : Ligne 642 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|searchVideos
|searchVideos
Ligne 597 : Ligne 649 :
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|searchWikis
|searchWikis
|search/wikis
| search/wikis
|
|
|'''q''' str
|'''q''' str
'''page''' int
'''page''' int
'''perPage''' int
'''perPage''' int
|
|-
|-
|sponso
|sponso
Ligne 611 : Ligne 661 :
|
|
|
|
|'''If-None-Match''' str
|}
|}
{| class="wikitable sortable"
|+PUT
!NOM
!PATH
!PARAMS, HEADERS
!BODY
|-
|saveDescription
|accounts/me/profile/description
|
|<pre>"description": "Ma description"</pre>
|-
|saveExcludedMachines
|accounts/me/profile/excluded-machines
|
|<pre>"machines" : [1, 2, 3, 4]  // id des machines</pre>
|-
|saveMachines
|accounts/me/profile/machines
|
|<pre>"machines" : [1, 2, 3, 4]  // id des machines</pre>
|-
|updateComment
|contents/{contentID}/comments/{commentID}
|'''contentID''' int
'''commentID''' int
|<pre>"content": "Super jeu !"</pre>
|-
|uploadAvatar
|accounts/me/avatar
| '''header''' : <code>"Content-Type": "application/octet-stream"</code> <br>
|'''body''' : objet <code>bytes</code> (conversion du fichier image en octets, pas de clé associée)
|-
|uploadCover
|accounts/me/cover
| '''header''' : <code>"Content-Type": "application/octet-stream"</code> <br>
|'''body''' : objet <code>bytes</code> (conversion du fichier image en octets, pas de clé associée)
|}
{| class="wikitable sortable"
|+DELETE
!NOM
!PATH
!PARAMS, HEADERS
!BODY
|-
|deleteComment
|contents/{contentID}/comments/{commentID}
|'''contentID''' int
'''commentID''' int
|
|-
|deleteCommentVote
|contents/{contentID}/comments/{commentID}/vote
|'''contentID''' int
'''commentID''' int
|
|-
|deleteFavorisForum
|accounts/me/favorites/forums
|
|<code>"forums" : [50,51,52] # ID des forums</code>
|-
| deleteFavorisGames
|accounts/me/favorites/games
|
|<code>"forums" : [{"id":"ID du jeu", "machine":"ID de la machine"}]</code>
|-
|deleteFavorisTopics
|accounts/me/favorites/topics
|
|<code>"topics" : [74229153, 74229154] # ID des topics</code>
|}
<div id="annexe"></div>
== Annexe ==
=== Jvc-Authorization ===
Voici un script Python qui permet de construire le ''header'' ''Jvc-Authorization'' :
<pre>
import hashlib, hmac, datetime, urllib.parse
PARTNER_KEY = '550c04bf5cb2b'
HMAC_SECRET = b'd84e9e5f191ea4ffc39c22d11c77dd6c'
DOMAIN = 'api.jeuxvideo.com'
API_VERSION = 4
def auth_header(path: str, method: str = 'GET', query: dict = None) -> str:
    # Conversion de path en URL avec urllib.parse.urlparse
    date = datetime.datetime.now().isoformat()
    parsed_url = urllib.parse.urlparse(f"https://{DOMAIN}/v{API_VERSION}/{path}")
    if query:
        parsed_url = parsed_url._replace(query=urllib.parse.urlencode(query))
       
    # Création de la chaîne à hash
    string_to_hash = [PARTNER_KEY, date, method, parsed_url.netloc, parsed_url.path]
    if parsed_url.query:
        string_to_hash.append(parsed_url.query)
    else:
        string_to_hash[-1] += '\n'
    string_to_hash = '\n'.join(string_to_hash)
   
    # Hash de la chaîne puis renvoi sous forme de Jvc-Authorization
    signature = hmac.new(HMAC_SECRET, msg=string_to_hash.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    return f"PartnerKey={PARTNER_KEY}, Signature={signature}, Timestamp={date}"
</pre>
=== Requêtes à l'API ===
Voici un script Python permettant d'envoyer des requêtes à l'API et qui utilise la fonction précédente :
<pre>
import requests, json
from requests import Response
DOMAIN = 'api.jeuxvideo.com'
API_VERSION = 4
def call(path: str, method: str = 'GET', query: dict = None, data: dict = None, cookies: dict = None) -> Response:
    url = f"https://{DOMAIN}/v{API_VERSION}/{path}"
    jv_auth = auth_header(path, method=method, query=query)
    headers = {
        "Jvc-Authorization":jv_auth,
        "Content-Type": "application/json",
        "jvc-app-platform":"Android",
        "jvc-app-version":"338",
        "user-agent":"JeuxVideo-Android/338",
        "host":"api.jeuxvideo.com"
    }
    if data:
        data = json.dumps(data)
    return requests.request(url=url, method=method, data=data, headers=headers, params=query, cookies=cookies)
</pre>
=== ''Parsing'' des ''queries'' ===
Lors de l'appel à ''contents'' par exemple, vous pouvez passer en ''query'' des chaînes de caractère comme ''categories'' ou ''types''. Ces ''queries'', s'il contiennent plusieurs valeurs, doivent être encodés au format d'encodage URL, ce qui peut être fait en Python :
<pre>
from urllib.parse import quote
categories = "50,53,56" # la recherche récupérera les articles de catégories 50, 53 et 56.
categories_parsed = quote(categories)
query = {"categories": categories_parsed}
# vous pouvez désormais passer ce dictionnaire en query
</pre>
== Accès aux forums ==
Étrangement, il n'existe pas d'''endpoint'' particulier pour envoyer un message ou un topic sur les [[forum|forums]] de jeuxvideo.com.
Lorsque l'application mobile souhaite récupérer les pages des forums, elle envoie une requête au lien usuel de la ressource à ceci près que ''www'' est remplacé par ''api''. La requête renvoie alors une page HTML en guise de réponse, voir la [[#sous-domaine|rubrique en question]].
Ainsi, lorsque l'on cherche à interagir avec les forums, il n'y a pas d'avantage à envoyer des requêtes à l'API plutôt qu'au site de base en l'état.
=== Principe général ===
En premier lieu, afin d'envoyer un message ou un topic sur un forum, il faut bien évidemment que vous soyez connecté et vus devrez donc passer en en-tête de la requête votre cookie ''coniunctio'', récupérable à partir d'une requête à l'''endpoint'' <code>accounts/login</code> envoyant vos identifiants.
[[File:js_form.png|vignette|400px|Les ''inputs'' cachés contenant les valeurs à envoyer dans les requêtes visibles depuis les outils de développeur.]]
En second lieu, vous ne devrez pas seulement envoyer aux serveurs le contenu de votre message (et le titre du topic si besoin) : la requête ''POST'' devra aussi contenir des paires clés-valeurs générées par le site lui-même et qui agissent comme une sorte de protection. Celles-ci sont : ''fs_session'', ''fs_timestamp'', ''fs_version'', ''form_alias_rang'' ainsi qu'un ''hash''.
Heureusement, ces valeurs sont directement disponibles depuis des ''inputs'' cachées dans le code HTML des pages de forums/topics et situées dans la ''div'' nommée ''js-form-session-data''.
Si l'on veut envoyer un message sur un topic (ou un forum), il faudra d'abord envoyer une requête ''GET'' à l'URL associée et récupérer ces valeurs chiffrées avant d'envoyer une requête ''POST'' avec le contenu du message en plus de ces dernières.
=== Script Python ===
La classe Python suivante permet d'interagir avec les forums de cette manière. Il vous suffit d'entrer la valeur de votre cookie ''coniunctio'' et d'appeler les fonctions souhaitées avec le contenu des messages ou topics.
Bien que cette méthode permette d'outrepasser les captchas, il se peut que vous soyez bloqué par le site en envoyant un trop grand nombre de requêtes dans un trop court laps de temps.
<pre>
import requests, json
from requests import Response
from bs4 import BeautifulSoup
DIV_CLASS = 'js-form-session-data'
class ForumClient:
    def __init__(self, coniuntio: str):
        self._cookies = {'coniunctio':coniuntio}
    def _scrape_input_tokens(self, res: Response) -> dict:
        soup = BeautifulSoup(res.text, 'html.parser')
        form = soup.find('div', {'class':DIV_CLASS})
        jsi = form.findChildren("input")
       
        tokens = {
            "fs_session":jsi[0].attrs["value"],
            "fs_timestamp":jsi[1].attrs["value"],
            "fs_version":jsi[2].attrs["value"],
            jsi[3].attrs["name"]:jsi[3].attrs["value"],
            "form_alias_rang":"1",
            "g-recaptcha-response":""
        }
       
        return tokens
    def _get_forum_URL(self, forum_id) -> str:
        return requests.get(f'https://www.jeuxvideo.com/forums/0-{forum_id}-0-1-0-1-0-a.htm').url
    def _get_topic_URL(self, topic_id) -> str:
        return requests.get(f'https://www.jeuxvideo.com/forums/42-1-{topic_id}-1-0-1-0-a.htm').url
    def post_message(self, topic_id: int, message: str) -> Response:
        """
        topic_id, int : ID du topic
        message, str : votre message
        renvoie un objet Response
        """
        topic_URL = self._get_topic_URL(topic_id)
        res = requests.get(topic_URL, cookies=self._cookies)
        data = self._scrape_input_tokens(res)
        data['message_topic'] = message
        res = requests.post(topic_URL, data=data, cookies=self._cookies)
        return res
    def post_topic(self, forum_id: int, titre: str, message: str, sondage: dict = None) -> Response:
        """
        forum_id, int : ID du forum
        titre, str : titre du topic
        message, str : corps du topic
        [sondage], dict : dictionnaire de la forme {
            "question_sondage" : "Question ?",
            "reponse_sondage[]" : ["reponse 1", "reponse 2"]
        }
        """
        forum_URL = self._get_forum_URL(forum_id)
        res = requests.get(forum_URL, cookies=self._cookies)
        data = self._scrape_input_tokens(res)
        data['titre_topic'] = titre
        data['message_topic'] = message
        if sondage:
            data |= sondage
            data['submit_sondage'] = '1'
        res = requests.post(forum_URL, data=data, cookies=self._cookies)
        return res
if __name__ == '__main__':
    client = ForumClient("votre coniunctio ici")
    # Exemples d'utilisation
    # Pour poster un message
    client.post_message(74204156, "Je poste depuis Python :)")
    # Pour poster un topic
    client.post_topic(51, 'Je poste depuis Python', 'Et toi ? :)')
</pre>


==Classe PHP exploitant l'API==
== Limitations ==
L'API v4 comporte plusieurs limitations :
* Il est impossible d'outrepasser les protections [[captcha|captchas]] requises par le site notamment pour [[DDB|signaler]] un message.
* Il est impossible de s'inscrire en utilisation l'''endpoint'' <code>accounts/register</code> car celui-ci nécessite un ''header'' contenant le ''token'' FireBase généré lors de l'installation de l'application (voir le [[#jvc-auth-header|paragraphe à ce sujet]]). Une solution serait de créer un grand nombre de machines virtuelles (ou d'en automatiser la création), et pour chacune d'entre elles d'intercepter le trafic de l'application (voir [[#interception|section associée]]) pour récupérer et conserver les ''tokens'' dans un fichier. Cette méthode, longue et fastidieuse, ne permettrait donc pas de générer un nombre infini de comptes. Une autre méthode possible, bien qu'elle n'utilise pas l'API, serait de créer un compte sur le site à l'aide d'un ''webdriver''. Celle-ci fonctionne (l'auteur de ce paragraphe l'a déjà implémentée) mais il serait difficile de la rendre compatible pour toutes les plateformes (du fait de la nécessité d'avoir un VPN gérable depuis un script).
* Il est particulièrement hardu de faire du ''web scraping'' sur les pages de forum puisque l'API renvoie une page HTML et non un fichier JSON dans ce cas.


-> https://pastebin.com/LWNDQDKy
==Ressources==
*[https://archive.org/details/jeuxvideo.com-pc-et-consoles-5.4.7-apkpure APK 5.4.7] décompilable avec JADX.
*[https://pastebin.com/LWNDQDKy Classe PHP] exploitant l'API (cette classe est incomplète et sera possiblement rendue obsolète dans le temps).


Cette classe est incomplète et sera possiblement rendue obsolète dans le temps.
= API jvc.gg =
[[File:phoenix.png|300px|vignette|La page Phoenix des jeux.]]


=Ancienne API =
Cette API est une API distincte de la ''v4'' et utilisée par la partie Phoenix du site, c'est-à-dire les pages listant les jeux, ''reviews'', vidéos, etc., comme [https://www.jeuxvideo.com/tous-les-jeux/ celle-ci]. Elle est hébergée sur le domaine [https://api.jvc.gg api.jvc.gg].


Le contenu qui va suivre concerne l'ancienne API, qui n'existe plus. Si quelqu'un est courageux pour de nouveau documenter l'API actuelle de JV, nous en serions tous ravis.
{{clear}}


==Identification==
== Rétro-ingénierie ==
L'API étant privée, il a fallu procéder par rétro-ingénierie pour la documenter.
 
Les appels à l'API sont effectués depuis un script chargé par les pages Phoenix. Les noms de variable sont obfusqués mais pas les chaînes de caractère : ainsi une partie des ''endpoints'' y sont lisibles. Ce script est téléchargeable [https://archive.org/details/phoenix_script ici].
 
Il est également possible d'observer les URL et les contenus des requêtes depuis le panneau ''Network'' (ou ''Réseau'') des [https://developer.mozilla.org/fr/docs/Learn/Common_questions/Tools_and_setup/What_are_browser_developer_tools outils de développement Web].
 
== Préambule ==
L'API jvc.gg est basée sur l'utilisation de ''JSON Web Token'' (JWT), un standard utilisé pour créer des jetons de sécurité qui permettent de transférer de l'information de manière sécurisée entre deux parties au format JSON.
 
Lorsque vous visitez pour la première fois une page Phoenix, le serveur de JVC renvoie une requête comportant un cookie ''accessToken''. La valeur de ce cookie est une chaîne de caractères encodée au format JWT qui contient notamment votre ID de compte et la valeur de votre cookie ''coniunctio''. La fonction Python suivante permet de récupérer le cookie ''accessToken'' en fonction de votre cookie ''coniunctio'' :
<pre>
def get_access_cookie(coniunctio: str) -> str:
  cookies = {'coniunctio':coniunctio}
  res = requests.get('https://www.jeuxvideo.com/tous-les-jeux/', cookies=cookies)
  access_cookie = res.headers['set-cookie'].split(';')[0].replace('accessToken=', '')
  return access_cookie
</pre>
 
Ce cookie vous sera utile lorsque les requêtes envoyées sont destinées à opérer sur votre compte.
 
== Endpoints ==
Le tableau suivant répertorie les ''endpoints'' connus de cette API. Voici la signification de ses colonnes :
 
*'''PATH''' : chemin de l'''endpoint''.
*'''PARAMS, HEADERS''' :
** '''param''' : variable entre accolades à remplacer dans l'URL, suivie dans la documentation de son type (''str'' ou ''int''). Par exemple, <code>contents/{contentID}/comments</code> doit être appelé comme suit : <code>contents/123456/comments</code>.
** '''header''' : ''header'' particulier à inclure dans l'en-tête de la requête. Si pas de spécification, l'en-tête ne contient que les ''headers'' de base spécifiés plus haut (''User-Agent'', ''Jvc-Authorization'', etc.).
 
*'''QUERY''' : valeur à ajouter à l'URL permettant si précisée de filtrer les résultats. Par exemple pour les deux query ''page'', ''perPage'' int, l'URL devra être: <code>api.jeuxvideo.com/v4/contents/1234ID/comments?page=1&perPage=30</code>
*'''BODY''' : Corps de la requête au format JSON
 
=== Les paramètres spéciaux ===
* Le paramètre <code>{accessToken}</code> devra toujours être remplacé par la valeur du cookie éponyme.
* Le paramètre <code>{gameCategory}</code> peut être remplacée par les valeurs suivantes, qui proviennent évidemment du système de classification de JVC :
** <code>all</code> pour avoir la liste de tous les jeux ;
** <code>awaited</code> pour avoir la liste des jeux attendus ;
** <code>popular</code> pour avoir la liste des jeux populaires ;
** <code>best</code> pour avoir la liste des meilleurs jeux ;
** <code>currentBest</code> pour avoir la liste des meilleurs jeux du moment ;
** <code>releases</code> pour avoir la liste des jeux pas encore sortis.
 
=== Queries des jeux ===
Les ''queries'' renseignent la plateforme, le genre et le mode du jeu. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/1LGEibT8 ici].
 
Par exemple, si vous souhaitez obtenir la liste des jeux PS4 de genre action RPG et de forme multi en ligne, votre chaîne de ''queries'' devra être <code>platform=ps4&gameGenre=action_rpg&gameMode=multiplayer_online</code>.
 
De plus, le ''query'' ''release'' désigne la date de sortie du jeu, '''compatible seulement avec les catégories <code>best</code> et <code>releases</code>'''. Il peut s'agir d'une année ou d'une décennie (dans ce cas, la valeur du ''query'' représentera la première année de la décennie suivie du caractère 'd' ; par exemple, <code>2010d</code> pour les années 2010). Pour les jeux pas encore sortis, ce ''query'' peut également contenir le mois (par exemple : <code>2024-07</code> pour juillet 2024). Incompatible avec la catégorie <code>currentBest</code>.
 
=== Queries des news ===
Les ''queries'' renseignent la plateforme, le type, le genre et l'événement des news. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/Dk4SkDkA ici].
 
Par exemple, si vous souhaitez obtenir la liste des news de jeux PS5 de genre aventure en rapport avec l'événement ''Game Awards'' publiés en 2022, votre chaîne de ''queries'' devra être <code>publicationType=game_news&platform=ps5&gameGenre=adventure&event=id201395&publicationPeriod=2022</code>.
 
Le ''query publicationPeriod'' peut être : une année, une année suivie du numéro du mois (<code>2024-07</code> pour juillet 2024) ou une décennie (<code>2010d</code> pour les années 2010).
 
=== Queries des tests ===
Les ''queries'' renseignent la plateforme, le mode et le genre des jeux testés, ainsi que l'encadrement de la note des rédacteurs et la date de publication. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/Dk4SkDkA ici].
 
Par exemple, si vous souhaitez obtenir la liste des tests de jeux PC de genre aventure, de mode solo ayant eu au moins 14 et datant d'il y a au plus 6 mois, votre chaîne de ''queries'' devra être
<code>platform=pc&gameGenre=adventure&gameMode=singleplayer&editorialRatingRange=%5B14%2C)&maxAge=P6M</code>
 
Notez que le ''query editorialRatingRange'' doit être encodé au format URL avant d'être envoyé.
 
=== Queries des previews ===
Les ''queries'' renseignent la plateforme, le genre et l'événement des jeux testés. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/xaqTQPfr ici].
 
Par exemple, si vous souhaitez obtenir la liste des ''previews'' des jeux PS5 de genre FPS en rapport avec l'événement ''Find Your Next Game'', votre chaîne de ''queries'' devra être <code>platform=ps5&gameGenre=fps&event=fyng</code>.
 
=== Queries des vidéos ===
Les ''queries'' renseignent le type des vidéos ainsi que la plateforme, le genre et l'événement des jeux traités. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/nBUAP4XX ici].
 
Par exemple, si vous souhaitez obtenir la liste des vidéos ''gameplays'' des jeux PC de genre Action en rapport avec l'événement E3, votre chaîne de ''queries'' devra être <code>publicationType=gameplay&platform=pc&gameGenre=action&event=e3</code>.
 
=== Queries des dossiers ===
Les ''queries'' renseignent la plateforme, le genre et l'événement des jeux traités. La liste complète des valeurs des ''queries'' possibles pour ces attributs est disponible au format JSON [https://pastebin.com/UGd3b2qJ ici].
 
Par exemple, si vous souhaitez obtenir la liste des vidéos ''gameplays'' des jeux PC de genre Action en rapport avec l'événement E3, votre chaîne de ''queries'' devra être <code>publicationType=gameplay&platform=pc&gameGenre=action&event=e3</code>.
 
=== Queries globaux ===
Ce sont les ''queries'' ''offset'' et ''limit'', de type ''int'', qui représentent respectivement l'indice de l'item dans la liste à partir duquel les items sont listés (par défaut 0) et le nombre d'items à lister (par défaut 100). Ils fonctionnent avec toutes les recherches.
 
=== Requêtes auxiliaires ===
En parallèle des requêtes envoyées par le site pour rechercher des jeux, news, previews, vidéos ou tests, des requêtes auxiliaires sont adressées aux serveurs. Elles permettent en particulier de connaître ce qu'il faut afficher sur la page, les URL des pages ainsi que les options (''queries'') restantes selon ce qui a déjà été entré dans le panel de recherche.
 
Ces requêtes utilisent un ''endpoint'' contenant un token JWT dans lequel sont stockées des informations générales sur la requête (''queries'' notamment). Comme cette clé secrète n'est pas connue, il nous est malheureusement impossible de générer de tels tokens à la demande.
 
=== Liste des endpoints connus ===
 
{| class="wikitable"
|+GET
!PATH
!PARAMS
!QUERY
!DESCRIPTION
|-
|games/{gameCategory}/popularity.desc
| '''gameCategory''' str
|'''platform''' str <br>
'''gameGenre''' str <br>
'''gameMode''' str <br>
'''offset''' int <br>
'''limit''' int <br>
'''release''' str
|Renvoie la liste des jeux de la catégorie <code>{gameCategory}</code> triés par popularité décroissante et satisfaisant les ''queries''.
|-
|games/{gameCategory}/editorialRating.desc
| '''gameCategory''' str
|'''platform''' str <br>
'''gameGenre''' str <br>
'''gameMode''' str <br>
'''offset''' int <br>
'''limit''' int <br>
'''release''' str
|Renvoie la liste des jeux de la catégorie <code>{gameCategory}</code> triés par popularité décroissante et satisfaisant les ''queries''.
|-
|games/{gameCategory}/releaseDate.asc
| '''gameCategory''' str
|'''platform''' str <br>
'''gameGenre''' str <br>
'''gameMode''' str <br>
'''offset''' int <br>
'''limit''' int <br>
'''release''' str
|Renvoie la liste des jeux de la catégorie <code>{gameCategory}</code> triés par popularité décroissante et satisfaisant les ''queries''.
|-
|games/{gameCategory}/title.asc
| '''gameCategory''' str
|'''platform''' str <br>
'''gameGenre''' str <br>
'''gameMode''' str <br>
'''offset''' int <br>
'''limit''' int <br>
'''release''' str
|Renvoie la liste des jeux de la catégorie <code>{gameCategory}</code> triés par popularité décroissante et satisfaisant les ''queries''.
|-
|news/datePublished.desc
|
| '''publicationType''' str <br>
'''platform''' str <br>
'''gameGenre''' str <br>
'''event''' str <br>
'''publicationPeriod''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des actualités triées par date de publication croissante et satisfaisant les ''queries''.
|-
|reviews/datePublished.desc
|
|
'''platform''' str<br>
'''gameGenre''' str<br>
'''gameMode''' str<br>
'''editorialRatingRange''' str<br>
'''maxAge''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des tests triés par date de publication croissante et satisfaisant les ''queries''.
|-
|reviews/editorialRating.desc
|
|
'''platform''' str<br>
'''gameGenre''' str<br>
'''gameMode''' str<br>
'''editorialRatingRange''' str<br>
'''maxAge''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des tests triés par note décroissante des rédacteurs JVC et satisfaisant les ''queries''.
|-
|reviews/gameTitle.asc
|
|
'''platform''' str<br>
'''gameGenre''' str<br>
'''gameMode''' str<br>
'''editorialRatingRange''' str<br>
'''maxAge''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des tests triés par nom croissant du jeu et satisfaisant les ''queries''.
|-
|previews/datePublished.desc
|
|'''platform''' str <br>
'''gameGenre''' str <br>
'''event''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des annonces triées par date de publication croissante et satisfaisant les ''queries''.
|-
|videos/datePublished.desc
|
|'''publicationType''' str <br>
'''platform''' str <br>
'''gameGenre''' str <br>
'''event''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des vidéos triées par date de publication croissante et satisfaisant les ''queries''.
|-
|topics/datePublished.desc
|
|'''platform''' str <br>
'''gameGenre''' str <br>
'''event''' str <br>
'''offset''' int <br>
'''limit''' int
|Renvoie la liste des dossiers triés par date de publication croissante et satisfaisant les ''queries''.
|-
|me/{accessToken}/privateMessages
| '''accessToken''' str
|
|Renvoie la liste des 25 derniers messages privés.
|-
|me/{accessToken}/notifications/subscription
| '''accessToken''' str
|
|Renvoie la liste des dernières notifications.
|-
|me/{accessToken}/notifications/privateMessage/unseenCount
| '''accessToken''' str
|
|Renvoie le nombre de messages privés non-vus.
|-
|me/{accessToken}/notifications/subscription/unseenCount
| '''accessToken''' str
|
|Renvoie le nombre de notifications non-vues.
|-
|me/{accessToken}/profile
| '''accessToken''' str
|
|Informations du profil.
|-
|config/menu
|
|
|Informations à afficher sur les pages Phoenix.
|}
 
{| class="wikitable"
|+POST
!PATH
!PARAMS
!BODY
!DESCRIPTION
|-
|me/{accessToken}/notifications/subscription/resetUnseenCount
| '''accessToken''' str
|
|Met à zéro le nombre de notifications non-vues.
|-
|me/{accessToken}/notifications/privateMessage/resetUnseenCount
| '''accessToken''' str
|
|Met à zéro le nombre de messages privés non-vus.
|-
|me/{accessToken}/notifications/subscription/resetUnreadCount
| '''accessToken''' str
|
|Met à zéro le nombre de notifications non-lues.
|-
|me/{accessToken}/notifications/privateMessage/resetUnreadCount
| '''accessToken''' str
|
|Met à zéro le nombre de messages privés non-lus.
|-
|me/{accessToken}/notification/{notifId}/setRead
| '''accessToken''' str <br>
'''notifId''' int
|
|Lit une notification.
|-
|me/{accessToken}/privateMessage/{messageId}/setRead
| '''accessToken''' str <br>
'''messageId''' int
|
|Lit un message privé.
|-
|_auth/{accessToken}/logout
| '''accessToken''' str
|
|Se déconnecter.
|}
 
=Ancienne API (dépréciée) =
{{Bannière Note|Contenu=Le contenu qui va suivre concerne l'ancienne API qui n'existe plus.}}
==Identification ==
L'API utilise le nom de domaine [https://ws.jeuxvideo.com/ ws.jeuxvideo.com]. Pour y accéder, il faut utiliser un des identifiants ci-dessous (authentification HTTP basique).
L'API utilise le nom de domaine [https://ws.jeuxvideo.com/ ws.jeuxvideo.com]. Pour y accéder, il faut utiliser un des identifiants ci-dessous (authentification HTTP basique).


Ligne 634 : Ligne 1 232 :
! scope="col" |Mot de passe
! scope="col" |Mot de passe
|-
|-
! scope="row" | Android 1.0
! scope="row" |Android 1.0
|appandr
|appandr  
|e32!cdf
|e32!cdf
|-
|-
! scope="row" |Android 2.0.3
! scope="row" |Android 2.0.3
| app_and_gnw
|app_and_gnw
| FC?4554?
|FC?4554?
|-
|-
! scope="row" | Android 2.5
! scope="row" |Android 2.5
|app_and_ms
|app_and_ms  
| D9!mVR4c
|D9!mVR4c
|-
|-
! scope="row" |Android MP
! scope="row" |Android MP
Ligne 650 : Ligne 1 248 :
|LXnb45=d#
|LXnb45=d#
|-
|-
! scope="row" | Android Tab
! scope="row" |Android Tab
|nex12sz
|nex12sz
| GT4!V2cT
|GT4!V2cT
|-
|-
! scope="row" |iPhone
! scope="row" |iPhone
Ligne 658 : Ligne 1 256 :
|W!P45-R
|W!P45-R
|-
|-
! scope="row" |iPad
! scope="row" |iPad  
|ip45de
|ip45de
|XpD5!FT
|XpD5!FT
Ligne 665 : Ligne 1 263 :
L'API est accessible en HTTP et en HTTPS, préférez la version HTTPS !
L'API est accessible en HTTP et en HTTPS, préférez la version HTTPS !


==Utilisation==
==Utilisation ==
===Connexion ===
===Connexion===
Pour vous connecter, utilisez la page [https://ws.jeuxvideo.com/mon_compte/connexion.php mon_compte/connexion.php].
Pour vous connecter, utilisez la page [https://ws.jeuxvideo.com/mon_compte/connexion.php mon_compte/connexion.php].


Ligne 677 : Ligne 1 275 :
|Le pseudo de l'utilisateur.
|Le pseudo de l'utilisateur.
|-
|-
|stamp
| stamp
|Le timestamp (le nombre de secondes depuis le 1er janvier 1970) à l'heure où la requête est envoyée.
|Le timestamp (le nombre de secondes depuis le 1er janvier 1970) à l'heure où la requête est envoyée.
|-
|-
Ligne 686 : Ligne 1 284 :
Vous renconterez potentiellement une erreur vous demandant de remplir un captcha, avec un lien vers l'image, et une balise ''params_form'', qu'il faudra ajouter aux paramètres de la première requête, ainsi que le paramètre ''code'' avec la valeur du captcha.
Vous renconterez potentiellement une erreur vous demandant de remplir un captcha, avec un lien vers l'image, et une balise ''params_form'', qu'il faudra ajouter aux paramètres de la première requête, ainsi que le paramètre ''code'' avec la valeur du captcha.


'''Important : n'ouvrez pas le lien du captcha dans votre navigateur si vous êtes connecté à votre compte JVC ; un captcha différent est renvoyé selon la présence du cookie ''coniunctio'' (qui ne doit PAS être présent pour afficher un captcha de l'API, sinon vous aurez systématiquement une erreur de captcha invalide).'''
'''Important : n'ouvrez pas le lien du captcha dans votre navigateur si vous êtes connecté à votre compte JVC ; un captcha différent est renvoyé selon la présence du cookie ''wenvjgol'' (qui ne doit PAS être présent pour afficher un captcha de l'API, sinon vous aurez systématiquement une erreur de captcha invalide).'''


En réponse, vous recevez plusieurs informations présentes dans la CDV, mais surtout le cookie ''coniunctio'', que vous devrez utiliser pour poster sur les forums et utiliser les messages privés.
En réponse, vous recevez plusieurs informations présentes dans la CDV, mais surtout le cookie ''wenvjgol'', que vous devrez utiliser pour poster sur les forums et utiliser les messages privés.


Pour information, ''coniunctio'' signifie ''logjvnew'' à l'envers, et c'est le cookie de session de JVC dont le nom a subi plusieurs transformations au fil des années.
Pour information, ''wenvjgol'' signifie ''logjvnew'' à l'envers, et c'est le cookie de session de JVC dont le nom a subi plusieurs transformations au fil des années.


Pour le renvoi de mot de passe, utilisez cette URL (en remplaçant ''Cisla'' par le pseudo ou bien l'adresse e-mail) :
Pour le renvoi de mot de passe, utilisez cette URL (en remplaçant ''Cisla'' par le pseudo ou bien l'adresse e-mail) :
Ligne 720 : Ligne 1 318 :
|-
|-
|02.news/''1234''.xml<br>02.news_screen/''1234''.xml
|02.news/''1234''.xml<br>02.news_screen/''1234''.xml
| Voir une news (le nombre correspond à l'id)
|Voir une news (le nombre correspond à l'id)
|-
|-
|ean.php?ean=''0045496830144''
|ean.php?ean=''0045496830144''
|Voir le jeu associé au code-barre (EAN) 0045496830144
|Voir le jeu associé au code-barre (EAN) 0045496830144
|-
|-
|forums_index.xml
| forums_index.xml
|Liste des forums généraux
|Liste des forums généraux
|-
|-
|search_n/''mario''<br>search/''mario''<br>search_sug/''mario''<br>search_forums/''mario''<br>search_forums_sug/''mario''
|search_n/''mario''<br>search/''mario''<br>search_sug/''mario''<br>search_forums/''mario''<br>search_forums_sug/''mario''
| Effectuer une recherche dans le nom des jeux ou des forums
|Effectuer une recherche dans le nom des jeux ou des forums
|-
|-
|tab_suggest_blocs.xml<br>tab_suggest_forums.xml
|tab_suggest_blocs.xml<br>tab_suggest_forums.xml
|''À compléter''
| ''À compléter''
|-
|-
|cgi-bin/liste.cgi
| cgi-bin/liste.cgi
|''À compléter''
|''À compléter''
|}
|}
Ligne 740 : Ligne 1 338 :
===Forums===
===Forums===
Pour les forums, les URL sont les mêmes que pour JVC, à part que :
Pour les forums, les URL sont les mêmes que pour JVC, à part que :
*Le .htm est transformé en .xml
* Le .htm est transformé en .xml
*Le www.jeuxvideo.com est remplacé par un ws.jeuxvideo.com
* Le www.jeuxvideo.com est remplacé par un ws.jeuxvideo.com
* La chaîne de caractères à la fin de l'URL (comme ''blabla-15-18-ans'' ou ''nom-du-topic'') est remplacée par un 0 (sauf pour la recherche).
*La chaîne de caractères à la fin de l'URL (comme ''blabla-15-18-ans'' ou ''nom-du-topic'') est remplacée par un 0 (sauf pour la recherche).


Par exemple :<br>
Par exemple :<br>
Ligne 759 : Ligne 1 357 :
|-
|-
|https://ws.jeuxvideo.com/forums/0-50-0-1-0-1-2-cactus.xml
|https://ws.jeuxvideo.com/forums/0-50-0-1-0-1-2-cactus.xml
| Rechercher « cactus » dans le titre des topics
|Rechercher « cactus » dans le titre des topics
|-
|-
|https://ws.jeuxvideo.com/forums/1-50-1-1-0-1-0-0.xml
|https://ws.jeuxvideo.com/forums/1-50-1-1-0-1-0-0.xml
| Un topic
|Un topic
|-
|-
|https://ws.jeuxvideo.com/forums/3-50-0-1-0-1-0-0.xml
|https://ws.jeuxvideo.com/forums/3-50-0-1-0-1-0-0.xml
Ligne 770 : Ligne 1 368 :
|10 derniers messages d'un topic + formulaire
|10 derniers messages d'un topic + formulaire
|-
|-
|https://ws.jeuxvideo.com/forums/5-50-128244545-1-0-1-0-0.xml
|https://ws.jeuxvideo.com/forums/5-50-128244545-1-0-1-0-0.xml  
| Formulaire de réponse à un topic
| Formulaire de réponse à un topic
|-
|-
|https://ws.jeuxvideo.com/cgi-bin/jvforums/forums.cgi
|https://ws.jeuxvideo.com/cgi-bin/jvforums/forums.cgi
|Envoyer un message (avec les données POST)
|Envoyer un message (avec les données POST)  
|-
|-
|https://ws.jeuxvideo.com/profil/cisla.xml
|https://ws.jeuxvideo.com/profil/cisla.xml  
|Voir une CDV
| Voir une CDV  
|}
|}


Ligne 786 : Ligne 1 384 :
Le même formulaire peut aussi bien être envoyé au ''forums.cgi'' de ws.jeuxvideo.com qu'à celui de www.jeuxvideo.com, ce qui vous permet de choisir entre apparaître « via mobile » ou non. Les données POST à envoyer sont donc les mêmes sur toutes les versions de JVC.
Le même formulaire peut aussi bien être envoyé au ''forums.cgi'' de ws.jeuxvideo.com qu'à celui de www.jeuxvideo.com, ce qui vous permet de choisir entre apparaître « via mobile » ou non. Les données POST à envoyer sont donc les mêmes sur toutes les versions de JVC.


===Messages privés===
=== Messages privés===
L'id de l'utilisateur mentionnée ci-dessous correspond à la partie du cookie ''coniunctio'' qui se trouve avant le premier "$".
L'id de l'utilisateur mentionnée ci-dessous correspond à la partie du cookie ''coniunctio'' qui se trouve avant le premier "$".


Ligne 792 : Ligne 1 390 :


{| class="wikitable alternance centre"
{| class="wikitable alternance centre"
! scope="col" |URL
! scope="col" |URL  
! scope="col" |Description
! scope="col" |Description
|-
|-
Ligne 798 : Ligne 1 396 :
|Dernière version de l'application, liste des consoles et des smileys, lien vers la charte, pub activée ou non
|Dernière version de l'application, liste des consoles et des smileys, lien vers la charte, pub activée ou non
|-
|-
|messages-prives/connexion_ws.php
| messages-prives/connexion_ws.php  
|Les paramètres sont les mêmes que pour la connexion depuis ''mon_compte/connexion.php'', décrite [[#Connexion|plus haut]]. Cependant, la réponse XML contient quelques informations supplémentaires à propos des MP.
|Les paramètres sont les mêmes que pour la connexion depuis ''mon_compte/connexion.php'', décrite [[#Connexion|plus haut]]. Cependant, la réponse XML contient quelques informations supplémentaires à propos des MP.
|-
|-
|messages-prives/boite-reception_ws.php
|messages-prives/boite-reception_ws.php
|Permet de connaître le contenu de la boîte de réception. Paramètre à envoyer :
|Permet de connaître le contenu de la boîte de réception. Paramètre à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur  
|-
|-
|messages-prives/envoyes_ws.php
|messages-prives/envoyes_ws.php
|Permet de connaître la liste des messages envoyés. Paramètre à envoyer :
|Permet de connaître la liste des messages envoyés. Paramètre à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
|-
|-
|messages-prives/message_ws.php
|messages-prives/message_ws.php  
|Permet de lire le contenu d'un message privé. Paramètres à envoyer :
| Permet de lire le contenu d'un message privé. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''id_discussion'' : id de la discussion
*''id_discussion'' : id de la discussion
* ''nb_clic'' : le nombre de fois où l'utilisateur a cliqué sur « Voir les messages précédents »
*''nb_clic'' : le nombre de fois où l'utilisateur a cliqué sur « Voir les messages précédents »
*''last_position_message'' : Si ce paramètre vaut 0, les 5 derniers messages sont affichés. Sinon, les 10 messages les plus anciens en partant du ''x''<sup>ème</sup> sont affichés, la numérotation débutant à partir de 0.
*''last_position_message'' : Si ce paramètre vaut 0, les 5 derniers messages sont affichés. Sinon, les 10 messages les plus anciens en partant du ''x''<sup>ème</sup> sont affichés, la numérotation débutant à partir de 0.
|-
|-
|messages-prives/nouveau_ws.php
| messages-prives/nouveau_ws.php
|Permet d'envoyer un nouveau message privé. Paramètres à envoyer :
|Permet d'envoyer un nouveau message privé. Paramètres à envoyer :  
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''all_dest'' : les destinataires, séparés pas des point-virgules
*''all_dest'' : les destinataires, séparés pas des point-virgules
*''sujet'' : le titre du MP
*''sujet'' : le titre du MP
*''yournewmessage'' : le contenu du MP
*''yournewmessage'' : le contenu du MP
*''tmp'' : le timestamp actuel
*''tmp'' : le timestamp actuel
* ''control'' : md5("k_ys4+a]x" + idUtilisateur + "j6A=d?6-W*-" + timestamp + "z_.Im")
*''control'' : md5("k_ys4+a]x" + idUtilisateur + "j6A=d?6-W*-" + timestamp + "z_.Im")
Il se peut également que vous ayez à gérer un code de confirmation.
Il se peut également que vous ayez à gérer un code de confirmation.
|-
|-
|messages-prives/repondre_ws.php
|messages-prives/repondre_ws.php
|Permet de répondre à un message privé. Paramètres à envoyer :
|Permet de répondre à un message privé. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''id_discussion'' : id de la discussion
*''id_discussion'' : id de la discussion
*''yournewmessage'' : le contenu du message
*''yournewmessage'' : le contenu du message
Ligne 838 : Ligne 1 436 :
|messages-prives/suggest_pseudo_ws.php
|messages-prives/suggest_pseudo_ws.php
|Liste quelques pseudos qui commencent par ce que l'utilisateur est en train de taper. Paramètres à envoyer :
|Liste quelques pseudos qui commencent par ce que l'utilisateur est en train de taper. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''search'' : ce que l'utilisateur est en train de taper
*''search'' : ce que l'utilisateur est en train de taper
|-
|-
Ligne 856 : Ligne 1 454 :
|messages-prives/del_indesirable_ws.php
|messages-prives/del_indesirable_ws.php
|Permet d'enlever un utilisateur des indésirables. Paramètres à envoyer :
|Permet d'enlever un utilisateur des indésirables. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur actuel
*''id_user'' : id de l'utilisateur actuel
*''id_user_indesirable'' : id de l'utilisateur à enlever des indésirables
* ''id_user_indesirable'' : id de l'utilisateur à enlever des indésirables  
|-
|-
|messages-prives/info_alerte_ws.php
|messages-prives/info_alerte_ws.php
|Permet de connaître les informations qui permettront de faire une DDB ou une mise en indésirable sur un message. Paramètres à envoyer :
|Permet de connaître les informations qui permettront de faire une DDB ou une mise en indésirable sur un message. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur qui fait la DDB
*''id_user'' : id de l'utilisateur qui fait la DDB
*''id_message'' : id du message concerné
* ''id_message'' : id du message concerné  
|-
|-
|messages-prives/alerte_ws.php
|messages-prives/alerte_ws.php
|Permet d'effectuer une DDB (ce qui est inutile, étant donné qu'elles ne sont jamais traitées). Certaines données nécessaires pour former la requête doivent être récupérés avec info_alerte_ws.php. Paramètres à envoyer :
| Permet d'effectuer une DDB (ce qui est inutile, étant donné qu'elles ne sont jamais traitées). Certaines données nécessaires pour former la requête doivent être récupérés avec info_alerte_ws.php. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur qui fait la DDB
*''id_user'' : id de l'utilisateur qui fait la DDB
*''pseudo'' : pseudo de l'utilisateur
*''pseudo'' : pseudo de l'utilisateur
*''motif'' : le motif, par exemple ''Piratage'' (les mêmes motifs que dans les MP sur JVC normal)
*''motif'' : le motif, par exemple ''Piratage'' (les mêmes motifs que dans les MP sur JVC normal)
Ligne 875 : Ligne 1 473 :
|-
|-
|messages-prives/connexion_valid_ws.php
|messages-prives/connexion_valid_ws.php
|Permet d'afficher le nombre de messages non-lus ainsi que l'URL de l'avatar. Paramètres à envoyer :
|Permet d'afficher le nombre de messages non-lus ainsi que l'URL de l'avatar. Paramètres à envoyer :  
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''pseudo'' : pseudo de l'utilisateur
*''pseudo'' : pseudo de l'utilisateur
|-
|-
|messages-prives/add_destinataire_ws.php
|messages-prives/add_destinataire_ws.php
|Permet d'ajouter un destinataire à un message privé. Paramètres à envoyer :
|Permet d'ajouter un destinataire à un message privé. Paramètres à envoyer :
* ''id_user'' : id de l'utilisateur
*''id_user'' : id de l'utilisateur
*''id_discussion'' : id de la discussion
*''id_discussion'' : id de la discussion
*''tab_pseudo'' : liste des destinataires à ajouter, séparés par un point-virgule
*''tab_pseudo'' : liste des destinataires à ajouter, séparés par un point-virgule
Ligne 892 : Ligne 1 490 :
|}
|}


==Reverse-engineering==
== Ancienne rétro-ingéniérie ==
===Décompilation===
===Décompilation===
Cette section concerne la décompilation de l'application Android de JVC sous Linux.
Cette section concerne la décompilation de l'application Android de JVC sous Linux.
Ligne 935 : Ligne 1 533 :
  print repr(string[:-ord(string[-1])])
  print repr(string[:-ord(string[-1])])


Modifiez la clé selon l'application que vous décompilez:
Modifiez la clé selon l'application que vous décompilez:  


*Pour l'application Jeuxvideo.com (version >= 2.0.3), c'est ''package com.jeuxvideo.activity''
*Pour l'application Jeuxvideo.com (version >= 2.0.3), c'est ''package com.jeuxvideo.activity''
Ligne 944 : Ligne 1 542 :


==Liens externes==
==Liens externes==
*[https://fr.wikipedia.org/wiki/Interface_de_programmation API sur Wikipédia]  
*[https://fr.wikipedia.org/wiki/Interface_de_programmation API sur Wikipédia]
*[http://www.jeuxvideo.com/forums/1-1000021-1691093-1-0-1-0-documentation-de-l-api-de-jeuxvideo-com.htm Lien du topic sur JVC pour poser vos questions]
*[http://www.jeuxvideo.com/forums/1-1000021-1691093-1-0-1-0-documentation-de-l-api-de-jeuxvideo-com.htm Lien du topic sur JVC pour poser vos questions]
*[https://play.google.com/store/apps/details?id=com.jeuxvideo&hl=fr L'application Android]
*[https://play.google.com/store/apps/details?id=com.jeuxvideo&hl=fr L'application Android]
Ligne 969 : Ligne 1 567 :
{{TableauCatégorieFonctionnementJVC}}
{{TableauCatégorieFonctionnementJVC}}


[[Catégorie:Fonctionnement de jeuxvideo.com]]
[[Catégorie:Fonctionnement technique de Jeuxvideo.com]]
[[Catégorie:Extensions et outils]]
[[Catégorie:Extensions et outils]]
{{TableauCatégories}}

Dernière version du 19 mai 2024 à 21:23

L'API de jeuxvideo.com, utilisée à l'origine par les applications mobiles de jeuxvideo.com, permet de développer plus facilement des applications, sites Web, et autres scripts en rapport avec jeuxvideo.com.

Actuellement, deux API sont utilisées : l'api v4 et l'api jvc.gg :

  • L'API v4 est hébergée sur le sous-domaine api, et est actuellement à sa quatrième version (v4). Elle est utilisée par l'application mobile JVC. Cette API est privée et ne peut être documentée que par rétro-ingénierie de l'application mobile.
  • L'API jvc.gg, d'importance moindre, est hébergée à api.jvc.gg. C'est une API utilisée par les pages Phoenix du site qui sert notamment à la recherche des jeux.

Les anciennes versions sont toujours exploitables avec les tokens qui leur sont propressommaire puces.png potentiellement obsolète , mais certains endpoints renvoient des erreurs HTTP 403.

Rétro-ingénierie de l'application mobile JVC[modifier | modifier le wikicode]

Pour connaître les endpoints de l'API, il faut décompiler l'application mobile JVC ou intercepter les requêtes qu'elle envoie.

Décompilation[modifier | modifier le wikicode]

Mise en place du processus de décompilation[modifier | modifier le wikicode]

Pour pouvoir décompiler l'application mobile, vous aurez besoin des outils suivants :

Il faut également le fichier .apk de l'application mobile, téléchargeable ici (privilégiez les versions supérieures à la 5.4.3, sans quoi vous devrez extraire le fichier .xapk et appliquer le processus de décompilation sur le fichier .apk principal).

Il vaut mieux renommer le fichier .apk obtenu pour éviter d'avoir à être embarrassé par de trops longs noms de fichiers. Ici, nous l'avons enregistré sous jeuxvideo.apk.

Suivez les étapes ci-dessous (en remplaçant jeuxvideo.apk par le nom du fichier .apk à votre disposition) :

  1. Dans un terminal ouvert au dossier de l'APK, exécutez la commande suivante :
$ apktool d jeuxvideo.apk jeuxvideo-decompiled
  1. Ouvrez JADX, activez l'option Show flatten packages (dans la barre d'outils), dans les préférences du logiciel veillez à avoir changé l'option Code comments level sur DEBUG.
  2. Importez les fichiers .dex de jeuxvideo-decompiled dans JADX.

JvApiService[modifier | modifier le wikicode]

Une fois le code Java disponible, on peut ouvrir l'interface JvApiService (située dans com.jeuxvideo.api.web), qui contient tous les endpoints. L'application utilise le bibliothèque Retrofit 2.x pour gérer l'API, et la classe contient tous les endpoints formels de l'API.

Extrait de l'interface JvApiService

    @POST("contents/{contentID}/comments")
    Call<UserComment> addComment(@Path("contentID") String str, @Body UserComment.Body body);

    @POST("contents/{contentID}/comments/{commentID}/vote")
    Call<UserComment> addCommentVote(@Path("contentID") String str, @Path("commentID") String str2, @Body UserComment.Vote vote);

Interface complète disponible ici.

SigningInterceptor[modifier | modifier le wikicode]

La classe SigningInterceptor (du package com.jeuxvideo.api.utils), permet de déduire la valeur du header Jvc-Authorization à envoyer dans les requêtes à l'API.

    public static String m27149f(String str, String str2, String str3, String str4, String str5) {
        StringBuilder sb2 = new StringBuilder();
        sb2.append("550c04bf5cb2b\n");
        sb2.append(str);
        sb2.append("\n");
        sb2.append(str2);
        sb2.append("\n");
        sb2.append(str3);
        sb2.append("\n");
        sb2.append(str4);
        sb2.append("\n");
        if (str5 == null) {
            str5 = "";
        }
        sb2.append(str5);
        String m14428b = CryptoUtils.m14428b("d84e9e5f191ea4ffc39c22d11c77dd6c", sb2.toString());
        return "PartnerKey=550c04bf5cb2b, Signature=" + m14428b + ", Timestamp=" + str;
    }

Classe complète disponible ici.

JVApi et UserAgentInterceptor[modifier | modifier le wikicode]

JVApi (du package arbitrairement nommé p379s3 car nom perdu lors de la compilation) et UserAgentInterceptor (du package com.jeuxvideo.api.utils) permettent de déduire le user-agent et la valeur des headers contenant des informations sur la version et la plateforme.

JVApiManager[modifier | modifier le wikicode]

La classe JVApiManager (du package arbitrairement nommé p379s3 car nom perdu lors de la compilation) est celle qui envoie les requêtes à l'API. Elle permet de savoir quelles variables sont passées à la requête vers tel endpoint.

Classe complète disponible ici.

Le cas Jvc-Auth-Header[modifier | modifier le wikicode]

Jvc-Auth-Header est le header envoyé à l'endpoint nommé accounts/register. Cet endpoint est crucial en ce qu'il représente l'unique manière de créer un compte JVC sans le système reCaptcha.

Il est actuellement impossible que la requête soit validée par l'API sans ce header.

Il s'avère que Jvc-Auth-Header contient le token FCM, un token créé par la plateforme FireBase de Google et utilisé par l'application mobile pour accéder aux bases de données. Ce token est unique à chaque appareil et est créé à chaque installation de l'application mobile. En outre, il permet à l'API de savoir si l'application a déjà effectué une requête d'inscription dans les dernières 24 heures et, le cas échéant, de bloquer les requêtes d'inscription.

Il semble malheureusement impossible de générer de tels tokens « à la demande », en tout cas sans connaître les noms et clés des services FireBase dédiés à l'application.

Interception du trafic[modifier | modifier le wikicode]

Il est possible d'intercepter le trafic émis par l'application mobile lorsqu'elle est exécutée sur un appareil afin de connaître en détail les requêtes envoyées à l'API.

Cette méthode est indépendante de la précédente qui nécessitait de décompiler l'APK. Ici, vous aurez besoin du fichier APK original.

Mise en place du processus d'interception[modifier | modifier le wikicode]

Pour pouvoir intercepter le trafic de l'application mobile, vous aurez besoin des outils suivants :

  1. Android Studio
  2. HTTP Toolkit

Il faut également le fichier .apk de l'application mobile, téléchargeable ici.

Pour commencer, il vous faut créer un appareil virtuel avec Android Studio : suivez pour cela le tutoriel disponible à cette adresse. Pour que HTTP Toolkit puisse intercepter le trafic de l'application, il faut choisir un appareil ne disposant pas de Google Play. Nous vous conseillons de choisir le téléphone Pixel 6 Pro et l'image système Tiramisu (niveau d'API 33, Android 13.0). Une fois l'appareil virtuel créé, lancez-le.

Une fois ceci fait, ouvrez le logiciel HTTP Toolkit puis dans le menu principal choisissez l'option Android Device via ADB qui devrait désormais être accessible. HTTP Toolkit va automatiquement installer tous les logiciels nécessaires sur l'appareil ; vous serez invités, sur l'appareil, à autoriser une connextion VPN à HTTP Toolkit : appuyez sur « OK ».

Ensuite, vous devrez utiliser ADB pour installer l'APK sur l'appareil virtuel. Premièrement, il vous faut ajouter ADB à la variable PATH de votre ordinateur ; consultez la section « How to use ADB from any directory on your PC or Mac » de cet article, qui contient les étapes à suivre sur Windows, MacOS et Linux.

Ouvrez un terminal de commande dans le dossier où se trouve l'APK et exécutez adb install jeuxvideo.apk (remplacez jeuxvideo.apk par le nom du fichier APK téléchargé). Une fois la commande exécutée, vous n'avez plus qu'à lancer l'application mobile et observer quelles requêtes sont envoyées dans le panel View de HTTP Toolkit.

Vous pouvez désormais voir quelles requêtes sont passées à l'API, ainsi que les headers et body associés. Par exemple lorsque l'on se connecte :

http toolkit.png

API v4[modifier | modifier le wikicode]

Préambule[modifier | modifier le wikicode]

L'API v4 utilise une sécurité, l'en-tête HTTP Jvc-Authorization , sa valeur dépend d'une clé fixe partner_key, d'une signature et de la date .

Le header de vos requêtes devra toujours être de la forme :

"Jvc-Authorization" : header,
"User-Agent" : "JeuxVideo-Android/338",
"Jvc-App-Platform" : "Android",
"Jvc-App-Version" : 338,
"Content-Type" : "application/json"

header est égal à une chaîne de caractères de la forme "PartnerKey=partner_key, Signature=signature, Timestamp=timestamp".

Veuillez consulter l'annexe pour trouver un script Python permettant de créer ce header.

Authentification[modifier | modifier le wikicode]

Comme pour l'ancienne API, le cookie coniunctio est toujours le cookie de session utilisateur. Il doit être récupéré avec l'appel au point d'entrée accounts/login (voir tableau POST) puis il doit être envoyé dans une en-tête HTTP "Cookie" : "coniunctio={coniunctio}" pour chaque appel.

Comportement du sous-domaine[modifier | modifier le wikicode]

Il est intéressant de remarquer que l'API se comporte souvent en véritable sous-domaine, de manière similaire au site www.jeuxvideo.com de base : par exemple, si vous êtes sur un topic ou un forum, il vous suffit de remplacer dans l'URL « www » par « api » et vous vous trouverez sur une page aux informations identiques quoique présentées de manière différente. Ainsi, les URL seront de la forme https://api.jeuxvideo.com/forums/0-51-0-1-0-1-0-blabla-18-25-ans.htm.

Le forum Blabla 18-25 ans sur le site de l'API.

L'API ne fonctionne donc pas que par endpoints.



Endpoints « formels »[modifier | modifier le wikicode]

Il s'agit des endpoints « documentés » dans le code de l'application mobile et qui se comportent différemment des services de l'API qui ne se contentent que de « copier » le contenu JVC (voir sous-section précédente).

URL de base: https://api.jeuxvideo.com/v4/

  • PATH: chemin d'accès de l'endpoint
  • PARAMS, HEADERS:
    • param : variable entre accolades à remplacer dans l'URL, suivie dans la documentation de son type (str ou int). Par exemple, contents/{contentID}/comments doit être appelé comme suit : contents/123456/comments.
      Note : dans la plupart des cas, le paramètre accountId peut être remplacé par la valeur me lorsque la requête doit pointer sur le compte connecté.
    • header : header particulier à inclure dans l'en-tête de la requête. Si pas de spécification, l'en-tête ne contient que les headers de base spécifiés plus haut (User-Agent, Jvc-Authorization, etc.).
  • QUERY: par exemple pour les deux query page, perPage int, l'URL devra être: api.jeuxvideo.com/v4/contents/1234ID/comments?page=1&perPage=30
  • BODY: Corps de la requête au format JSON

Le fichier JSON contenant les id des params et des query strings chronicles modes genres events machine categories (obtenu via la requête GET config) est disponible ici.

POST
NOM PATH PARAMS, HEADERS BODY
addComment contents/{contentID}/comments contentID int
"content": "commentaire"
addCommentVote contents/{contentID}/comments/{commentID}/vote contentID int

commentID int

"type": 1 ou -1
addReply contents/{contentID}/comments/{commentID}/answers contentID int

commentID int

"content": "Super jeu !"
addReview games/{id}/{machine}/reviews/users id int

machine int

"content": "Super jeu !",
"mark": 4,
"onProfile": true
login accounts/login
"alias": "pseudo",
"password": "mot_de_passe"
logout accounts/logout
register accounts/register header : "Jvc-Auth-Token" : str
"email": "email",
"alias": "pseudo",
"password": "motdepasse",
"optin": false
reinitPassword accounts/reset
"alias": "pseudo",
"password": "nouveau",
"session": "",
"captcha": {
    "imageKey": "",
    "imageName": "",
    "imageValues": [
        "val1",
        "val2"
     ]
 }
reportAccount accounts/{accountId}/report accountId int
"reason": 1,
"message": "Raison"
reportComment contents/{contentID}/comments/{commentID}/report contentID int

commentID int

"reason": 1,
"message": "Raison",
"captcha": {
    "session": "clé"
    "clé_captcha": "Valeur"
    "valeur_captcha": "Valeur"
}
reportReview games/{id}/{machine}/reviews/users/{reviewId}/report id int

machine int reviewId int

"reason": 1, 
"message": "Raison",
"captcha": {
    "session": "clé"
    "clé_captcha": ""
    "valeur_captcha": ""
}
restoreComment contents/{contentID}/comments/{commentID} contentID int

commentID int

"content" : "comment"
validateAccount accounts/confirm
"id": 123,
"hash": "hash",
"alias": "pseudo",
"password": "mdp"
validateSignature general/stores/android/validation
"signature": "signature",
"signed_data": "signed_data"
addFavoritesForums accounts/me/favorites/forums "forums" : [50,51,52] # ID des forums
addFavoritesGames accounts/me/favorites/games "games" : [{"id":"ID du jeu", "machine":"ID de la machine"}]
addFavoritesTopics accounts/me/favorites/topics "topics" : [74229153, 74229154] # ID des forums

Ci-dessous figurent les requêtes GET, qui se comportent comme des recherches selon les filtres passés en queries. Les queries de type str (chaînes de caractère) représentent des ID répertoriées dans la réponse à la requête general/config (sous forme de fichier JSON, disponible ici). Si l'on souhaite filtrer les résultats selon leur nature (news, vidéos, previews, etc.), on dispose du dictionnaire suivant (provenant de l'application mobile) qui à chaque nature de document associe l'ID des catégories associées :

{'videos': '6,13,14,15,16,17,145,19,20',
'news': '50',
'previews': '55',
'tests': '56,57',
'all': '6,13,14,15,16,17,145,50,19,20,53,55,56,57'}

Pour plus de détails, veuillez consulter le fichier JSON de configuration.

Comment vous pouvez le constater, contrairement aux variables de type int, les str peuvent contenir plusieurs valeurs, séparées par des virgules : dans ce cas, elles devront être parsées selon l'encodage URL (voir l'annexe où un script Python réalisant cette tâche est mis à disposition).

Les queries page et perPage, toujours des entiers, représentent respectivement : le numéro de la page de recherche et le nombre d'objets à afficher par page. Les ID comme accountID et commentID sont des entiers. Il est à noter que dans la plupart des cas, accountID peut être remplacé par la chaîne de caractères me qui représente l'utilisateur connecté (connu grâce au cookie coniunctio passé en en-tête de la requête).

GET
NOM PATH PARAMS, HEADERS QUERY
config general/config
getAccount accounts/{accountId} accountId int
getAllFavorisGames accounts/{accountId}/favorites/games/all accountId int
getArticle contents/{id} id int
getCaptcha captcha/start nb int
getChroniclesSummary contents/chronicles machines str
getComment contents/{contentID}/comments/{commentID} contentID int

commentID int

getCommentAnswers contents/{contentID}/comments/{commentID}/answers contentID int

commentID int

getComments contents/{contentID}/comments contentID int page int

perPage int

getContentBean contents/{id} id int
getContentList contents categories str

types str events str machines str page int perPage int

getCurrentAccountReport accounts/{accountId}/report accountId int
getCurrentCommentReport contents/{contentID}/comments/{commentID}/report contentID int

commentID int

getCurrentReviewReport games/{id}/{machine}/reviews/users/{reviewId}/report id int

machine str reviewId int

getFavorites accounts/{accountId}/favorites accountId int
getFavoritesForum accounts/{accountId}/favorites/forums accountId int page int

perPage int

getFavoritesGames accounts/{accountId}/favorites/games accountId int
getFavoritesTopics accounts/{accountId}/favorites/topics accountId int page int

perPage int

getFolder contents/{id} id int
getGame games/{id}/{machine} id int

machine str

getGameDetails games/{id}/{machine}/details id int

machine str

getGameImages games/{id}/{machine}/images id int

machine str

page int

perPage int

getGameList games/{type} type str machines str

machine str genre str mode str page int perPage int

getGameNews games/{id}/{machine}/news id int page int

perPage int

getGameReleaseList games/releases month str

year str machines str page int perPage int

getGameReview games/{id}/{machine}/reviews/users/{review} id int

machine str review str

getGameReviews games/{id}/any/reviews id int
getGameReviews games/{id}/{machine}/reviews id int

machine str

getGameSummary contents/games machines str
getGameUserReviews games/{id}/{machine}/reviews/users id int

machine str

page int

perPage int

getGameVideos games/{id}/{machine}/videos id int

machine str

page int

perPage int

getGameWikis games/{id}/{machine}/wikis id int

machine str

page int

perPage int

getHeadlineList contents/trending machines str

page int perPage int

getHighTechSummary contents/hightech machines str
getLightGame games/{id}/{machine}/light id int

machine str

getNews contents/{id} id int
getNewsHighTech contents/hightech page int

perPage int

getPageContents accounts/{accountId}/page/contents accountId int page int

perPage int

getPageReviews accounts/{accountId}/page/reviews accountId int page int

perPage int

getProfile accounts/{accountId}/profile accountId int
getProfilePage accounts/{accountId}/page accountId int
getRelatedNews contents/{id}/news id int page int

perPage int

getRelatedVideos contents/{id}/videos id int page int

perPage int

getRelatedWikis contents/{id}/wikis id int page int

perPage int

getStores games/{id}/{machine}/stores id int

machine str

getTechList contents/hightech page int

perPage int

getTopWikis contents/wikis
getTopsComments contents/{contentID}/comments/tops contentID int
getVideo videos/{id} id int
getVideosSummary contents/videos machines str
search search q str
searchArticles search/articles q str

page int perPage int

searchAutocomplete search/games/autocomplete q str
searchGames search/games q str

page int perPage int

searchNews search/news q str

page int perPage int

searchVideos search/videos q str

page int perPage int

searchWikis search/wikis q str

page int perPage int

sponso general/nativeads
PUT
NOM PATH PARAMS, HEADERS BODY
saveDescription accounts/me/profile/description
"description": "Ma description"
saveExcludedMachines accounts/me/profile/excluded-machines
"machines" : [1, 2, 3, 4]  // id des machines
saveMachines accounts/me/profile/machines
"machines" : [1, 2, 3, 4]  // id des machines
updateComment contents/{contentID}/comments/{commentID} contentID int

commentID int

"content": "Super jeu !"
uploadAvatar accounts/me/avatar header : "Content-Type": "application/octet-stream"
body : objet bytes (conversion du fichier image en octets, pas de clé associée)
uploadCover accounts/me/cover header : "Content-Type": "application/octet-stream"
body : objet bytes (conversion du fichier image en octets, pas de clé associée)
DELETE
NOM PATH PARAMS, HEADERS BODY
deleteComment contents/{contentID}/comments/{commentID} contentID int

commentID int

deleteCommentVote contents/{contentID}/comments/{commentID}/vote contentID int

commentID int

deleteFavorisForum accounts/me/favorites/forums "forums" : [50,51,52] # ID des forums
deleteFavorisGames accounts/me/favorites/games "forums" : [{"id":"ID du jeu", "machine":"ID de la machine"}]
deleteFavorisTopics accounts/me/favorites/topics "topics" : [74229153, 74229154] # ID des topics

Annexe[modifier | modifier le wikicode]

Jvc-Authorization[modifier | modifier le wikicode]

Voici un script Python qui permet de construire le header Jvc-Authorization :

import hashlib, hmac, datetime, urllib.parse

PARTNER_KEY = '550c04bf5cb2b'
HMAC_SECRET = b'd84e9e5f191ea4ffc39c22d11c77dd6c'
DOMAIN = 'api.jeuxvideo.com'
API_VERSION = 4

def auth_header(path: str, method: str = 'GET', query: dict = None) -> str:
    # Conversion de path en URL avec urllib.parse.urlparse
    date = datetime.datetime.now().isoformat()
    parsed_url = urllib.parse.urlparse(f"https://{DOMAIN}/v{API_VERSION}/{path}")
    if query:
        parsed_url = parsed_url._replace(query=urllib.parse.urlencode(query))
        
    # Création de la chaîne à hash
    string_to_hash = [PARTNER_KEY, date, method, parsed_url.netloc, parsed_url.path]
    if parsed_url.query:
        string_to_hash.append(parsed_url.query)
    else:
        string_to_hash[-1] += '\n'
    string_to_hash = '\n'.join(string_to_hash)
    
    # Hash de la chaîne puis renvoi sous forme de Jvc-Authorization
    signature = hmac.new(HMAC_SECRET, msg=string_to_hash.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    return f"PartnerKey={PARTNER_KEY}, Signature={signature}, Timestamp={date}"

Requêtes à l'API[modifier | modifier le wikicode]

Voici un script Python permettant d'envoyer des requêtes à l'API et qui utilise la fonction précédente :

import requests, json
from requests import Response

DOMAIN = 'api.jeuxvideo.com'
API_VERSION = 4

def call(path: str, method: str = 'GET', query: dict = None, data: dict = None, cookies: dict = None) -> Response:
    url = f"https://{DOMAIN}/v{API_VERSION}/{path}"
    jv_auth = auth_header(path, method=method, query=query)
    headers = {
        "Jvc-Authorization":jv_auth,
        "Content-Type": "application/json",
        "jvc-app-platform":"Android",
        "jvc-app-version":"338",
        "user-agent":"JeuxVideo-Android/338",
        "host":"api.jeuxvideo.com"
    }

    if data:
        data = json.dumps(data)

    return requests.request(url=url, method=method, data=data, headers=headers, params=query, cookies=cookies)

Parsing des queries[modifier | modifier le wikicode]

Lors de l'appel à contents par exemple, vous pouvez passer en query des chaînes de caractère comme categories ou types. Ces queries, s'il contiennent plusieurs valeurs, doivent être encodés au format d'encodage URL, ce qui peut être fait en Python :

from urllib.parse import quote

categories = "50,53,56" # la recherche récupérera les articles de catégories 50, 53 et 56.
categories_parsed = quote(categories)

query = {"categories": categories_parsed}

# vous pouvez désormais passer ce dictionnaire en query

Accès aux forums[modifier | modifier le wikicode]

Étrangement, il n'existe pas d'endpoint particulier pour envoyer un message ou un topic sur les forums de jeuxvideo.com.

Lorsque l'application mobile souhaite récupérer les pages des forums, elle envoie une requête au lien usuel de la ressource à ceci près que www est remplacé par api. La requête renvoie alors une page HTML en guise de réponse, voir la rubrique en question.

Ainsi, lorsque l'on cherche à interagir avec les forums, il n'y a pas d'avantage à envoyer des requêtes à l'API plutôt qu'au site de base en l'état.

Principe général[modifier | modifier le wikicode]

En premier lieu, afin d'envoyer un message ou un topic sur un forum, il faut bien évidemment que vous soyez connecté et vus devrez donc passer en en-tête de la requête votre cookie coniunctio, récupérable à partir d'une requête à l'endpoint accounts/login envoyant vos identifiants.

Les inputs cachés contenant les valeurs à envoyer dans les requêtes visibles depuis les outils de développeur.

En second lieu, vous ne devrez pas seulement envoyer aux serveurs le contenu de votre message (et le titre du topic si besoin) : la requête POST devra aussi contenir des paires clés-valeurs générées par le site lui-même et qui agissent comme une sorte de protection. Celles-ci sont : fs_session, fs_timestamp, fs_version, form_alias_rang ainsi qu'un hash.

Heureusement, ces valeurs sont directement disponibles depuis des inputs cachées dans le code HTML des pages de forums/topics et situées dans la div nommée js-form-session-data.

Si l'on veut envoyer un message sur un topic (ou un forum), il faudra d'abord envoyer une requête GET à l'URL associée et récupérer ces valeurs chiffrées avant d'envoyer une requête POST avec le contenu du message en plus de ces dernières.

Script Python[modifier | modifier le wikicode]

La classe Python suivante permet d'interagir avec les forums de cette manière. Il vous suffit d'entrer la valeur de votre cookie coniunctio et d'appeler les fonctions souhaitées avec le contenu des messages ou topics.

Bien que cette méthode permette d'outrepasser les captchas, il se peut que vous soyez bloqué par le site en envoyant un trop grand nombre de requêtes dans un trop court laps de temps.

import requests, json
from requests import Response
from bs4 import BeautifulSoup

DIV_CLASS = 'js-form-session-data'

class ForumClient:
    def __init__(self, coniuntio: str):
        self._cookies = {'coniunctio':coniuntio}

    def _scrape_input_tokens(self, res: Response) -> dict:
        soup = BeautifulSoup(res.text, 'html.parser')
        form = soup.find('div', {'class':DIV_CLASS})
        jsi = form.findChildren("input")
        
        tokens = {
            "fs_session":jsi[0].attrs["value"],
            "fs_timestamp":jsi[1].attrs["value"],
            "fs_version":jsi[2].attrs["value"],
            jsi[3].attrs["name"]:jsi[3].attrs["value"],
            "form_alias_rang":"1",
            "g-recaptcha-response":""
        }
        
        return tokens

    def _get_forum_URL(self, forum_id) -> str:
        return requests.get(f'https://www.jeuxvideo.com/forums/0-{forum_id}-0-1-0-1-0-a.htm').url

    def _get_topic_URL(self, topic_id) -> str:
        return requests.get(f'https://www.jeuxvideo.com/forums/42-1-{topic_id}-1-0-1-0-a.htm').url

    def post_message(self, topic_id: int, message: str) -> Response:
        """
        topic_id, int : ID du topic
        message, str : votre message

        renvoie un objet Response
        """
        topic_URL = self._get_topic_URL(topic_id)
        res = requests.get(topic_URL, cookies=self._cookies)

        data = self._scrape_input_tokens(res)
        data['message_topic'] = message

        res = requests.post(topic_URL, data=data, cookies=self._cookies)

        return res

    def post_topic(self, forum_id: int, titre: str, message: str, sondage: dict = None) -> Response:
        """
        forum_id, int : ID du forum
        titre, str : titre du topic
        message, str : corps du topic

        [sondage], dict : dictionnaire de la forme {
            "question_sondage" : "Question ?",
            "reponse_sondage[]" : ["reponse 1", "reponse 2"]
        }
        """
        forum_URL = self._get_forum_URL(forum_id)
        res = requests.get(forum_URL, cookies=self._cookies)
        data = self._scrape_input_tokens(res)

        data['titre_topic'] = titre
        data['message_topic'] = message
        if sondage:
            data |= sondage
            data['submit_sondage'] = '1'

        res = requests.post(forum_URL, data=data, cookies=self._cookies)

        return res

if __name__ == '__main__':
    client = ForumClient("votre coniunctio ici")
    # Exemples d'utilisation
    # Pour poster un message
    client.post_message(74204156, "Je poste depuis Python :)")
    # Pour poster un topic
    client.post_topic(51, 'Je poste depuis Python', 'Et toi ? :)')

Limitations[modifier | modifier le wikicode]

L'API v4 comporte plusieurs limitations :

  • Il est impossible d'outrepasser les protections captchas requises par le site notamment pour signaler un message.
  • Il est impossible de s'inscrire en utilisation l'endpoint accounts/register car celui-ci nécessite un header contenant le token FireBase généré lors de l'installation de l'application (voir le paragraphe à ce sujet). Une solution serait de créer un grand nombre de machines virtuelles (ou d'en automatiser la création), et pour chacune d'entre elles d'intercepter le trafic de l'application (voir section associée) pour récupérer et conserver les tokens dans un fichier. Cette méthode, longue et fastidieuse, ne permettrait donc pas de générer un nombre infini de comptes. Une autre méthode possible, bien qu'elle n'utilise pas l'API, serait de créer un compte sur le site à l'aide d'un webdriver. Celle-ci fonctionne (l'auteur de ce paragraphe l'a déjà implémentée) mais il serait difficile de la rendre compatible pour toutes les plateformes (du fait de la nécessité d'avoir un VPN gérable depuis un script).
  • Il est particulièrement hardu de faire du web scraping sur les pages de forum puisque l'API renvoie une page HTML et non un fichier JSON dans ce cas.

Ressources[modifier | modifier le wikicode]

  • APK 5.4.7 décompilable avec JADX.
  • Classe PHP exploitant l'API (cette classe est incomplète et sera possiblement rendue obsolète dans le temps).

API jvc.gg[modifier | modifier le wikicode]

La page Phoenix des jeux.

Cette API est une API distincte de la v4 et utilisée par la partie Phoenix du site, c'est-à-dire les pages listant les jeux, reviews, vidéos, etc., comme celle-ci. Elle est hébergée sur le domaine api.jvc.gg.



Rétro-ingénierie[modifier | modifier le wikicode]

L'API étant privée, il a fallu procéder par rétro-ingénierie pour la documenter.

Les appels à l'API sont effectués depuis un script chargé par les pages Phoenix. Les noms de variable sont obfusqués mais pas les chaînes de caractère : ainsi une partie des endpoints y sont lisibles. Ce script est téléchargeable ici.

Il est également possible d'observer les URL et les contenus des requêtes depuis le panneau Network (ou Réseau) des outils de développement Web.

Préambule[modifier | modifier le wikicode]

L'API jvc.gg est basée sur l'utilisation de JSON Web Token (JWT), un standard utilisé pour créer des jetons de sécurité qui permettent de transférer de l'information de manière sécurisée entre deux parties au format JSON.

Lorsque vous visitez pour la première fois une page Phoenix, le serveur de JVC renvoie une requête comportant un cookie accessToken. La valeur de ce cookie est une chaîne de caractères encodée au format JWT qui contient notamment votre ID de compte et la valeur de votre cookie coniunctio. La fonction Python suivante permet de récupérer le cookie accessToken en fonction de votre cookie coniunctio :

def get_access_cookie(coniunctio: str) -> str:
  cookies = {'coniunctio':coniunctio}
  res = requests.get('https://www.jeuxvideo.com/tous-les-jeux/', cookies=cookies)
  access_cookie = res.headers['set-cookie'].split(';')[0].replace('accessToken=', '')
  return access_cookie

Ce cookie vous sera utile lorsque les requêtes envoyées sont destinées à opérer sur votre compte.

Endpoints[modifier | modifier le wikicode]

Le tableau suivant répertorie les endpoints connus de cette API. Voici la signification de ses colonnes :

  • PATH : chemin de l'endpoint.
  • PARAMS, HEADERS :
    • param : variable entre accolades à remplacer dans l'URL, suivie dans la documentation de son type (str ou int). Par exemple, contents/{contentID}/comments doit être appelé comme suit : contents/123456/comments.
    • header : header particulier à inclure dans l'en-tête de la requête. Si pas de spécification, l'en-tête ne contient que les headers de base spécifiés plus haut (User-Agent, Jvc-Authorization, etc.).
  • QUERY : valeur à ajouter à l'URL permettant si précisée de filtrer les résultats. Par exemple pour les deux query page, perPage int, l'URL devra être: api.jeuxvideo.com/v4/contents/1234ID/comments?page=1&perPage=30
  • BODY : Corps de la requête au format JSON

Les paramètres spéciaux[modifier | modifier le wikicode]

  • Le paramètre {accessToken} devra toujours être remplacé par la valeur du cookie éponyme.
  • Le paramètre {gameCategory} peut être remplacée par les valeurs suivantes, qui proviennent évidemment du système de classification de JVC :
    • all pour avoir la liste de tous les jeux ;
    • awaited pour avoir la liste des jeux attendus ;
    • popular pour avoir la liste des jeux populaires ;
    • best pour avoir la liste des meilleurs jeux ;
    • currentBest pour avoir la liste des meilleurs jeux du moment ;
    • releases pour avoir la liste des jeux pas encore sortis.

Queries des jeux[modifier | modifier le wikicode]

Les queries renseignent la plateforme, le genre et le mode du jeu. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des jeux PS4 de genre action RPG et de forme multi en ligne, votre chaîne de queries devra être platform=ps4&gameGenre=action_rpg&gameMode=multiplayer_online.

De plus, le query release désigne la date de sortie du jeu, compatible seulement avec les catégories best et releases. Il peut s'agir d'une année ou d'une décennie (dans ce cas, la valeur du query représentera la première année de la décennie suivie du caractère 'd' ; par exemple, 2010d pour les années 2010). Pour les jeux pas encore sortis, ce query peut également contenir le mois (par exemple : 2024-07 pour juillet 2024). Incompatible avec la catégorie currentBest.

Queries des news[modifier | modifier le wikicode]

Les queries renseignent la plateforme, le type, le genre et l'événement des news. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des news de jeux PS5 de genre aventure en rapport avec l'événement Game Awards publiés en 2022, votre chaîne de queries devra être publicationType=game_news&platform=ps5&gameGenre=adventure&event=id201395&publicationPeriod=2022.

Le query publicationPeriod peut être : une année, une année suivie du numéro du mois (2024-07 pour juillet 2024) ou une décennie (2010d pour les années 2010).

Queries des tests[modifier | modifier le wikicode]

Les queries renseignent la plateforme, le mode et le genre des jeux testés, ainsi que l'encadrement de la note des rédacteurs et la date de publication. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des tests de jeux PC de genre aventure, de mode solo ayant eu au moins 14 et datant d'il y a au plus 6 mois, votre chaîne de queries devra être platform=pc&gameGenre=adventure&gameMode=singleplayer&editorialRatingRange=%5B14%2C)&maxAge=P6M

Notez que le query editorialRatingRange doit être encodé au format URL avant d'être envoyé.

Queries des previews[modifier | modifier le wikicode]

Les queries renseignent la plateforme, le genre et l'événement des jeux testés. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des previews des jeux PS5 de genre FPS en rapport avec l'événement Find Your Next Game, votre chaîne de queries devra être platform=ps5&gameGenre=fps&event=fyng.

Queries des vidéos[modifier | modifier le wikicode]

Les queries renseignent le type des vidéos ainsi que la plateforme, le genre et l'événement des jeux traités. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des vidéos gameplays des jeux PC de genre Action en rapport avec l'événement E3, votre chaîne de queries devra être publicationType=gameplay&platform=pc&gameGenre=action&event=e3.

Queries des dossiers[modifier | modifier le wikicode]

Les queries renseignent la plateforme, le genre et l'événement des jeux traités. La liste complète des valeurs des queries possibles pour ces attributs est disponible au format JSON ici.

Par exemple, si vous souhaitez obtenir la liste des vidéos gameplays des jeux PC de genre Action en rapport avec l'événement E3, votre chaîne de queries devra être publicationType=gameplay&platform=pc&gameGenre=action&event=e3.

Queries globaux[modifier | modifier le wikicode]

Ce sont les queries offset et limit, de type int, qui représentent respectivement l'indice de l'item dans la liste à partir duquel les items sont listés (par défaut 0) et le nombre d'items à lister (par défaut 100). Ils fonctionnent avec toutes les recherches.

Requêtes auxiliaires[modifier | modifier le wikicode]

En parallèle des requêtes envoyées par le site pour rechercher des jeux, news, previews, vidéos ou tests, des requêtes auxiliaires sont adressées aux serveurs. Elles permettent en particulier de connaître ce qu'il faut afficher sur la page, les URL des pages ainsi que les options (queries) restantes selon ce qui a déjà été entré dans le panel de recherche.

Ces requêtes utilisent un endpoint contenant un token JWT dans lequel sont stockées des informations générales sur la requête (queries notamment). Comme cette clé secrète n'est pas connue, il nous est malheureusement impossible de générer de tels tokens à la demande.

Liste des endpoints connus[modifier | modifier le wikicode]

GET
PATH PARAMS QUERY DESCRIPTION
games/{gameCategory}/popularity.desc gameCategory str platform str

gameGenre str
gameMode str
offset int
limit int
release str

Renvoie la liste des jeux de la catégorie {gameCategory} triés par popularité décroissante et satisfaisant les queries.
games/{gameCategory}/editorialRating.desc gameCategory str platform str

gameGenre str
gameMode str
offset int
limit int
release str

Renvoie la liste des jeux de la catégorie {gameCategory} triés par popularité décroissante et satisfaisant les queries.
games/{gameCategory}/releaseDate.asc gameCategory str platform str

gameGenre str
gameMode str
offset int
limit int
release str

Renvoie la liste des jeux de la catégorie {gameCategory} triés par popularité décroissante et satisfaisant les queries.
games/{gameCategory}/title.asc gameCategory str platform str

gameGenre str
gameMode str
offset int
limit int
release str

Renvoie la liste des jeux de la catégorie {gameCategory} triés par popularité décroissante et satisfaisant les queries.
news/datePublished.desc publicationType str

platform str
gameGenre str
event str
publicationPeriod str
offset int
limit int

Renvoie la liste des actualités triées par date de publication croissante et satisfaisant les queries.
reviews/datePublished.desc

platform str
gameGenre str
gameMode str
editorialRatingRange str
maxAge str
offset int
limit int

Renvoie la liste des tests triés par date de publication croissante et satisfaisant les queries.
reviews/editorialRating.desc

platform str
gameGenre str
gameMode str
editorialRatingRange str
maxAge str
offset int
limit int

Renvoie la liste des tests triés par note décroissante des rédacteurs JVC et satisfaisant les queries.
reviews/gameTitle.asc

platform str
gameGenre str
gameMode str
editorialRatingRange str
maxAge str
offset int
limit int

Renvoie la liste des tests triés par nom croissant du jeu et satisfaisant les queries.
previews/datePublished.desc platform str

gameGenre str
event str
offset int
limit int

Renvoie la liste des annonces triées par date de publication croissante et satisfaisant les queries.
videos/datePublished.desc publicationType str

platform str
gameGenre str
event str
offset int
limit int

Renvoie la liste des vidéos triées par date de publication croissante et satisfaisant les queries.
topics/datePublished.desc platform str

gameGenre str
event str
offset int
limit int

Renvoie la liste des dossiers triés par date de publication croissante et satisfaisant les queries.
me/{accessToken}/privateMessages accessToken str Renvoie la liste des 25 derniers messages privés.
me/{accessToken}/notifications/subscription accessToken str Renvoie la liste des dernières notifications.
me/{accessToken}/notifications/privateMessage/unseenCount accessToken str Renvoie le nombre de messages privés non-vus.
me/{accessToken}/notifications/subscription/unseenCount accessToken str Renvoie le nombre de notifications non-vues.
me/{accessToken}/profile accessToken str Informations du profil.
config/menu Informations à afficher sur les pages Phoenix.
POST
PATH PARAMS BODY DESCRIPTION
me/{accessToken}/notifications/subscription/resetUnseenCount accessToken str Met à zéro le nombre de notifications non-vues.
me/{accessToken}/notifications/privateMessage/resetUnseenCount accessToken str Met à zéro le nombre de messages privés non-vus.
me/{accessToken}/notifications/subscription/resetUnreadCount accessToken str Met à zéro le nombre de notifications non-lues.
me/{accessToken}/notifications/privateMessage/resetUnreadCount accessToken str Met à zéro le nombre de messages privés non-lus.
me/{accessToken}/notification/{notifId}/setRead accessToken str

notifId int

Lit une notification.
me/{accessToken}/privateMessage/{messageId}/setRead accessToken str

messageId int

Lit un message privé.
_auth/{accessToken}/logout accessToken str Se déconnecter.

Ancienne API (dépréciée)[modifier | modifier le wikicode]

Le contenu qui va suivre concerne l'ancienne API qui n'existe plus.

Identification[modifier | modifier le wikicode]

L'API utilise le nom de domaine ws.jeuxvideo.com. Pour y accéder, il faut utiliser un des identifiants ci-dessous (authentification HTTP basique).

Tous ces identifiants fonctionnement actuellement, et je n'ai pas repéré de page semblant afficher un comportement différent selon.

Utilisateur Mot de passe
Android 1.0 appandr e32!cdf
Android 2.0.3 app_and_gnw FC?4554?
Android 2.5 app_and_ms D9!mVR4c
Android MP app_ag_jvmp LXnb45=d#
Android Tab nex12sz GT4!V2cT
iPhone app_ios_nw W!P45-R
iPad ip45de XpD5!FT

L'API est accessible en HTTP et en HTTPS, préférez la version HTTPS !

Utilisation[modifier | modifier le wikicode]

Connexion[modifier | modifier le wikicode]

Pour vous connecter, utilisez la page mon_compte/connexion.php.

Voici les paramètres à envoyer (GET ou POST) :

Vous renconterez potentiellement une erreur vous demandant de remplir un captcha, avec un lien vers l'image, et une balise params_form, qu'il faudra ajouter aux paramètres de la première requête, ainsi que le paramètre code avec la valeur du captcha.

Important : n'ouvrez pas le lien du captcha dans votre navigateur si vous êtes connecté à votre compte JVC ; un captcha différent est renvoyé selon la présence du cookie wenvjgol (qui ne doit PAS être présent pour afficher un captcha de l'API, sinon vous aurez systématiquement une erreur de captcha invalide).

En réponse, vous recevez plusieurs informations présentes dans la CDV, mais surtout le cookie wenvjgol, que vous devrez utiliser pour poster sur les forums et utiliser les messages privés.

Pour information, wenvjgol signifie logjvnew à l'envers, et c'est le cookie de session de JVC dont le nom a subi plusieurs transformations au fil des années.

Pour le renvoi de mot de passe, utilisez cette URL (en remplaçant Cisla par le pseudo ou bien l'adresse e-mail) :

https://ws.jeuxvideo.com/cgi-bin/passperdu_ws.cgi?email_pseudo=Cisla

Jeux, astuces, news...[modifier | modifier le wikicode]

URL Description
00.machines_version.xml
00.version_tablette.xml
Dernière version de l'application, informations sur la publicité, liste des consoles
01.flux_jeux_nouveautes.xml
01.flux_jeux_prochainement.xml
Listes de jeux
01.jeux/21963.xml
01.jeux/details/21963.xml
01.jeux/videos/21963.xml
01.jeux/screen/21963.xml
03.preview/43689.xml
03.test_complet/9813.xml
01.jeux/news/21963.xml
05.jeu_astuce/41030.xml
05.astuce/33258.xml
01.jeux/screen_ast/38024.xml
Informations sur le jeu (le nombre correspond à l'id)
03.dossier/18270.xml
03.dossier/18270/1.xml
Un dossier : 18270 est l'id du dossier, 1 est la page (si vous n'en mettez pas, vous avez le sommaire)
02.flux_news.xml
02.flux_news-4.xml
La liste des dernières news. Dans le deuxième exemple, en ajoutant le nombre 4, vous retournez 4 jours en arrière (ça peut aller jusqu'à 9)
03.flux_articles_tests.xml
03.flux_articles_tests-4.xml
03.flux_articles_apercus.xml
03.flux_articles_apercus-4.xml
03.flux_articles_dossier.xml
03.flux_articles_dossier-4.xml
04.flux_videos_cliq.xml
04.flux_videos_cliq-4.xml
04.flux_videos_gaming.xml
04.flux_videos_gaming-4.xml
04.flux_videos_autres.xml
04.flux_videos_autres-4.xml
05.flux_astuces.xml
05.flux_astuces-4.xml
04.flux_videos_chroniques.xml
04.flux_videos_chroniques-4.xml
04.flux_toutes_les_videos.xml
04.flux_toutes_les_videos-4.xml
Même chose pour les derniers articles, tests, astuces et aperçus
02.news/1234.xml
02.news_screen/1234.xml
Voir une news (le nombre correspond à l'id)
ean.php?ean=0045496830144 Voir le jeu associé au code-barre (EAN) 0045496830144
forums_index.xml Liste des forums généraux
search_n/mario
search/mario
search_sug/mario
search_forums/mario
search_forums_sug/mario
Effectuer une recherche dans le nom des jeux ou des forums
tab_suggest_blocs.xml
tab_suggest_forums.xml
À compléter
cgi-bin/liste.cgi À compléter

Forums[modifier | modifier le wikicode]

Pour les forums, les URL sont les mêmes que pour JVC, à part que :

  • Le .htm est transformé en .xml
  • Le www.jeuxvideo.com est remplacé par un ws.jeuxvideo.com
  • La chaîne de caractères à la fin de l'URL (comme blabla-15-18-ans ou nom-du-topic) est remplacée par un 0 (sauf pour la recherche).

Par exemple :
http://www.jeuxvideo.com/forums/0-50-0-1-0-1-0-blabla-15-18-ans.htm
Devient :
https://ws.jeuxvideo.com/forums/0-50-0-1-0-1-0-0.xml

Exemples d'URL :

URL Description
https://ws.jeuxvideo.com/forums/0-50-0-1-0-1-0-0.xml La liste des sujets
https://ws.jeuxvideo.com/forums/0-50-0-1-0-1-2-cactus.xml Rechercher « cactus » dans le titre des topics
https://ws.jeuxvideo.com/forums/1-50-1-1-0-1-0-0.xml Un topic
https://ws.jeuxvideo.com/forums/3-50-0-1-0-1-0-0.xml Formulaire pour créer un topic
https://ws.jeuxvideo.com/forums/3-50-128244545-1-0-1-0-0.xml 10 derniers messages d'un topic + formulaire
https://ws.jeuxvideo.com/forums/5-50-128244545-1-0-1-0-0.xml Formulaire de réponse à un topic
https://ws.jeuxvideo.com/cgi-bin/jvforums/forums.cgi Envoyer un message (avec les données POST)
https://ws.jeuxvideo.com/profil/cisla.xml Voir une CDV

Pour envoyer un message ou créer un topic : aux données de formulaires qui vous sont communiquées dans la balise params_form, vous devez ajouter le paramètre yournewmessage qui contient le message et newsujet qui contient le titre du topic (si vous créez un nouveau topic). Vous devez ensuite attendre une seconde avant d'envoyer les données POST à la page forums.cgi. En cas de captcha à gérer, vous devez répéter l'opération à partir des informations qu'on vous envoie en réponse, avec la solution du captcha en paramètre code.

Vous devez être connecté (envoyer le cookie coniunctio) pour récupérer et envoyer un formulaire.

Le même formulaire peut aussi bien être envoyé au forums.cgi de ws.jeuxvideo.com qu'à celui de www.jeuxvideo.com, ce qui vous permet de choisir entre apparaître « via mobile » ou non. Les données POST à envoyer sont donc les mêmes sur toutes les versions de JVC.

Messages privés[modifier | modifier le wikicode]

L'id de l'utilisateur mentionnée ci-dessous correspond à la partie du cookie coniunctio qui se trouve avant le premier "$".

Une fois connecté, vous devez envoyer le cookie coniunctio à chaque requête.

URL Description
jvmp.xml Dernière version de l'application, liste des consoles et des smileys, lien vers la charte, pub activée ou non
messages-prives/connexion_ws.php Les paramètres sont les mêmes que pour la connexion depuis mon_compte/connexion.php, décrite plus haut. Cependant, la réponse XML contient quelques informations supplémentaires à propos des MP.
messages-prives/boite-reception_ws.php Permet de connaître le contenu de la boîte de réception. Paramètre à envoyer :
  • id_user : id de l'utilisateur
messages-prives/envoyes_ws.php Permet de connaître la liste des messages envoyés. Paramètre à envoyer :
  • id_user : id de l'utilisateur
messages-prives/message_ws.php Permet de lire le contenu d'un message privé. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • id_discussion : id de la discussion
  • nb_clic : le nombre de fois où l'utilisateur a cliqué sur « Voir les messages précédents »
  • last_position_message : Si ce paramètre vaut 0, les 5 derniers messages sont affichés. Sinon, les 10 messages les plus anciens en partant du xème sont affichés, la numérotation débutant à partir de 0.
messages-prives/nouveau_ws.php Permet d'envoyer un nouveau message privé. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • all_dest : les destinataires, séparés pas des point-virgules
  • sujet : le titre du MP
  • yournewmessage : le contenu du MP
  • tmp : le timestamp actuel
  • control : md5("k_ys4+a]x" + idUtilisateur + "j6A=d?6-W*-" + timestamp + "z_.Im")

Il se peut également que vous ayez à gérer un code de confirmation.

messages-prives/repondre_ws.php Permet de répondre à un message privé. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • id_discussion : id de la discussion
  • yournewmessage : le contenu du message
  • tmp : le timestamp actuel
  • control : md5("vx*96-dP" + timestamp + "8e63ddO_" + idUtilisateur + "al)orc9W")
  • box : 1

Il se peut également que vous ayez à gérer un code de confirmation.

messages-prives/suggest_pseudo_ws.php Liste quelques pseudos qui commencent par ce que l'utilisateur est en train de taper. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • search : ce que l'utilisateur est en train de taper
messages-prives/indesirable_ws.php Liste les utilisateurs ajoutés en indésirable. Paramètres à envoyer :
  • id_user : id de l'utilisateur
messages-prives/add_indesirable_ws.php Permet d'ajouter un utilisateur dans les indésirables. Certaines données nécessaires pour former la requête doivent être récupérés avec info_alerte_ws.php. Paramètres à envoyer :
  • id_user : id de l'utilisateur actuel
  • pseudo : pseudo de l'utilisateur
  • id_user_indesirable : id de l'utilisateur à ajouter en indésirable
  • pseudo_indesirable : pseudo de l'utilisateur à ajouter en indésirable
  • time_indesirable : le timestamp actuel
  • key_indesirable : md5("5d*" + timestamp + "v1S{" + idIndesirable + "I,5|S9z)" + pseudoIndesirable + "52Sx69,")
messages-prives/del_indesirable_ws.php Permet d'enlever un utilisateur des indésirables. Paramètres à envoyer :
  • id_user : id de l'utilisateur actuel
  • id_user_indesirable : id de l'utilisateur à enlever des indésirables
messages-prives/info_alerte_ws.php Permet de connaître les informations qui permettront de faire une DDB ou une mise en indésirable sur un message. Paramètres à envoyer :
  • id_user : id de l'utilisateur qui fait la DDB
  • id_message : id du message concerné
messages-prives/alerte_ws.php Permet d'effectuer une DDB (ce qui est inutile, étant donné qu'elles ne sont jamais traitées). Certaines données nécessaires pour former la requête doivent être récupérés avec info_alerte_ws.php. Paramètres à envoyer :
  • id_user : id de l'utilisateur qui fait la DDB
  • pseudo : pseudo de l'utilisateur
  • motif : le motif, par exemple Piratage (les mêmes motifs que dans les MP sur JVC normal)
  • id_message : id du message concerné
  • id_discussion : id de la discussion concernée
  • time_alerte : timestamp actuel
  • key_alerte : md5("v6dE{" + idDiscussion + "d96/" + idMessage + "mvn85Qa" + timestamp + "auxarmeheuuuuuuu")
messages-prives/connexion_valid_ws.php Permet d'afficher le nombre de messages non-lus ainsi que l'URL de l'avatar. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • pseudo : pseudo de l'utilisateur
messages-prives/add_destinataire_ws.php Permet d'ajouter un destinataire à un message privé. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • id_discussion : id de la discussion
  • tab_pseudo : liste des destinataires à ajouter, séparés par un point-virgule
messages-prives/del_message_ws.php Permet de supprimer un message privé. Paramètres à envoyer :
  • id_user : id de l'utilisateur
  • del_discussion : id des discussions à supprimer séparés par un tiret
  • box : 1

Ancienne rétro-ingéniérie[modifier | modifier le wikicode]

Décompilation[modifier | modifier le wikicode]

Cette section concerne la décompilation de l'application Android de JVC sous Linux.

Pour décompiler l'application JVC, commencez par récupérer le fichier APK ici (version 2.5). Ensuite, décompressez-le avec 7-Zip :

$ 7z x com.jeuxvideo-2.5.apk

Puis, transformez le .dex en .jar à l'aide de l'utilitaire dex2jar :

$ dex2jar classes.dex

Ensuite, vous pouvez ouvrir le .jar dans jd-gui, qui s'occupera de décompiler les fichiers :

$ jd-gui classes-dex2jar.jar

Déobfuscation[modifier | modifier le wikicode]

En parcourant le code source, vous verrez des choses de ce genre à la place des chaînes de caractères :

this.g.getString(2131230723)

Cela correspond à des chaînes de caractères stockées dans un fichier séparé, resources.arsc. Pour en extraire le contenu, nous allons utiliser l'utilitaire apktool :

$ apktool d com.jeuxvideo-2.5.apk strings

Ensuite, vous pourrez trouver dans le fichier strings/res/values/strings.xml la liste des chaînes de caractères associées à leurs variables, et dans strings/res/values/public.xml, la liste des variables associées aux nombres (en hexadécimal) comme 2131230723 que vous voyez dans le code décompilé. Ce n'est pas très pratique mais je n'ai pas trouvé d'outil qui modifie directement le code décompilé pour y intégrer les valeurs du resources.arsc (mais je n'ai pas beaucoup cherché non plus).

Ce n'est pas tout. À partir de la version 2.0.3 de l'application JVC, et pour toutes les version de l'application JVC MP, vous pourrez également voir dans le fichier strings.xml des variables telles que :

<string name="md5_a">290B2FB20CFD682C120BBBFFFE5928D9</string>

Ces chaînes de caractères (des URL et quelques salts) sont obfusquées. Après avoir lu un peu de code, j'ai trouvé comment les restituer. Voici un bout de Python qui montre comment faire :

from Crypto.Cipher import AES
from passlib.utils.pbkdf2 import pbkdf2

cle = 'package android.content'
cle = AES.new(pbkdf2(cle, cle, 10, 128/8, 'hmac-sha1'))

string = '290B2FB20CFD682C120BBBFFFE5928D9'
string = cle.decrypt(string.decode('hex'))
print repr(string[:-ord(string[-1])])

Modifiez la clé selon l'application que vous décompilez:

  • Pour l'application Jeuxvideo.com (version >= 2.0.3), c'est package com.jeuxvideo.activity
  • Pour l'application Jeuxvideo.com (version >= 2.5), c'est package android.content
  • Pour l'application Jeuxvideo.com MP (version >= 1.0), c'est package com.jeuxvideomp.activity

L'application pour tablettes n'est pas concernée par ce mécanisme d'obfuscation.

Liens externes[modifier | modifier le wikicode]

Références[modifier le wikicode]