diff --git a/lib/api/helper.dart b/lib/api/helper.dart index 9c46d4e3..ae935856 100644 --- a/lib/api/helper.dart +++ b/lib/api/helper.dart @@ -21,6 +21,7 @@ import 'package:nkust_ap/api/ap_helper.dart'; import 'package:nkust_ap/api/bus_helper.dart'; import 'package:nkust_ap/api/leave_helper.dart'; import 'package:nkust_ap/api/nkust_helper.dart'; +import 'package:nkust_ap/api/inkust_helper.dart'; import 'package:nkust_ap/config/constants.dart'; import 'package:nkust_ap/models/booking_bus_data.dart'; import 'package:nkust_ap/models/bus_violation_records_data.dart'; @@ -352,7 +353,7 @@ class Helper { }) async { if (isExpire()) await login(username: username, password: password); try { - var data = await WebApHelper.instance.coursetable( + var data = await InkustHelper.instance.courseTable( semester.year, semester.value, ); diff --git a/lib/api/inkust_helper.dart b/lib/api/inkust_helper.dart new file mode 100644 index 00000000..6cf98e11 --- /dev/null +++ b/lib/api/inkust_helper.dart @@ -0,0 +1,114 @@ +//dio +import 'package:ap_common/models/course_data.dart'; +import 'package:dio/adapter.dart'; +import 'package:dio/dio.dart'; +import 'package:dio_http_cache/dio_http_cache.dart'; +import 'package:cookie_jar/cookie_jar.dart'; + +//overwrite origin Cookie Manager. +import 'package:nkust_ap/api/private_cookie_manager.dart'; +import "dart:math"; +import 'helper.dart'; +import 'package:nkust_ap/api/parser/inkust_parser.dart'; + +class InkustHelper { + static Dio dio; + static DioCacheManager _manager; + static InkustHelper _instance; + static CookieJar cookieJar; + + static int reLoginReTryCountsLimit = 3; + static int reLoginReTryCounts = 0; + static String loginApiKey = ""; + static String host = "inkusts.nkust.edu.tw"; + static String get coursetableCacheKey => + "${Helper.username}_coursetableCacheKey"; + + static Map ueserRequestData = { + "apiKey": null, + "userId": null, + }; + + bool isLogin = false; + + static InkustHelper get instance { + if (_instance == null) { + _instance = InkustHelper(); + dioInit(); + } + return _instance; + } + + void setProxy(String proxyIP) { + (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = + (client) { + client.findProxy = (uri) { + return "PROXY " + proxyIP; + }; + }; + } + + static dioInit() { + dio = Dio(); + cookieJar = CookieJar(); + if (Helper.isSupportCacheData) { + _manager = DioCacheManager(CacheConfig(baseUrl: "https://${host}")); + dio.interceptors.add(_manager.interceptor); + } + + dio.interceptors.add(PrivateCookieManager(cookieJar)); + + var headerRandom = ['13_6', '12_4', '14_0', '13_1', '13_5']; + final _random = new Random(); + + dio.options.headers['user-agent'] = + 'Mozilla/5.0 (iPhone; CPU iPhone OS ${headerRandom[_random.nextInt(headerRandom.length)]} like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'; + } + + Future> inkustLogin() async { + if (Helper.username == null || Helper.password == null) { + throw NullThrownError; + } + Response res = await dio.post("https://${host}/User/DoLogin2", + data: { + "apiKey": loginApiKey, + "userId": Helper.username, + "userPw": Helper.password, + "userKeep": 0 + }, + options: Options(contentType: Headers.formUrlEncodedContentType)); + + if (res.statusCode == 200 && res.data["success"] == true) { + isLogin = true; + ueserRequestData['apiKey'] = res.data['data']["userKey"]; + ueserRequestData['userId'] = res.data['data']["userIdEncrypt"]; + } + return res.data; + } + + Future courseTable(String years, String semesterValue) async { + if (isLogin != true) { + await inkustLogin(); + } + Options _options; + _options = Options(contentType: Headers.formUrlEncodedContentType); + if (Helper.isSupportCacheData) { + _options = buildConfigurableCacheOptions( + options: _options, + maxAge: Duration(hours: 1), + primaryKey: "${coursetableCacheKey}_${years}_${semesterValue}"); + } + + var requestData = new Map.from(ueserRequestData); + requestData.addAll({ + 'academicYear': years, + 'academicSms': semesterValue, + }); + Response res = await dio.post("https://${host}/Course/GetStuCourse2", + data: requestData, options: _options); + if (res.data['success'] == false) { + return null; + } + return CourseData.fromJson(inkustCourseTableParser(res.data)); + } +} diff --git a/lib/api/parser/inkust_parser.dart b/lib/api/parser/inkust_parser.dart new file mode 100644 index 00000000..2ba46be1 --- /dev/null +++ b/lib/api/parser/inkust_parser.dart @@ -0,0 +1,69 @@ +Map inkustCourseTableParser(Map data) { + Map result = { + "courses": [], + "coursetable": { + "Monday": [], + "Tuesday": [], + 'Wednesday': [], + 'Thursday': [], + 'Friday': [], + 'Saturday': [], + 'Sunday': [], + 'timeCodes': [], + }, + }; + //reverse data type for more easy to use. + Map _tempDateTimeChange = {}; + + //timeCodes parse + data["data"]["time"].forEach((element) { + result["coursetable"]["timeCodes"].add("第${element["periodName"]}節"); + _tempDateTimeChange.addAll({element["period"]: element}); + }); + + //courses parse + data["data"]["course"].forEach((element) { + result["courses"].add({ + "code": "", + "title": element['courseName'], + "className": element['className'], + "group": element["courseGroup"], + "units": element["courseCredit"], + "hours": element["courseHour"], + "required": element["courseOption"], + "at": element["courseAnnual"], + "times": element["courseTime"], + "location": {"room": element['courseRoom']}, + "instructors": [element['courseTeacher']] + }); + }); + + Map courseWeek = { + "1": 'Monday', + "2": 'Tuesday', + "3": 'Wednesday', + "4": 'Thursday', + "5": 'Friday', + "6": 'Saturday', + "0": 'Sunday', + }; + //coursetable parse + data["data"]["course"].forEach((courseElement) { + courseElement['courseTimeData'].forEach((singleCourseObject) { + result['coursetable'][courseWeek[singleCourseObject['courseWeek']]].add({ + "title": courseElement['courseName'], + "date": { + "startTime": + "${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["begTime"].substring(0, 2)}:${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["begTime"].substring(2, 4)}", + "endTime": + "${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["endTime"].substring(0, 2)}:${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["endTime"].substring(2, 4)}", + "section": + "第${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["periodName"]}節" + }, + "location": {"room": courseElement['courseRoom']}, + "instructors": [courseElement['courseTeacher']] + }); + }); + }); + return result; +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index d04f7241..b81ba1e2 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -20,6 +20,7 @@ import 'package:http/http.dart' as http; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:nkust_ap/api/ap_status_code.dart'; +import 'package:nkust_ap/api/inkust_helper.dart'; import 'package:nkust_ap/models/login_response.dart'; import 'package:nkust_ap/models/models.dart'; import 'package:nkust_ap/pages/announcement/news_admin_page.dart'; @@ -656,6 +657,8 @@ class HomePageState extends State { } } + static const PREF_API_KEY = 'inkust_api_key'; + void _checkFeatureEnable() async { await Future.delayed(Duration(milliseconds: 100)); try { @@ -663,6 +666,10 @@ class HomePageState extends State { await remoteConfig.fetch(expiration: const Duration(seconds: 10)); await remoteConfig.activateFetched(); busEnable = remoteConfig.getBool('bus_enable'); - } catch (e) {} + InkustHelper.loginApiKey = remoteConfig.getString(PREF_API_KEY); + Preferences.setString(PREF_API_KEY, InkustHelper.loginApiKey); + } catch (e) { + InkustHelper.loginApiKey = Preferences.getString(PREF_API_KEY, ''); + } } }