以下要講下魔改 BlogArchive Widget,今次應該真係魔改,暫時在網站上找不到任何相關的文章和 API 講有關事情。
話說係 BlogArchive Widget 入面如果用 `HIERARCHY` 模式的話,可以得到一個 `data` 參數來畫出你需要的畫面。
因為是 `HIERARCHY` 的關係,所以 `data` 變數也是樹狀型態的。
依照[官方 Documentation](https://support.google.com/blogger/answer/47270) 所講,你可以得到以下的資料:
- title: The title of the widget.
- style: One of 'MENU', 'FLAT', or 'HIERARCHY'.
- data: A list of each archive unit, each of which contains:
- name: The name of this archive interval, e.g. "August 2006."
- url: The link to the page containing posts from this interval.
- post-count: How many posts there are in this interval.
### 限制
但是如果你玩過呢個 Widget 之後,發覺佢只會提供現時所在位置 Post 的相關月份內容,而不是提全部的內容給你去畫出 UI。
例如你現在是正在看 2023 年 6 月的文章,Widget 只會給你以下資料:
- [2023 年](https://example.blogspot.com/2023/)
- [7 月](https://example.blogspot.com/2023/07/)
- [6 月](https://example.blogspot.com/2023/06/)
- [你的文章 1](https://example.blogspot.com/2023/06/example-post1.html)
- [你的文章 2](https://example.blogspot.com/2023/06/example-post2.html)
- [4 月](https://example.blogspot.com/2023/04/)
- [1 月](https://example.blogspot.com/2023/01/)
- [2022 年](https://example.blogspot.com/2022/)
- [5 月](https://example.blogspot.com/2022/05/)
- [4 月](https://example.blogspot.com/2022/04/)
你只能夠按下 2023 年 7 月的連結,是不能夠取得 2023 年 7 月的文章列表。
### 開始 Reverse Engineering
如果大家有玩過 Blogger 預設的 Theme,有可能會發現有一些好舊版的 Theme (千禧年主題)入面個 Blog Archive 是可以做到 Ajax 載入不同月份的文章列表。
呢個時間你可以打開 Chrome development tool 來看看到底叫了一個什麼的 Ajax 出去。你應該會看到類似下以的 Url:
```js
https://example.blogspot.com/?action=getTitles&widgetId=BlogArchive1&widgetType=BlogArchive&responseType=js&path=https://example.blogspot.com/2023/06/&xssi_token=TOKEN
```
然後你可以試下去不同的頁面去開一開個 Blog Archive 上面個箭咀去觸發個 Ajax 載入:
```js
https://example.blogspot.com/2023/02/?action=getTitles&widgetId=BlogArchive1&widgetType=BlogArchive&responseType=js&path=https://example.blogspot.com/2023/06/&xssi_token=TOKEN
```
會發現到無論你在任何的 URL 上 Request 去 server 都好,只要你 Url 上 Query 上以下的參數就可以 Call `text/javascript` 的 Response 回來。
- action
暫時只發現 'getTitles' 這個值能用
- widgetId
大約是填返你 BlogArchive 個 id,試過填第二啲野會出 HTTP 400 Error
- widgetType
暫時只發現 'BlogArchive' 這個值能用
- responseType
暫時只發現 'js' 這個值能用
- path
這個必需要填對才能通過,如果你要取得 2023 年 6 月的文章列表,就必需要填入 'https://example.blogspot.com/2023/06/' 這個值,填錯出會 Error
- xssi_token
可以用 JS 在 runtime 時找到 xssi token
### 如何找到 XSSI Token
你可以在你的 Blogger 網站上用 Browser 按 `檢視源始碼` 來看看由 Blogger Server 最終 Generate 出來的 HTML 到底是怎樣。
我們先 Scroll 到最底,會發現到類似的 JS Code :
```js
window['__wavt'] = 'ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD';
```
就是這個 `window['__wavt']` 變數裝住的,就是你需要的 XSSI Token。你可以在 Event `DOMContentLoaded` 後任何時間取出來使用。
```js
// DOMContentLoaded 事件處理
document.addEventListener('DOMContentLoaded', function() {
// 儲起 XSSI TOKEN 備用
const XSSI_TOKEN = window['__wavt'];
});
```
### 開始 Request
有齊上面資料後,就可以用 `axios` 出個 GET Request 比伺服器。
```js
// prepare url
const url = 'https://example.blogspot.com/?action=getTitles&widgetId=BlogArchive1&widgetType=BlogArchive&responseType=js&path=https://example.blogspot.com/2023/06/&xssi_token=TOKEN';
// axios get request and extract 'data'
const { data } = axios.get(url);
```
如果成功沒有出 Error 的話,大約會得到以下的 Response :
```js
try {
_WidgetManager._HandleControllerResult('BlogArchive1', 'getTitles',{'path': 'https://example.blogspot.com/2023/06/', 'posts': [{'title': 'Your post title', 'url': 'https://example.blogspot.com/2023/06/your-post.html'}]});
} catch (e) {
if (typeof log != 'undefined') {
log('HandleControllerResult failed: ' + e);
}
}
```
但是問題來了,因為取得的不是 Json 格式,不能夠直接使用,必需要使用神器 `eval()` 來加工一下。
### JsonP
還記得先前有文章講解過 JsonP 嗎? 如果未睇過可以去睇下。
我們先整個 random function attach 去 window object 度先 :
```js
// 整條 Random String 作為 Function 名
const fncName = 'fnc_' + Math.random().toString(16).slice(2, 12);
// 用上面個 Random String 作為 window 的 Property, 指向一個 Function
window[fncName] = (widgetId, action, data) => {
// log 去 console 度
console.log(data);
// 用完要 delete 返 (一次性 function)
delete window[fncName];
};
```
然後然要搞搞個 Response 令到佢 `eval()` 時改為執後你個自訂 function。
```js
// 將 '_WidgetManager._HandleControllerResult' 改為你自訂的 function 名稱, 即係上面個 Random function 名
const jsCode = data.replace(/_WidgetManager\._HandleControllerResult/g, fncName);
// 執行 eval
eval(jsCode);
```
如果成功的話,就可以在 development console 上看到 print 左條 json 出來。
```js
{'path': 'https://example.blogspot.com/2023/06/', 'posts': [{'title': 'Your post title', 'url': 'https://example.blogspot.com/2023/06/your-post.html'}
```
有左呢條 json 的資料,就可以用佢來更新返相應的 UI 位置。
搞掂 ! 食碗麵 !
### 總結
呢個係魔改,不知道某一日會唔會突然用唔到,不過機會都好細,因為已經有成千上萬個 Blog 都用緊,所以不會輕易就會出改動。
同埋可能有更多更多的魔改有待發掘 !! 等大家繼續去發現 !!