Categories
blandet

Sådan låner jeg bøger på eReolen – med Python

eReolen er fint til at læse gratis e-bøger på, men både hjemmesiden og app’en synes jeg er kluntet at bruge. Derfor har jeg undersøgt, om jeg kunne bruge Python til at snakke med eReolen – og så udelukkende bruge app’en til at læse på.

Først havde jeg brug for at finde ud af, hvordan eReolens app virker.

Jeg installerede Android-emulatoren NOX og installerede app’en. Derefter hentede jeg Charles, som er et værktøj til at overvåge internettraffik. Jeg satte NOX op til at bruge Charles’ IP som proxyserver, åbnede op i Charles for at kunne se krypteret trafik og vupti – nu kunne jeg se trafikken fra NOX i Charles.

Det ser nogenlunde sådan ud, når man logger på eReolen:

eReolens app snakker altså med ereolen-be.redia.dk og en side på serveren, der hedder rpc.php. I spalten “Info” kan jeg se forskellige funktioner, som rpc.php understøtter. Den allerførste (authenticate) er login. Den kiggede jeg nærmere på:

Browseren sender data af sted i JSON-format
JSON-strengen har et id, som ser ud til at være tilfældigt genereret, fortæller hvilken protokol, der bruges, en metode (her “authenticate”) og nogle parametre (bibliotekskode, CPR-nummer og PIN-kode)

Når det lykkes at logge på, får man nogenlunde dette data tilbage:

{
	"jsonrpc": "2.0",
	"id": "[slettet]",
	"result": {
		"result": true,
		"data": {
			"retailerid": "810",
			"cardnumber": "[slettet]",
			"ereolen": {
				"result": true,
				"data": null,
				"message": "",
				"language": "da",
				"code": 0
			},
			"netlydbog": {
				"result": true,
				"data": null,
				"message": "",
				"language": "da",
				"code": 0
			}
		},
		"message": "",
		"language": "da",
		"code": 0
	}
}

Code 0 betyder, at login er lykkedes.

Tilbage får man også en cookie med et ID for den “session”, man har startet. Den hedder PHPSESSID.

Sådan her ser login-proceduren ud i Python:

import requests		# Used for http requests
import uuid			# Used to generate random strings for json request ids

# Credentials
# Library ID as string (See end of program for list), CPR-number as string (1234561234), PIN as string
logininfo = ['810','1234567890','1234']

# Login and get session cookie
headers = {
	'Accept-Charset': 'UTF-8',
	'Content-Type':	'application/json',
	'User-Agent': 'Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G925F Build/JLS36C)',
	'Accept-Encoding': 'gzip'
}	

json = {
	"id": str(uuid.uuid4()),
	"jsonrpc": "2.0",
	"method": "authenticate",
	"params": logininfo
}

url = 'https://ereolen-be.redia.dk/v5/rpc.php'
r = requests.post(url, headers=headers, json=json)
loginresponse = r.json()

# Save session cookie to cookie dictionary
cookies = {}
cookies['PHPSESSID'] = r.cookies['PHPSESSID']
cookies['ROUTEID'] = r.cookies['ROUTEID']

Det er let at undersøge de forskellige funktioner/metoder i Charles. I dette tilfælde var jeg interesseret i at låne en bog. Den funktion hedder, logisk nok, createLoan.

Her er hvad eReolens app sender af sted, når man låner en ebog:

{
	"id": "[slettet]",
	"jsonrpc": "2.0",
	"method": "createLoan",
	"params": ["9788792922021", "ereolen"]
}

En søgning på tallet i “params” viser, at vi her har at gøre med Håbløse slægter af Herman Bang. Tallet er simpelthen bogens ISBN-nummer.

Det færdige program

Det færdige program, der lader låneren indtaste et ISBN-nummer på en bog, som så bliver lånt, ser sådan her ud.

Du er velkommen til at bruge programmet, videreudvikle, og hvad du ellers har lyst til.

# -*- coding: utf-8 -*-
# Author: Morten Helmstedt. E-mail: helmstedt@gmail.com
""" Loan a book at eReolen """

import requests		# Used for http requests
import uuid			# Used to generate random strings for json request ids

# Credentials
# Library ID as string (See end of program for list), CPR-number as string (1234561234), PIN as string
logininfo = ['810','1234567890','1234']

# Login and get session cookie
headers = {
	'Accept-Charset': 'UTF-8',
	'Content-Type':	'application/json',
	'User-Agent': 'Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G925F Build/JLS36C)',
	'Accept-Encoding': 'gzip'
}	

json = {
	"id": str(uuid.uuid4()),
	"jsonrpc": "2.0",
	"method": "authenticate",
	"params": logininfo
}

url = 'https://ereolen-be.redia.dk/v5/rpc.php'
r = requests.post(url, headers=headers, json=json)
loginresponse = r.json()

# Save session cookie to cookie dictionary
cookies = {}
cookies['PHPSESSID'] = r.cookies['PHPSESSID']
cookies['ROUTEID'] = r.cookies['ROUTEID']

# Check for successful login
if loginresponse["result"]["result"] == True and loginresponse["result"]["data"]["ereolen"]["code"] == 0:
	print("Login lykkedes")
	print("")
	validinput = False
	# Loop to ensure the user enters valid ISBN
	while validinput ==  False:
		isbn = input("Indtast ISBN (13 cifre) på den bog, du vil låne: ")
		# ISBN must be 13 characters and digits only
		if len(isbn) == 13 and isbn.isdigit():
			validinput = True
			isbn = str(isbn)
			# Create loan
			json = {
				"id": str(uuid.uuid4()),
				"jsonrpc": "2.0",
				"method": "createLoan",
				"params": [isbn, "ereolen"]
			}
			r = requests.post(url, headers=headers, cookies=cookies, json=json)
			loanresponse = r.json()
			# Check for loan success
			if loanresponse["result"]["code"] == 0:
				print("Tillykke! Det lykkes at låne bogen. God fornøjelse.")
			else:
				print("Noget gik galt. Måske kan dette gøre dig klogere:")
				print("")
				print(loanresponse)
				print("")
		else:
			print("")
			print("Det ligner, du har tastet forkert ISBN. Prøv igen.")
			print("")
else:
	print("Noget gik galt ved login. Prøv at tjekke dine logininformationer.")

#LIBRARY IDS
'''
Haderslev:851
Frederiksberg:823
Herlev:866
Hedensted:912
Brøndby:860
Lolland:874
Esbjerg:826
Guldborgsund:873
Brønderslev:886
Vesthimmerland:890
Ikast-Brande:835
Glostrup:856
Assens:953
Roskilde:824
Hjørring:867
Køge:872
Greve:830
Samsø:936
Rebild:862
Frederikshavn:880
Dragør:905
Høje-Taastrup:875
Thisted:885
Nordfyn:950
Svendborg:897
Aarhus:816
Langeland:852
Herning:828
Vallensbæk:877
Favrskov:888
Kerteminde:917
Skive:907
Holbæk:899
Hillerød:861
Halsnæs:887
Jammerbugt:902
Ballerup:843
Kalundborg:910
Helsingør:896
Varde:834
Odsherred:879
Viborg:854
Stevns:882
Fredensborg:855
Fredericia:891
Sorø:848
Frederikssund:870
Faxe:895
Norddjurs:906
Rødovre:838
Randers:916
København:810
Hvidovre:853
Sydslesvig:889
Slagelse:894
Tønder:900
Ringsted:884
Albertslund:881
Silkeborg:829
Lejre:935
Gentofte:832
Odense:825
Struer:869
Faaborg-Midtfyn:840
Vejen:893
Ærø:954
Rudersdal:841
Hørsholm:871
Middelfart:864
Billund:850
Allerød:952
Solrød:844
Egedal:898
Lemvig:911
Býarbókasavnið:955
Horsens:847
Kolding:878
Gladsaxe:863
Aabenraa:839
Odder:876
Gribskov:903
Nyborg:858
Ishøj:868
Vejle:849
Sønderborg:836
Mariagerfjord:842
Skanderborg:883
Vordingborg:901
Morsø:904
Lyngby:857
Syddjurs:892
Aalborg:822
Bornholm:865
Furesø:908
Holstebro:859
Næstved:831
Tårnby:837
Ringkøbing-Skjern:845
'''