# TKB 預約小工具 ## 目前狀態 : 可能有問題(?) **有人有回報不能預約,有在使用的人請幫我在[這裡](https://strawpoll.com/143gjzwxa)投個票,讓我知道目前的使用情況。** 因為我 TKB 的課程已經過期了沒辦法預約,導致只能靠有使用的人回報給我 bug 與狀況,解決問題會比較緩慢,請見諒,有問題歡迎使用以下 email 聯絡我。 :mailbox: soimportant0318@gmail.com ## 前言 本工具較適合上課狀況比較規律的人使用,也就是說有固定在星期幾上固定的課,當然比較沒規律的人也可以用,只是相對來說可能會比較頻繁的修改程式碼,比較麻煩一點。 有在使用的人請開啟這篇文的變更通知,才能得知最新的更動哦。 ## 使用需求 ### 瀏覽器 Google Chrome 或是 Microsoft Edge(新版) ### 瀏覽器擴充功能 [Custom JavaScript for Websites 2](https://chrome.google.com/webstore/detail/custom-javascript-for-web/ddbjnfjiigjmcpcpkmhogomapikjbjdk) ## 設定 ### 前置步驟(只需要做一次) 1. 安裝擴充功能,務必確認有這個圖示在右上角 ![](https://i.imgur.com/oKUiVO9.png) 2. 登入 TKB,並進到[預約座位的頁面](https://bookseat.tkblearning.com.tw/book-seat/student/bookSeat/index) ![](https://i.imgur.com/zaLXFbE.jpg) 3. 按下擴充功能的那個按鈕會跑出這個頁面 ![](https://i.imgur.com/irSLFZJ.png) 4. 點選 `New RegExp`,把這串貼上去 `https://bookseat.tkblearning.com.tw/book-seat/student/bookSeat/index` 並按下 Add。 5. 接著把下面的程式碼貼到中間的區塊內,並依照下方的程式碼修改步驟開始修改。 ![](https://i.imgur.com/36cL7ZT.png) --- ### 程式碼修改步驟(第一次使用一定要做,之後有需要才要再做) 首先是定義==想要預約的課==以及==想預約的日期及時段==。 ```javascript= { name: "計組與計結", day: [], session: [] }, ``` + `name` 是科目名稱,注意**這裡要是完整的科目名稱,不可以錯字或缺漏字** + `day` 的 `[]` 裡放的是星期幾要預約。 + 星期一放 `1`,星期二放 `2`,以此類推,中間以逗號隔開,星期天要放 `0` + `session` 的 `[]` 裡放的是要哪一天要預約哪個時段。 + 第一個時段放 `"1"`,第二個時段放 `"2"`,以此類推,中間以逗號隔開,不同天的時段也以逗號隔開。 + 有的學堂有 ==`7:40` 開始的超早時段,那個算是第零個時段,放 `"0"`== + **一天不可以預約超過 3 個時段,請自行注意** + 這裡的 `session` 與 `day` 一一對應,請參考下面的範例。 :::success **簡單的格式範例** ```javascript= { name: "計組與計結", day: [0, 2], session: [["1"], ["1", "3"]] } ``` 這的範例代表我要預約計組與計結,時段是==星期日的第一個時段==與==星期二的第一個與第三個時段==。 若還有其他課程,則使用逗號隔開即可,像是以下範例 ```javascript= { name: "計組與計結", day: [0, 2], session: [["1"], ["1", "3"]] }, { name: "線性代數", day: [1], session: [["1"]], }, // 記得要有逗號 // ... 接下去 ``` ::: --- 再來要修改的是預約地點,在程式碼第`33`行,也就是 `var branchVal = ""` 那裡,請填上學堂的代碼,學堂代碼請參考以下表格。 :::success 例如以下的範例,填上 `"WW"`,就代表要預約==台南數位學堂==。 ```javascript=33 var branchVal = "WW" ``` ::: | 學堂名稱 | 代碼 | |:----------------:|:------:| | 三峽數位學堂 | `"TD"` | | 高雄數位學堂 | `"XX"` | | 中科數位學堂 | `"VD"` | | 台南數位學堂 | `"WW"` | | 宜蘭數位學堂 | `"CB"` | | 新莊中正數位學堂 | `"TF"` | | 彰化數位學堂 | `"VC"` | | 台中數位學堂 | `"VV"` | | 嘉義數位學堂 | `"WA"` | | 虎尾數位學堂 | `"WD"` | | 楠梓數位學堂 | `"XB"` | | 北高數位學堂 | `"XC"` | | 壽豐數位學堂 | `"CH"` | | 桃園數位學堂 | `"UB"` | | 基隆數位學堂 | `"TA"` | | 台北數位學堂 | `"TT"` | | 中壢數位學堂 | `"UA"` | | 新竹數位學堂 | `"UU"` | | 斗六數位學堂 | `"WB"` | | 新莊建興數位學堂 | `"TB"` | | 平鎮數位學堂 | `"UD"` | | 公館數位學堂 | `"TE"` | | 淡水數位學堂 | `"TS"` | | 林口數位學堂 | `"UC"` | | 苗栗數位學堂 | `"UE"` | | 西屯數位學堂 | `"VA"` | | 民雄數位學堂 | `"WF"` | | 屏東數位學堂 | `"XA"` | | 台東數位學堂 | `"ZV"` | :::danger 記得每次修改完都都要**按下左上方的 Save 按鈕哦**。 ::: ## 使用步驟(每次都要做) 1. 提早 `15~20` 分鐘登入 TKB,並到預約座位的頁面。 2. 按下 `F12(chrome)`,按下 `console(主控台)` 標籤,看看即將預約的時間與科目是否正確,`console(主控台)` 頁面如下圖,理論上裡面會顯示==要預約的科目==以及會==在什麼時間預約==。 ![](https://i.imgur.com/h94TyQj.png) :::info 為了避免被登出,每隔 `10` 分鐘頁面會重新整理一次。 ::: 3. 保持網路順暢,靜待預約時間。開始預約之後,**若預約成功, `console` 頁面會顯示預約成功,若失敗則會顯示失敗原因**。 ![](https://i.imgur.com/3JPQSzT.png) 若順利的話,很有可能每天都坐同一個座位(?) ![](https://i.imgur.com/8sPQihF.png) ## 使用限制 + 一次只能約一科。 + 預約的都是 `+6` 天後的時間,沒有預約明天後天的選項。 + 例如 `2021/3/29` 會預約 `2021/04/04` 的課 + 請務必保證瀏覽器的時間在 `GMT+8` 的時區內 + 每個科目的 `day` 裡面的值不能重複,若重複會預約到 `classes` 裡面較上面的科目。 + 受限於網路延遲以及 TKB 的 server,**不一定每次都成功**。 + 有時候因為超時而失敗的預約,可能會在短暫時間後成功,但不會顯示出來,請在大約 `00:10` 去查詢座位那裡查看有沒有預約成功。 ## 程式碼 註1: **根據使用回報情況不定時會改版,有空可以回來看看** 註2: coding style 因為 chrome 對於擴充功能的同步有大小上的限制,不是很好看,請見諒。 ==**Now version : 2021/04/05 16:30**== ```javascript= var classes = [ { name: "計組與計結", day: [], session: [], }, { name: "線性代數", day: [], session: [], }, { name: "離散數學", day: [], session: [], }, { name: "資料結構", day: [], session: [], }, { name: "作業系統", day: [], session: [], }, { name: "演算法", day: [], session: [], }, ] var branchVal = "" // 以上是需要自己修改的地方,從這裡以下的部分若有改版的話再覆蓋上去就好了 var url = "/book-seat/student/bookSeat/book"; var siteKey = "6LeuTLMUAAAAADQhb_N0EuidZomYHoWpuGZHKoxv"; var bt = new Date(), tt = new Date(), res; var cv, dv, sv, tv, atv = $("#access_token_id").val(), av = $("#action").val() var sec = 1000 function sleep(t) { return new Promise((resolve) => setTimeout(resolve, t)); } function init() { var s = bt.getTime() tt.setDate(tt.getDate() + 6); bt.setHours(bt.getHours() - 4) bt.setTime(bt.getTime() + 43200000 - bt.getTime() % 43200000) bt.setHours(bt.getHours() + 4) $("#class_selector option").each(function (idx, e) { for (let i = 0; i < classes.length; i++) { if (e.text.indexOf(classes[i].name) !== -1) { var a = classes[i].day.indexOf(tt.getDay()) if (a === -1) { continue; } console.log("即將要預約的課是: ", tt.toLocaleDateString(), "的", classes[i].name) cv = e.value; sv = classes[i].session[a] } } }); var ds = String(tt.getFullYear()) + "-" if (tt.getMonth() < 9) ds += "0" ds += String(tt.getMonth() + 1) + "-" if (tt.getDate() < 10) ds += "0" ds += String(tt.getDate()) console.log("將會在 ", bt, " 預約") dv = ds res = false } async function ready() { var d = bt - Date.now() + 30 await sleep(d).then(async function () { for (let i = 1; !res; i++) { await myBook(i) if (Date.now() - bt > 10*60*sec) break; await sleep(3*sec) } }); } function test() {} async function myBook(i) { var ok = false, now = new Date(), msg = ""; await grecaptcha .execute(siteKey, { action: "student_bookseat", }).then(function (t) { tv = t; }); postData = { class_data: cv, date: dv, branch_no: branchVal, session_time: sv, access_token: atv, token: tv, action: av, }; $.ajax({ url: url, cache: false, dataType: "json", type: "POST", data: postData, timeout: 30*sec, beforeSend: function () { console.log( `開始第 ${i} 次預約 : 已在開始後第 ${(now - bt) / sec}s 送出請求` ); }, success: function (d) { if (d.VERIFY_CODE === "B") { msg = "送出資料有誤"; } else { if (!d.MESSAGE || d.MESSAGE.search("成功") === -1) { msg = "未知原因導致失敗"; } else { ok = true; } } }, complete: function (xhr, s) { if (s === "error") { msg = "該時段沒有位子 or 已預約 or 沒有開放 or chrome 判斷 xhr 超時" } else if (s === "timeout") { msg = "請求超時" } console.log(`第 ${i} 次 嘗試預約: ${ok ? "成功" : `失敗 -> ${msg}`}`) console.log(`第 ${i} 次 request 經過 ${(Date.now() - now) / sec}s 得到結果`) res |= ok }, }); } init(); setTimeout(function () { var d = (bt - Date.now()); if (d > 10*60*sec) window.location.reload() }, 10*60*sec); test(); ready(); // author: soimportant ```