commit 64745479c6e6e0531ad2a97f461538f03b3f792d Author: xigoi Date: Fri Sep 4 11:32:45 2020 +0200 Semi-working signin and timetable diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dab652b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +* +!*.* +!/**/ +*.swp +*.un~ +.netrwhist +*.json diff --git a/bakalari.nim b/bakalari.nim new file mode 100644 index 0000000..f1ed59b --- /dev/null +++ b/bakalari.nim @@ -0,0 +1,169 @@ +import httpcore, httpclient, json, strformat, tables, times, uri +import elvis + +type + Bakalari* = ref object + website*: Uri + accessToken*: string + refreshToken*: string + Homework* = object + id*: string + subject*: string + teacher*: string + content*: string + startTime*: DateTime + endTime*: DateTime + Message* = object + title*: string + sender*: string + text*: string + sentTime*: DateTime + Hour* = ref object + number*: string + beginTime*: string + endTime*: string + Room* = ref object + name*: string + abbrev*: string + Subject* = ref object + name*: string + abbrev*: string + Teacher* = ref object + name*: string + abbrev*: string + Lesson* = object + hour*: Hour + room*: Room + subject*: Subject + teacher*: Teacher + Day* = object + lessons*: seq[Lesson] + dayOfWeek*: int + date*: DateTime + Timetable* = object + days*: seq[Day] + +const + iso8601 = initTimeFormat("yyyy-MM-dd'T'HH:mm:sszzz") + +proc newBakalari*(website: Uri, username: string, password: string): Bakalari = + new result + result.website = website + let + api = website / "api/login" + client = newHttpClient() + response = client.post($api, body = &"client_id=ANDR&grant_type=password&username={username}&password={password}") + root = response.body.parseJson + result.accessToken = root{"access_token"}.getStr + result.refreshToken = root{"refresh_token"}.getStr + +proc newBakalari*(website: Uri, refreshToken: string): Bakalari = + new result + result.website = website + let + api = website / "api/login" + client = newHttpClient() + response = client.post($api, body = &"client_id=ANDR&grant_type=refresh_token&refresh_token={refreshToken}") + root = response.body.parseJson + result.accessToken = root{"access_token"}.getStr + result.refreshToken = root{"refresh_token"}.getStr + +func accessToken*(bakalari: Bakalari): string = + bakalari.accessToken + +func refreshToken*(bakalari: Bakalari): string = + bakalari.refreshToken + +proc renewTokens(bakalari: Bakalari) = + let + api = bakalari.website / "api/login" + client = newHttpClient() + response = client.post($api, body = &"client_id=ANDR&grant_type=refresh_token&refresh_token={bakalari.refreshToken}") + body = response.body + root = body.parseJson + bakalari.accessToken = root{"access_token"}.getStr + bakalari.refreshToken = root{"refresh_token"}.getStr + +proc getEndpoint*(bakalari: Bakalari, endpoint: string): JsonNode = + let + api = bakalari.website / "api/3" / endpoint + client = newHttpClient(headers = newHttpHeaders { + "Authorization": "Bearer " & bakalari.accessToken, + }) + response = client.get($api) + body = response.body + bakalari.renewTokens + result = body.parseJson + +proc postEndpoint*(bakalari: Bakalari, endpoint: string): JsonNode = + let + api = bakalari.website / "api/3" / endpoint + client = newHttpClient(headers = newHttpHeaders { + "Authorization": "Bearer " & bakalari.accessToken, + }) + response = client.post($api) + body = response.body + bakalari.renewTokens + result = body.parseJson + +iterator homework*(bakalari: Bakalari): Homework = + let root = bakalari.getEndpoint("homeworks") + for node in root{"Homeworks"}.getElems: + yield Homework( + id: node{"ID"}.getStr, + subject: node{"Subject"}{"Name"}.getStr, + teacher: node{"Teacher"}.getStr, + content: node{"Content"}.getStr, + startTime: node{"DateStart"}.getStr.parse(iso8601), + endTime: node{"DateEnd"}.getStr.parse(iso8601), + ) + +iterator messages*(bakalari: Bakalari): Message = + let root = bakalari.postEndpoint("komens/messages/received") + for node in root{"Messages"}.getElems: + yield Message( + title: node{"Title"}.getStr, + sender: node{"Sender"}{"Name"}.getStr, + text: node{"Text"}.getStr, + sentTime: node{"SentDate"}.getStr.parse(iso8601) + ) + +proc timetable*(bakalari: Bakalari, permanent: bool): Timetable = + let root = bakalari.getEndpoint(permanent ? "timetable/permanent" ! "timetable/actual") + var hours: Table[int, Hour] + for hourNode in root{"Hours"}: + hours[hourNode{"Id"}.getInt] = Hour( + number: hourNode{"Caption"}.getStr, + beginTime: hourNode{"BeginTime"}.getStr, + endTime: hourNode{"EndTime"}.getStr, + ) + var rooms: Table[string, Room] + for roomNode in root{"Rooms"}: + rooms[roomNode{"Id"}.getStr] = Room( + name: roomNode{"Name"}.getStr, + abbrev: roomNode{"Abbrev"}.getStr, + ) + var subjects: Table[string, Subject] + for subjectNode in root{"Subjects"}: + subjects[subjectNode{"Id"}.getStr] = Subject( + name: subjectNode{"Name"}.getStr, + abbrev: subjectNode{"Abbrev"}.getStr, + ) + var teachers: Table[string, Teacher] + for teacherNode in root{"Teachers"}: + teachers[teacherNode{"Id"}.getStr] = Teacher( + name: teacherNode{"Name"}.getStr, + abbrev: teacherNode{"Abbrev"}.getStr, + ) + for dayNode in root{"Days"}: + var day: Day + for lessonNode in dayNode{"Atoms"}: + var lesson: Lesson + lesson.hour = hours[lessonNode{"HourId"}.getInt] + lesson.subject = subjects[lessonNode{"SubjectId"}.getStr] + lesson.teacher = teachers[lessonNode{"TeacherId"}.getStr] + lesson.room = rooms[lessonNode{"RoomId"}.getStr] + day.lessons.add lesson + day.dayOfWeek = dayNode{"DayOfWeek"}.getInt + day.date = dayNode{"Date"}.getStr.parse(iso8601) + result.days.add day diff --git a/bk.nim b/bk.nim new file mode 100644 index 0000000..05893af --- /dev/null +++ b/bk.nim @@ -0,0 +1,62 @@ +import json, os, times, unicode, uri +import cligen +import bakalari + +type + Config = object + website: string + username: string + refreshToken: string + +const + defaultConfigFile = getConfigDir() / "bk.json" + +proc loadConfig(configFile = defaultConfigFile): Config = + defaultConfigFile.readFile.parseJson.to(Config) + +proc saveConfig(configFile = defaultConfigFile, config: Config) = + configFile.writeFile((%*config).pretty) + +proc signin( + website: string, + username: string, + password: string, + configFile = defaultConfigFile, + ) = + let + bakalari = newBakalari(website.parseUri, username, password) + config = Config( + website: website, + username: username, + refreshToken: bakalari.refreshToken, + ) + configFile.saveConfig(config) + +proc rozvrh( + configFile = defaultConfigFile, + date = "", + oneDay = false, + permanent = false, + ) = + var config = configFile.loadConfig + let bakalari = newBakalari(config.website.parseUri, config.refreshToken) + config.refreshToken = bakalari.refreshToken + configFile.saveConfig(config) + let timetable = bakalari.timetable(permanent) + for day in timetable.days: + stdout.writeLine day.date.format("yyyyMMdd") + for lesson in day.lessons: + var line = "" + line &= lesson.hour.number + line &= ". " + line &= lesson.hour.beginTime.align(5, '0'.Rune) + line &= "-" + line &= lesson.hour.endTime.align(5, '0'.Rune) + line &= " " + line &= lesson.subject.abbrev.align(4) + line &= " " + line &= lesson.teacher.abbrev.align(4) + line &= " " + line &= lesson.room.abbrev.align(4) + stdout.writeLine line +dispatchMulti([signin], [rozvrh]) diff --git a/bk.nims b/bk.nims new file mode 100644 index 0000000..fc86e83 --- /dev/null +++ b/bk.nims @@ -0,0 +1 @@ +--define:ssl