# 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. 安裝擴充功能,務必確認有這個圖示在右上角

2. 登入 TKB,並進到[預約座位的頁面](https://bookseat.tkblearning.com.tw/book-seat/student/bookSeat/index)

3. 按下擴充功能的那個按鈕會跑出這個頁面

4. 點選 `New RegExp`,把這串貼上去 `https://bookseat.tkblearning.com.tw/book-seat/student/bookSeat/index` 並按下 Add。
5. 接著把下面的程式碼貼到中間的區塊內,並依照下方的程式碼修改步驟開始修改。

---
### 程式碼修改步驟(第一次使用一定要做,之後有需要才要再做)
首先是定義==想要預約的課==以及==想預約的日期及時段==。
```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(主控台)` 頁面如下圖,理論上裡面會顯示==要預約的科目==以及會==在什麼時間預約==。

:::info
為了避免被登出,每隔 `10` 分鐘頁面會重新整理一次。
:::
3. 保持網路順暢,靜待預約時間。開始預約之後,**若預約成功, `console` 頁面會顯示預約成功,若失敗則會顯示失敗原因**。

若順利的話,很有可能每天都坐同一個座位(?)

## 使用限制
+ 一次只能約一科。
+ 預約的都是 `+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
```