From a740761a8722083096323e6ec70b798d6f1148db Mon Sep 17 00:00:00 2001 From: Zhaoyang Li Date: Sun, 17 Feb 2019 22:54:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E5=AD=A6=E5=A0=822018?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Background Tasks/BackgroundTasks.cs | 3 - Class Library/Appointment.cs | 44 +-- Class Library/Class Library.csproj | 3 - Class Library/DataAccess.cs | 183 ++++++---- Class Library/Record.cs | 128 ++++--- Class Library/Remote.cs | 408 ++++------------------ README.md | 4 +- Release Notes/version-history.txt | 5 +- Tsinghua UWP/MainPage.xaml | 4 - Tsinghua UWP/MainPage.xaml.cs | 12 +- Tsinghua UWP/Package.StoreAssociation.xml | 19 +- Tsinghua UWP/Package.appxmanifest | 2 +- Tsinghua UWP/Tsinghua UWP.csproj | 2 +- 13 files changed, 294 insertions(+), 523 deletions(-) diff --git a/Background Tasks/BackgroundTasks.cs b/Background Tasks/BackgroundTasks.cs index 447e3dc..0b1504a 100644 --- a/Background Tasks/BackgroundTasks.cs +++ b/Background Tasks/BackgroundTasks.cs @@ -78,9 +78,6 @@ public async void Run(IBackgroundTaskInstance taskInstance) { try { await Appointment.updateDeadlines(); } catch (Exception) { } - try { - if (goRemote) { await Appointment.updateLectures(); } - } catch (Exception) { } deferral.Complete(); diff --git a/Class Library/Appointment.cs b/Class Library/Appointment.cs index 8f31d0d..cac9875 100644 --- a/Class Library/Appointment.cs +++ b/Class Library/Appointment.cs @@ -95,9 +95,11 @@ public static async Task updateDeadlines() { foreach (var i in to_be_inserted) { Debug.WriteLine("[updateDeadlines] inserting " + i.Subject); + if (i.StartTime - DateTime.Now < TimeSpan.FromHours(7)) { + // WTF ?? Debug.WriteLine("[updateDeadlines] ignoring " + i.Subject); - continue; + // continue; } await ddl_cal.SaveAppointmentAsync(i); } @@ -140,25 +142,6 @@ public static async Task updateCalendar() { Debug.WriteLine("[Appointment] calendar finish"); } - public static async Task updateLectures() { //always force remote - Debug.WriteLine("[Appointment] lecture begin"); - - //TODO: possible duplication, lock? - - var lectures = await Remote.getHostedLectures(); - - //get Calendar object - AppointmentCalendar cal = await getAppointmentCalendar(lec_cal_name, lec_storedKey); - - await deleteAllAppointments(cal); - - foreach (var lec in lectures) { - await cal.SaveAppointmentAsync(getAppointment(lec)); - } - - Debug.WriteLine("[Appointment] lecture finish"); - } - public static async Task updateTimetable(bool forceRemote = false) { Debug.WriteLine("[Appointment] update start"); @@ -211,27 +194,8 @@ private static Windows.ApplicationModel.Appointments.Appointment getAppointment( a.StartTime = DateTime.Parse(e.nq + " " + e.kssj); a.Duration = DateTime.Parse(e.nq + " " + e.jssj) - a.StartTime; - // 修正考试时间 12 小时制 - if (e.fl == "考试") { - if (a.StartTime.Hour < 8) { - a.StartTime += TimeSpan.FromHours(12); - } - a.Subject += "考试"; - } - a.AllDay = false; - return a; - } - private static Windows.ApplicationModel.Appointments.Appointment getAppointment(Lecture l) { - var a = new Windows.ApplicationModel.Appointments.Appointment(); - a.Subject = l.summary; - a.Location = l.location; - a.StartTime = DateTime.Parse(l.dtstart); - a.Duration = DateTime.Parse(l.dtend) - a.StartTime; - a.DetailsKind = AppointmentDetailsKind.PlainText; - a.Details = l.description; a.AllDay = false; - a.BusyStatus = AppointmentBusyStatus.Free; return a; } @@ -240,7 +204,7 @@ private static Windows.ApplicationModel.Appointments.Appointment getAppointment( Regex re = new Regex("&[^;]+;"); a.Subject = re.Replace(e.name, " "); a.Location = re.Replace(e.course, " "); - a.StartTime = DateTime.Parse(e.ddl + " 23:59"); + a.StartTime = DateTime.Parse(e.ddl); a.AllDay = false; a.BusyStatus = e.hasBeenFinished ? AppointmentBusyStatus.Free : AppointmentBusyStatus.Tentative; if (e.hasBeenFinished) diff --git a/Class Library/Class Library.csproj b/Class Library/Class Library.csproj index 2cf7602..5176a99 100644 --- a/Class Library/Class Library.csproj +++ b/Class Library/Class Library.csproj @@ -114,9 +114,6 @@ - - 1.4.9.5 - 5.1.0 diff --git a/Class Library/DataAccess.cs b/Class Library/DataAccess.cs index 712e0b8..b34c381 100644 --- a/Class Library/DataAccess.cs +++ b/Class Library/DataAccess.cs @@ -101,7 +101,7 @@ orderby assignment.daysFromNow() descending return future.Concat(past.Take(limit - futureCount)).ToList(); } - public static async Task> getCourses(bool forceRemote = false) { + public static async Task> getCourses(bool forceRemote = false, string semester = "") { if (isDemo()) { var list = new List(); list.Add(new Course { @@ -116,6 +116,9 @@ public static async Task> getCourses(bool forceRemote = false) { return list; } + if(semester == "") { + semester = (await getSemester()).id; + } if (!forceRemote) { //try memory @@ -135,7 +138,7 @@ public static async Task> getCourses(bool forceRemote = false) { //fetch from remote - var _courses = await Remote.getRemoteCourseList(); + var _courses = await Remote.getRemoteCourseList(semester); courses = _courses; writeCache(COURSES_FILENAME, JSON.stringify(_courses)); @@ -144,61 +147,122 @@ public static async Task> getCourses(bool forceRemote = false) { } public static async Task getTimetable(bool forceRemote = false) { - if (isDemo()) { - var table = new Timetable(); - - var start = DateTime.Now.AddDays(-20); - while (start.DayOfWeek != DayOfWeek.Monday) - start = start.AddDays(-1); - - for (var i = 0; i < 10; i++) { - table.Add(new Event { - nr = "形式语言与自动机", - dd = "六教 6A301", - nq = start.AddDays(i * 7 + 2).ToString("yyyy-MM-dd"), - kssj = "08:00", - jssj = "09:35" - }); + { + if (isDemo()) { + var table = new Timetable(); + + var start = DateTime.Now.AddDays(-20); + while (start.DayOfWeek != DayOfWeek.Monday) + start = start.AddDays(-1); + + for (var i = 0; i < 10; i++) { + table.Add(new Event { + nr = "形式语言与自动机", + dd = "六教 6A301", + nq = start.AddDays(i * 7 + 2).ToString("yyyy-MM-dd"), + kssj = "08:00", + jssj = "09:35" + }); + + table.Add(new Event { + nr = "高级数据结构", + dd = "六教 6A301", + nq = start.AddDays(i * 7 + 2).ToString("yyyy-MM-dd"), + kssj = "09:50", + jssj = "11:25" + }); + + table.Add(new Event { + nr = "操作系统", + dd = "六教 6A303", + nq = start.AddDays(i * 7 + 3).ToString("yyyy-MM-dd"), + kssj = "09:50", + jssj = "11:25" + }); + + table.Add(new Event { + nr = "概率论与数理统计", + dd = "六教 6C102", + nq = start.AddDays(i * 7 + 4).ToString("yyyy-MM-dd"), + kssj = "15:20", + jssj = "16:55" + }); + + table.Add(new Event { + nr = "概率论与数理统计", + dd = "一教 104", + nq = start.AddDays(i * 7 + 1).ToString("yyyy-MM-dd"), + kssj = "13:30", + jssj = "15:05" + }); + } + return table; + } + } + { + //fetch from remote + string[] 大节开始时间 = { + "", + "8:00", + "9:50", + "13:30", + "15:20", + "17:05", + "19:20" + }; + int[] 大节开始小节 = { 0, 1, 3, 6, 8, 10, 12 }; + string[] 小节结束时间 = { + "", + "8:45", + "9:35", + "10:35", + "11:25", + "12:15", + "14:15", + "15:05", + "16:05", + "16:55", + "17:50", + "18:40", + "20:05", + "20:55", + "21:45" + }; - table.Add(new Event { - nr = "高级数据结构", - dd = "六教 6A301", - nq = start.AddDays(i * 7 + 2).ToString("yyyy-MM-dd"), - kssj = "09:50", - jssj = "11:25" - }); + var table = new Timetable(); - table.Add(new Event { - nr = "操作系统", - dd = "六教 6A303", - nq = start.AddDays(i * 7 + 3).ToString("yyyy-MM-dd"), - kssj = "09:50", - jssj = "11:25" - }); + bool[] bools = { false, true }; + foreach (bool getNextSemester in bools) { + + var semester = await getSemester(forceRemote, getNextSemester); + var start = DateTime.Parse(semester.startDate); + + foreach (var course in await getCourses(forceRemote, semester.id)) { + Debug.WriteLine("[getAllDeadlines] Remote " + course.name); + var id = course.id; + try { + var detail = await Remote.getRemoteCourseDetail(course.id); + foreach (var segment in detail) { + for (int weekOffset = 0; weekOffset < segment.skzc.Length; ++weekOffset) { + if (segment.skzc[weekOffset] == '1') { + table.Add(new Event { + nr = course.name, + dd = segment.skdd, + nq = start.AddDays(weekOffset * 7 + segment.skxq - 1).ToString("yyyy-MM-dd"), + kssj = 大节开始时间[segment.skjc], + jssj = 小节结束时间[大节开始小节[segment.skjc] + segment.skxs - 1] + }); + } + } + } + } catch { } + } + } - table.Add(new Event { - nr = "概率论与数理统计", - dd = "六教 6C102", - nq = start.AddDays(i * 7 + 4).ToString("yyyy-MM-dd"), - kssj = "15:20", - jssj = "16:55" - }); - table.Add(new Event { - nr = "概率论与数理统计", - dd = "一教 104", - nq = start.AddDays(i * 7 + 1).ToString("yyyy-MM-dd"), - kssj = "13:30", - jssj = "15:05" - }); - } + Debug.WriteLine("[getTimetable] Returning remote"); return table; } - - //fetch from remote - var _remoteTimetable = await Remote.getRemoteTimetable(); - Debug.WriteLine("[getTimetable] Returning remote"); - return _remoteTimetable; } public static async Task getSemester(bool forceRemote = false, bool getNextSemester = false) { @@ -255,6 +319,9 @@ public static async Task getSemester(bool forceRemote = false, bool ge return __semesters.nextSemester; } Debug.WriteLine("[getCalendar] Returning cache"); + if (getNextSemester) { + return semesters.nextSemester; + } return __semesters.currentSemester; } } @@ -276,6 +343,9 @@ public static async Task getSemester(bool forceRemote = false, bool ge writeCache(SEMESTERS_FILENAME, JSON.stringify(semesters)); Debug.WriteLine("[getCalendar] Returning remote"); + if (getNextSemester) { + return semesters.nextSemester; + } return semesters.currentSemester; } @@ -331,16 +401,11 @@ static public async Task> getAllDeadlines(bool forceRemote = fals foreach (var course in await getCourses(forceRemote)) { Debug.WriteLine("[getAllDeadlines] Remote " + course.name); var id = course.id; - try - { + try { List __deadlines; - if (course.isNew) - __deadlines = await Remote.getRemoteHomeworkListNew(id); - else - __deadlines = await Remote.getRemoteHomeworkList(id); + __deadlines = await Remote.getRemoteHomeworkList(id); _deadlines = _deadlines.Concat(__deadlines).ToList(); - } - catch { } + } catch { } } diff --git a/Class Library/Record.cs b/Class Library/Record.cs index 556571d..8e90abf 100644 --- a/Class Library/Record.cs +++ b/Class Library/Record.cs @@ -23,8 +23,6 @@ public class Password { public class Course { public string id; public string name; - public bool isNew; //uses (`learn.cic` or `learn`?) .tsinghua.edu.cn - public string semester; override public string ToString() { return "#" + id + ": " + name; @@ -36,7 +34,6 @@ public class Deadline { public string name; public string ddl; public string course; - public string detail; public bool hasBeenFinished; public bool hasBeenToasted() { string toasted = ""; @@ -77,7 +74,7 @@ public void mark_as_toasted() { } public double daysFromNow() { - return (DateTime.Parse(ddl + " 23:59") - DateTime.Now).TotalDays; + return (DateTime.Parse(ddl) - DateTime.Now).TotalDays; } public string timeLeft() { @@ -85,11 +82,11 @@ public string timeLeft() { } public bool isPast() { - return DateTime.Parse(ddl + " 23:59") < DateTime.Now; + return DateTime.Parse(ddl) < DateTime.Now; } public string timeLeftChinese() { - TimeSpan timeDelta = DateTime.Parse(ddl + " 23:59") - DateTime.Now; + TimeSpan timeDelta = DateTime.Parse(ddl) - DateTime.Now; var daysLeft = timeDelta.TotalDays; string timeLeft = ""; @@ -108,13 +105,13 @@ public string timeLeftChinese() { timeLeft = "即将到期!"; } else if (daysLeft > -1) { var d = (-timeDelta.Hours); - timeLeft = "已经过去 " + d.ToString() + " 小时"; + timeLeft = "已过期 " + d.ToString() + " 小时"; } else if (daysLeft > -10) { var d = (-timeDelta.Days); - timeLeft = "已经过去 " + d.ToString() + " 天"; + timeLeft = "已过期 " + d.ToString() + " 天"; } else { var d = Math.Round(timeDelta.TotalDays / -7); - timeLeft = "已经过去 " + d.ToString() + " 周"; + timeLeft = "已过期 " + d.ToString() + " 周"; } @@ -148,71 +145,92 @@ public string getWeekName() { // the following classes are generated from JSON by Visual Studio, // for JSON parser only - public class CourseAssignmentsRootobject { - public Resultlist[] resultList { get; set; } - } - - public class Resultlist { - public Coursehomeworkrecord courseHomeworkRecord { get; set; } - public Coursehomeworkinfo courseHomeworkInfo { get; set; } - } - - public class Coursehomeworkrecord { - public string status { get; set; } - } - public class Coursehomeworkinfo { - public int homewkId { get; set; } - public long endDate { get; set; } - public string title { get; set; } - public string detail { get; set; } - public string courseId { get; set; } + public class RemoteSemester { + public string xnxq { get; set; } + public string xnxqmc { get; set; } + public string kssj { get; set; } + public string jssj { get; set; } + public string id { get; set; } } - public class SemestersRootObject { - public Currentteachingweek currentTeachingWeek { get; set; } - public Semester currentSemester { get; set; } - public string currentDate { get; set; } - public Semester nextSemester { get; set; } - } - - public class Currentteachingweek { - public int teachingWeekId { get; set; } - public string weekName { get; set; } - public string beginDate { get; set; } - public string endDate { get; set; } - public string semesterId { get; set; } + public RemoteSemester result { get; set; } + public string message { get; set; } + public RemoteSemester[] resultList { get; set; } } - public class Timetable : List { } public class Event { public string dd { get; set; } - public string fl { get; set; } - public int grrlID { get; set; } public string jssj { get; set; } public string kssj { get; set; } public string nq { get; set; } public string nr { get; set; } - public string sfSjtz { get; set; } - public string skjc { get; set; } - public string sm { get; set; } + + } + + public class RemoteCourseRootObject { + public string currentUser { get; set; } + public string message { get; set; } + public RemoteCourse[] resultList { get; set; } } - public class Lectures { - public Lecture[] Property1 { get; set; } + public class RemoteCourse { + + public string wlkcid { get; set; } + public string kcm { get; set; } + public string kch { get; set; } + public int kxh { get; set; } + + public string jsm { get; set; } + + } + + public class HomeworkDetailRootobject { + public string result { get; set; } + public HomeworkDetailObject objects { get; set; } + } + + public class HomeworkDetailObject { + public string iTotalDisplayRecords { get; set; } + public HomeworkDetailAadata[] aaData { get; set; } + } + + public class HomeworkDetailAadata { + public long jzsj { get; set; } + public string jzsjStr { get; set; } + + public string bt { get; set; } + + public string wlkcid { get; set; } + + public string zyid { get; set; } + } - public class Lecture { - public string description { get; set; } - public string summary { get; set; } - public string location { get; set; } - public string dtstart { get; set; } - public string dtend { get; set; } - public long uid { get; set; } + public class CourseDetail { + public string id { get; set; } + public string wlkcid { get; set; } + + public string xnxq { get; set; } + public string kch { get; set; } + public string kxh { get; set; } + + public string skzc { get; set; } + public int skxq { get; set; } + public int skjc { get; set; } + public int skxs { get; set; } + + public string zcms { get; set; } + + public string skdd { get; set; } + + public string jxlh { get; set; } + public string jash { get; set; } + } } \ No newline at end of file diff --git a/Class Library/Remote.cs b/Class Library/Remote.cs index 4f41276..cf12d9c 100644 --- a/Class Library/Remote.cs +++ b/Class Library/Remote.cs @@ -1,5 +1,4 @@ -using HtmlAgilityPack; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -20,173 +19,56 @@ public static class Remote { // exposes access to remote objects - public static async Task getRemoteTimetable() { + public static async Task> getRemoteCourseDetail(string courseId) { Debug.WriteLine("[getRemoteTimetable] start"); await login(); - var ticket = await POST( - "http://learn.cic.tsinghua.edu.cn:80/gnt", - "appId=ALL_ZHJW"); + var page = await GET($"http://learn2018.tsinghua.edu.cn/b/kc/v_wlkc_xk_sjddb/detail?id={courseId}"); + var parsed = JSON.parse(page); - //await 十1s(); //cross-domain tickets needs some time to take effect - - bool outside_campus_network = false; - try { - var zhjw = await GET( - $"http://zhjw.cic.tsinghua.edu.cn/j_acegi_login.do?url=/&ticket={ticket}"); - } catch (System.Runtime.InteropServices.COMException e) { - if (e.Message.IndexOf("403") == -1) - throw e; - Debug.WriteLine("[getRemoteTimetable] outside campus network"); - //throw new NeedCampusNetworkException(); - - outside_campus_network = true; - } - - - - if (outside_campus_network) { - //connect via sslvpn - await logoutSSLVPN(); - await loginSSLVPN(); - - await login(); - - ticket = await POST( - "http://learn.cic.tsinghua.edu.cn:80/gnt", - "appId=ALL_ZHJW"); - - await 十1s(); - - var ticketPage = await GET( - $"https://sslvpn.tsinghua.edu.cn/,DanaInfo=zhjw.cic.tsinghua.edu.cn+j_acegi_login.do?url=/&ticket={ticket}"); - - Timetable timetable = new Timetable(); - - for (int i = -6; i <= 4; i += 2) { - var roles = new List { "bks", "yjs" }; - foreach (var role in roles) - { - try - { - string page; - try - { - page = await get_calendar_sslvpn_page( - DateTime.Now.AddMonths(i).AddDays(1).ToString("yyyyMMdd"), - DateTime.Now.AddMonths(i + 2).ToString("yyyyMMdd"), - role - ); - } - catch (Exception) - { - page = await get_calendar_sslvpn_page( - DateTime.Now.AddMonths(i).AddDays(1).ToString("yyyyMMdd"), - DateTime.Now.AddMonths(i + 2).ToString("yyyyMMdd"), - role - ); - } - var set_to_be_appended = parseTimetablePage(page); - foreach (var _____ in set_to_be_appended) - { - timetable.Add(_____); - } - } - catch (Exception) { } - } - } - - logoutSSLVPN(); - - Debug.WriteLine("[getRemoteTimetable] returning sslvpn"); - return timetable; - } else { //TODO: duplicate code - - //connect directly - - Timetable timetable = new Timetable(); - for (int i = -6; i <= 4; i += 2) { - var roles = new List { "bks", "yjs" }; - foreach (var role in roles) - { - try - { - string page; - try - { - page = await get_calendar_page( - DateTime.Now.AddMonths(i).AddDays(1).ToString("yyyyMMdd"), - DateTime.Now.AddMonths(i + 2).ToString("yyyyMMdd"), - role - ); - } - catch (Exception) - { - page = await get_calendar_page( - DateTime.Now.AddMonths(i).AddDays(1).ToString("yyyyMMdd"), - DateTime.Now.AddMonths(i + 2).ToString("yyyyMMdd"), - role - ); - } - var set_to_be_appended = parseTimetablePage(page); - foreach (var _____ in set_to_be_appended) - { - timetable.Add(_____); - } - } - catch (Exception) { } - } - } - - Debug.WriteLine("[getRemoteTimetable] returning direct"); - return timetable; - } - } - static async Task get_calendar_page(string starting_date, string ending_date, string role) { - Debug.WriteLine($"[get_calendar_page] {role}, {starting_date}-{ending_date}"); - var stamp = (long)UnixTime().TotalMilliseconds; - return await GET( - $"http://zhjw.cic.tsinghua.edu.cn/jxmh.do?m={role}_jxrl_all&p_start_date={starting_date}&p_end_date={ending_date}&jsoncallback=_&_={stamp}"); - } - static async Task get_calendar_sslvpn_page(string starting_date, string ending_date, string role) { - Debug.WriteLine($"[get_calendar_sslvpn_page] {role}, {starting_date}-{ending_date}"); - var stamp = (long)UnixTime().TotalMilliseconds; - return await GET( - $"https://sslvpn.tsinghua.edu.cn/,DanaInfo=zhjw.cic.tsinghua.edu.cn,CT=js+jxmh.do?m={role}_jxrl_all&p_start_date={starting_date}&p_end_date={ending_date}&jsoncallback=_&_={stamp}"); + Debug.WriteLine("[getRemoteTimetable] returning direct"); + return parsed.ToList(); } + + public static async Task> getRemoteHomeworkList(string courseId) { await login(); - return parseHomeworkListPage(await getHomeworkListPage(courseId)); + var result = new List { }; + result = result.Concat(await parseHomeworkListPage(await getHomeworkListPage(courseId, "Wj"))).ToList(); + return result; } - public static async Task> getRemoteHomeworkListNew(string courseId) { - await login(); - return await parseHomeworkListPageNew(await getHomeworkListPageNew(courseId)); - } - public static async Task> getRemoteCourseList() { + public static async Task> getRemoteCourseList(string semester) { await login(); - return parseCourseList(await getCourseListPage()); + return parseCourseList(await getCourseListPage(semester)); } public static async Task getHostedSemesters() { return JSON.parse(await GET(hostedCalendarUrl)); } - public static async Task> getHostedLectures() { - var raw = await GET(hostedLectureUrl); - var parsed = JSON.parse>(raw); - return parsed; - } - public static async Task getRemoteSemesters() { await login(); - var _remoteCalendar = parseSemestersPage(await getCalendarPage()); + await 十1s(); + var _remoteCalendar = parseSemestersPage(await getSemesterPage()); return new Semesters { - currentSemester = _remoteCalendar.currentSemester, - nextSemester = _remoteCalendar.nextSemester + currentSemester = new Semester { + id = _remoteCalendar.result.id, + semesterEname = Regex.Replace(Regex.Replace(Regex.Replace(_remoteCalendar.result.id, "-1$", "-Autumn"), "-2$", "-Spring"), "-3$", "-Summer"), + semesterName = _remoteCalendar.result.xnxqmc, + startDate = _remoteCalendar.result.kssj, + endDate = _remoteCalendar.result.jssj + }, + nextSemester = new Semester { + id = _remoteCalendar.resultList[0].id, + semesterEname = Regex.Replace(Regex.Replace(Regex.Replace(_remoteCalendar.resultList[0].id, "-1$", "-Autumn"), "-2$", "-Spring"), "-3$", "-Summer"), + semesterName = _remoteCalendar.resultList[0].xnxqmc, + startDate = _remoteCalendar.resultList[0].kssj, + endDate = _remoteCalendar.resultList[0].jssj + }, }; } @@ -240,36 +122,25 @@ private static async Task login(bool useLocalSettings = true, string userna try { string loginResponse; - //login to learn.tsinghua.edu.cn + //login to learn2018.tsinghua.edu.cn loginResponse = await POST( loginUri, - $"leixin1=student&userid={username}&userpass={password}"); + $"i_user={username}&i_pass={password}&atOnce=true"); //check if successful - var alertInfoGroup = Regex.Match(loginResponse, @"window.alert\(""(.+)""\);").Groups; - if (alertInfoGroup.Count > 1) { - throw new LoginException(alertInfoGroup[1].Value.Replace("\\r\\n", "\n")); - } - if (loginResponse.IndexOf(@"window.location = ""loginteacher_action.jsp"";") == -1) { - throw new ParsePageException("login_redirect"); - } + var ticketGroup = Regex.Match(loginResponse, @"window.location.replace\(""(.+)""\);").Groups; - //get iframe src - HtmlDocument htmlDoc = new HtmlDocument(); - htmlDoc.LoadHtml(await GET(courseListUrl)); + var redirectUrl = ticketGroup[1].Value; - string iframeSrc; - try { - iframeSrc = htmlDoc.DocumentNode.Descendants("iframe")/*MAGIC*/.First().Attributes["src"].Value; - } catch (Exception) { - throw new ParsePageException("find_cic_iframe"); + var redirectStatus = Regex.Match(redirectUrl, @"status=([^&]+)(&|$)").Groups[1].Value; + var redirectTicket = Regex.Match(redirectUrl, @"ticket=([^&]+)(&|$)").Groups[1].Value; + if (redirectStatus != "SUCCESS") { + throw new LoginException("登录失败:" + redirectStatus); } + await GET($"http://learn2018.tsinghua.edu.cn/b/j_spring_security_thauth_roaming_entry?ticket={redirectTicket}"); - //login to learn.cic.tsinghua.edu.cn - await 十1s(); - await GET(iframeSrc); } catch (Exception e) { occupied = false; Debug.WriteLine("[login] unsuccessful"); @@ -286,73 +157,11 @@ private static async Task login(bool useLocalSettings = true, string userna return 0; } - private static async Task logoutSSLVPN() { - await GET(logoutSslvpnUrl); - Debug.WriteLine("[logoutSSLVPN] finish"); - } - - private static async Task loginSSLVPN() { - - Debug.WriteLine("[loginSSLVPN] start"); - - //retrieve username and password - if (DataAccess.credentialAbsent()) { - throw new LoginException("没有指定用户名和密码"); - } - var username = DataAccess.getLocalSettings()["username"].ToString(); - - var vault = new Windows.Security.Credentials.PasswordVault(); - var password = vault.Retrieve("Tsinghua_Learn_Website", username).Password; - - - //login to sslvpn.tsinghua.edu.cn - var loginResponse = await POST( - loginSslvpnUri, - $"tz_offset=480&username={username/*should be numeral ID*/}&password={password}&realm=ldap&btnSubmit=登录"); - - - //another sslvpn session exist? - if (loginResponse.IndexOf("btnContinue") != -1) { - Debug.WriteLine("[loginSSLVPN] another sslvpn session exist"); - - HtmlDocument htmlDoc = new HtmlDocument(); - htmlDoc.LoadHtml(loginResponse); - string formDataStr = htmlDoc.GetElementbyId("DSIDFormDataStr").Attributes["value"].Value; - - loginResponse = await POST( - loginSslvpnUri, - $"btnContinue=继续会话&FormDataStr={Uri.EscapeDataString(formDataStr)}"); - } - - //get xauth token - var xsauthGroups = Regex.Match(loginResponse, @"name=""xsauth"" value=""([^""]+)""").Groups; - if (xsauthGroups.Count < 2) { - throw new ParsePageException("find_xsauth_from_sslvpn"); - } - var xsauth = xsauthGroups[1]; - - //second step, invoking xsauth token - var timestamp = UnixTime().TotalSeconds; - - loginResponse = await POST( - loginSslvpnCheckUri, - $"xsauth={xsauth}&tz_offset=480&clienttime={timestamp}&url=&activex_enabled=0&java_enabled=0&power_user=0&grab=1&browserproxy=&browsertype=&browserproxysettings=&check=yes"); - - - - Debug.WriteLine("[loginSSLVPN] finish"); - return 0; - } private static DateTime lastLogin = DateTime.MinValue; private static string lastLoginUsername = ""; private static int LOGIN_TIMEOUT_MINUTES = 5; - - private static string loginSslvpnUri = "https://sslvpn.tsinghua.edu.cn/dana-na/auth/url_default/login.cgi"; - private static string logoutSslvpnUrl = "https://sslvpn.tsinghua.edu.cn/dana-na/auth/logout.cgi"; - private static string loginSslvpnCheckUri = "https://sslvpn.tsinghua.edu.cn/dana/home/starter0.cgi"; - private static string loginSslvpnCheckUriCheck = "https://sslvpn.tsinghua.edu.cn/dana/home/starter0.cgi?check=yes"; - private static string loginUri = "https://learn.tsinghua.edu.cn/MultiLanguage/lesson/teacher/loginteacher.jsp"; + private static string loginUri = "https://id.tsinghua.edu.cn/do/off/ui/auth/login/post/bb5df85216504820be7bba2b0ae1535b/0?/login.do"; @@ -361,26 +170,21 @@ private static async Task loginSSLVPN() { // remote object URLs and wrappers - private static string courseListUrl = "http://learn.tsinghua.edu.cn/MultiLanguage/lesson/student/MyCourse.jsp?language=cn"; + private static string hostedCalendarUrl = "https://static.nullspace.cn/thuCalendar.json"; private static string hostedLectureUrl = "http://vultr.nullspace.cn:8000/test.json"; public static string helpUrl = "https://static.nullspace.cn/thuUwpHelp.html"; - private static async Task getHomeworkListPage(string courseId) { - return await GET($"http://learn.tsinghua.edu.cn/MultiLanguage/lesson/student/hom_wk_brw.jsp?course_id={courseId}"); - } - - private static async Task getHomeworkListPageNew(string courseId) { - var timestamp = UnixTime().TotalMilliseconds; - string url = $"http://learn.cic.tsinghua.edu.cn/b/myCourse/homework/list4Student/{courseId}/0?_={timestamp}"; - return await GET(url); + private static async Task getHomeworkListPage(string courseId, string category) { + var payload = $"aoData=[{{\"name\":\"wlkcid\",\"value\":\"{courseId}\"}},{{\"name\":\"iDisplayStart\",\"value\":0}},{{\"name\":\"iDisplayLength\",\"value\":-1}}]"; + return await POST($"http://learn2018.tsinghua.edu.cn/b/wlxt/kczy/zy/student/zyList" + category, payload); } - private static async Task getCourseListPage() { - return await GET(courseListUrl); + private static async Task getCourseListPage(string semester) { + return await GET("http://learn2018.tsinghua.edu.cn/b/wlxt/kc/v_wlkc_xs_xkb_kcb_extend/student/loadCourseBySemesterId/" + semester); } - private static async Task getCalendarPage() { - return await GET("http://learn.cic.tsinghua.edu.cn/b/myCourse/courseList/getCurrentTeachingWeek"); + private static async Task getSemesterPage() { + return await GET("http://learn2018.tsinghua.edu.cn/b/kc/zhjw_v_code_xnxq/getCurrentAndNextSemester"); } @@ -392,70 +196,19 @@ private static async Task getCalendarPage() { // parse HTML or JSON, and return corresponding Object - private static List parseHomeworkListPage(string page) { - try { - HtmlDocument htmlDoc = new HtmlDocument(); - htmlDoc.LoadHtml(page); - - string _name, _due, _course; - - _course = htmlDoc.DocumentNode.Descendants("td")/*MAGIC*/.First().InnerText; - _course = _course.Trim(); - _course = _course.Substring(6/*MAGIC*/); - _course = WebUtility.HtmlDecode(_course); - - HtmlNode[] nodes = htmlDoc.DocumentNode.Descendants("tr")/*MAGIC*/.ToArray(); - - - List deadlines = new List(); - Regex re = new Regex("&[^;]+;"); - for (int i = 4/*MAGIC*/; i < nodes.Length - 1/*MAGIC*/; i++) { - HtmlNode node = nodes[i]; - - var tds = node.Descendants("td"); - - var _isFinished = (tds.ElementAt(3/*MAGIC*/).InnerText.Trim() == "已经提交"); - - _due = tds.ElementAt(2/*MAGIC*/).InnerText; - - var link_to_detail = node.Descendants("a")/*MAGIC*/.First(); - _name = link_to_detail.InnerText; - _name = WebUtility.HtmlDecode(_name); - - var _href = link_to_detail.Attributes["href"].Value; - var _id = Regex.Match(_href, @"[^_]id=(\d+)").Groups[1].Value; - - deadlines.Add(new Deadline { - name = re.Replace(_name, " "), - ddl = _due, - course = re.Replace(_course, " "), - hasBeenFinished = _isFinished, - id = "@" + _id - }); - } - return deadlines; - } catch (Exception) { - throw new ParsePageException("AssignmentList"); - } - - } - - private static async Task> parseHomeworkListPageNew(string page) { - - List deadlines = new List(); - + private static async Task> parseHomeworkListPage(string page) { + var deadlines = new List { }; string _course = ""; - var root = JSON.parse(page); Regex re = new Regex("&[^;]+;"); - foreach (var item in root.resultList) { - var _isFinished = (item.courseHomeworkRecord.status != "0" /*MAGIC*/); - var _dueTimeStamp = item.courseHomeworkInfo.endDate; - var _dueDate = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).ToLocalTime().AddMilliseconds(_dueTimeStamp).Date; - string _due = $"{_dueDate.Year}-{_dueDate.Month}-{_dueDate.Day}"; + var parsed = JSON.parse(page.Replace("\"object\":", "\"objects\":")); + foreach (var a in parsed.objects.aaData){ + var _isFinished = false; + + string _due = a.jzsjStr; - string _name = item.courseHomeworkInfo.title; - string _courseId = item.courseHomeworkInfo.courseId; + string _name = a.bt; + string _courseId = a.wlkcid; if (_course == "") _course = _courseId; @@ -474,52 +227,21 @@ private static async Task> parseHomeworkListPageNew(string page) ddl = _due, course = re.Replace(_course, " "), hasBeenFinished = _isFinished, - id = "_" + item.courseHomeworkInfo.homewkId + id = a.wlkcid + "_" + a.zyid }); } return deadlines; } private static List parseCourseList(string page) { - try { - List courses = new List(); - - HtmlDocument htmlDoc = new HtmlDocument(); - htmlDoc.LoadHtml(page); - var tables = htmlDoc.DocumentNode.Descendants("table"); - var links = tables/*MAGIC*/.Take(3).Last()/*MAGIC*/.Descendants("a")/*MAGIC*/.ToArray(); - - foreach (var link in links) { - string _name = link.InnerText.Trim(); - string _url = link.Attributes["href"].Value; - if (_url.Contains("teacher")) - { - continue; - } - var match = Regex.Match(_name, "(.+?)\\((\\d+)\\)\\((.+?)\\)"); - string _semester = match.Groups[3].Value; - _name = match.Groups[1].Value; - bool _isNew = false; - string _id = ""; - - if (_url.StartsWith("http://learn.cic.tsinghua.edu.cn/")) { - _isNew = true; - _id = Regex.Match(_url, "/([-\\d]+)").Groups[1].Value; - } else { - _isNew = false; - _id = Regex.Match(_url, "course_id=(\\d+)").Groups[1].Value; - } - courses.Add(new Course { - name = _name, - isNew = _isNew, - id = _id, - semester = _semester - }); - } - return courses; - } catch (Exception) { - throw new ParsePageException("CourseList"); + var result = new List { }; + foreach (var c in JSON.parse(page).resultList) { + result.Add(new Course { + id = c.wlkcid, + name = c.kcm, + }); } + return result; } private static SemestersRootObject parseSemestersPage(string page) { @@ -596,4 +318,4 @@ public static string stringify(object jsonObject) { } } } -} +} \ No newline at end of file diff --git a/README.md b/README.md index ae52c1c..99ce854 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ 大二时的练手之作,代码相当杂乱(捂脸)。也就 Class Library 工程里的 Remote 类还算有点复用的价值: * C# 版本的清华大学网络学堂爬虫 - * 支持 2001 版(learn)、2015 版(learn.cic) - * 基于 [HtmlAgilityPack](https://www.nuget.org/packages/HtmlAgilityPack/) + * ~~支持 2001 版(learn)、2015 版(learn.cic),基于 [HtmlAgilityPack](https://www.nuget.org/packages/HtmlAgilityPack/)~~ + * 支持 2018 版(learn2018) ## 友情链接 基于本项目的其他项目 diff --git a/Release Notes/version-history.txt b/Release Notes/version-history.txt index 2492772..3075ac8 100644 --- a/Release Notes/version-history.txt +++ b/Release Notes/version-history.txt @@ -1,4 +1,7 @@ -版本 1.2.15 +版本 1.3.0 +- [新增] 升级到网络学堂2018版 + +版本 1.2.15 - [修复] 2015版网络学堂部分课程作业导致刷新失败 版本 1.2.14 diff --git a/Tsinghua UWP/MainPage.xaml b/Tsinghua UWP/MainPage.xaml index 200d866..f7dfd85 100644 --- a/Tsinghua UWP/MainPage.xaml +++ b/Tsinghua UWP/MainPage.xaml @@ -18,10 +18,6 @@ - - - - diff --git a/Tsinghua UWP/MainPage.xaml.cs b/Tsinghua UWP/MainPage.xaml.cs index 239dae4..5a9ad67 100644 --- a/Tsinghua UWP/MainPage.xaml.cs +++ b/Tsinghua UWP/MainPage.xaml.cs @@ -34,13 +34,8 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) { } private async void update_with_credential() { - updateDeadlinesAsyc(); - updateTimetableAsync(); - Appointment.updateCalendar(); - try { - await Appointment.updateLectures(); - } catch { } + // updateTimetableAsync(); // fix: 重复导入课程表 } private async void update_without_credential() { @@ -48,10 +43,6 @@ private async void update_without_credential() { await Notification.update(calendarOnly: true); await Appointment.updateCalendar(); } catch { } - try { - await Appointment.updateLectures(); - } catch { } - } private async Task changeAccountAsync() { @@ -151,6 +142,7 @@ private void launchHelp() { } private void btnRefreshTimetable_Click(object sender, RoutedEventArgs e) { + Appointment.updateCalendar(); updateTimetableAsync(); } diff --git a/Tsinghua UWP/Package.StoreAssociation.xml b/Tsinghua UWP/Package.StoreAssociation.xml index 6ea061e..c89fc62 100644 --- a/Tsinghua UWP/Package.StoreAssociation.xml +++ b/Tsinghua UWP/Package.StoreAssociation.xml @@ -2,6 +2,7 @@ CN=03155EEE-D512-488E-9665-514F20C1D599 Zhaoyang Li + MSA http://www.w3.org/2001/04/xmlenc#sha256 @@ -364,5 +365,21 @@ - + + + 0.0.0.0 + X64 + 1.2.15.0 + + + 0.0.0.0 + X86 + 1.2.15.0 + + + 0.0.0.0 + Arm + 1.2.15.0 + + \ No newline at end of file diff --git a/Tsinghua UWP/Package.appxmanifest b/Tsinghua UWP/Package.appxmanifest index 51a0884..fa791d3 100644 --- a/Tsinghua UWP/Package.appxmanifest +++ b/Tsinghua UWP/Package.appxmanifest @@ -1,6 +1,6 @@  - + 清华磁贴 diff --git a/Tsinghua UWP/Tsinghua UWP.csproj b/Tsinghua UWP/Tsinghua UWP.csproj index c371d9b..57f69eb 100644 --- a/Tsinghua UWP/Tsinghua UWP.csproj +++ b/Tsinghua UWP/Tsinghua UWP.csproj @@ -20,7 +20,7 @@ False Never x86|x64|arm - D4E46E36BD31AA0079F45F415EBDB91D6EFCD60A + 29BAD16FD86D09168BA0CCC02F2049F938768959 True win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot False