HackMD
    • Sharing Link copied
    • /edit
    • View mode
      • Edit mode
      • View mode
      • Book mode
      • Slide mode
      Edit mode View mode Book mode Slide mode
    • Note Permission
    • Read
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • Write
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • More (Comment, Invitee)
    • Publishing
    • Commenting Enable
      Disabled Forbidden Owners Signed-in users Everyone
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Invitee
    • No invitee
    • Options
    • Versions
    • Transfer ownership
    • Delete this note
    • Template
    • Save as template
    • Insert from template
    • Export
    • Google Drive Export to Google Drive
    • Gist
    • Import
    • Google Drive Import from Google Drive
    • Gist
    • Clipboard
    • Download
    • Markdown
    • HTML
    • Raw HTML
Menu Sharing Help
Menu
Options
Versions Transfer ownership Delete this note
Export
Google Drive Export to Google Drive Gist
Import
Google Drive Import from Google Drive Gist Clipboard
Download
Markdown HTML Raw HTML
Back
Sharing
Sharing Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
More (Comment, Invitee)
Publishing
More (Comment, Invitee)
Commenting Enable
Disabled Forbidden Owners Signed-in users Everyone
Permission
Owners
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Invitee
No invitee
   owned this note    owned this note      
Published Linked with
Like BookmarkBookmarked
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- tags: cofacts --- # rumors-line-bot 過去傳過訊息 implementation Past discussion: https://github.com/cofacts/rumors-line-bot/issues/133 ## User story - 作為 **Chatbot 使用者**,我希望 chatbot 可以列出我過去傳過的訊息以及回應的狀況,讓我能在有需要的時候快速翻找。 - 作為 **Chatbot 使用者**,我希望 chatbot 在我傳過的訊息有新回應的時候可以通知我,讓我接收最新的不同意見。 ## Task breakdown 1. Chatbot server 需連結至新的資料庫。此資料庫: - 記錄每名 chatbot 使用者曾經查詢過哪些 article、哪些有新 reply - 記錄每名 chatbot 使用者是否點擊觀看新 reply(列表標上「新 reply 標記」,在使用者點入連結前持續顯示) - 紀錄每名 chatbot 使用者曾經接收過哪些 reply 的推播(避免重複推播) 2. Chatbot 提供一個「查詢過的訊息列表」 - 應使用 LIFF 實作、透過 rich menu 觸發 - 使用者透過訊息列表點入訊息,需要標記成已讀、消除新 reply 標記 > 「點入訊息」應該無法直接顯示文章頁面,因為這樣無法送出「有用」或「沒用」。 > 這可能要回到 chatbot 顯示或者直接在 LIFF 另外實作,才能正常讓使用者送出「有用」或者是「沒用」。 - 應[重構現有 LIFF 實作](https://github.com/cofacts/rumors-line-bot/issues/144),套用 server-render 或 client UI library 以利開發 - 應讓 postback button [往上捲回去的時候仍然可用](https://github.com/cofacts/rumors-line-bot/issues/49) - ~~需在 chatbot server 上建立 API server 支援上述列表與操作~~ ~~server-sider render, 實作 CSRF protection~~ 不想 proxy Cofacts API 也想降低 server load 而不走 server-side render,最後作成 GraphQL server + token-based authentication 3. 需實作一 cron job 實作通知,包含: - 透過 push API 或 LINE Notify 告知 Chatbot 使用者有新回應。該 notification 的 call-to-action 就是請使用者打開「查詢過的訊息列表」介面。 - 更新相對應的新 reply 標記(標成未讀) ## 資料庫: mongodb w/ [mLab](https://elements.heroku.com/addons/mongolab) 496MB Free! ## 資料表:`UserArticleLinks` 紀錄每個 bot user 與訊息的羈絆 (?) - `userId`: ID - `articleId`: ID - `createdAt`: timestamp 使用者傳送此訊息、建立此 link 的時間 - `lastViewedAt`: timestamp 使用者上次查看此訊息~~回應~~的時間。 - 2020/7/4 討論後追加:`UserArticleLink` create 時必會寫入 - ~~`lastRepliedAt`: timestamp Cofacts 資料庫內最新回應的時間,cronjob 或 view 時更新~~ (不用惹) - ~~`lastPositiveFeedbackRepliedAt`: [timestamp 資料庫內評價為正的最新回應時間](https://g0v-slack-archive.g0v.ronny.tw/index/channel/C2PPMRQGP#ts-1584172124.191900) ,cronjob 或 view 時更新~~ (不用惹) ### 什麼時候會新增一筆 `UserArticleLink` - 傳訊息、資料庫沒有、填寫理由送出後 - 傳訊息、選擇一則資料庫訊息,但該訊息沒有回應。 (沒填理由也算) - 傳訊息、選擇一則資料庫訊息,有回應。 :::info 大小估算: 2017~2020 年所有 user selects an article + confirm submit article = (270,018+18,369) ~= 300,000,每個都是一個 user-article link 粗估每個 document 500byte 300,000 * 0.5KB = 150MB ::: ## 資料表:`UserSettings` 一個 LINE bot user 僅會有一筆 UserSetting。 - `userId` - `allowNewReplyUpdate`:使用者是否允許我們推新回應。預設 true - `newReplyNotifyToken`:若使用者有設定 LINE Notify,此欄位會存 LINE notify 用的 token。預設為空。 :::info 大小估算: 200byte/record * 200K LINE bot users = 40MB ::: ### 什麼時候會新增一筆 `UserSettings` 使用者加 chatbot 為好友時。 ## LINE bot user flow 1. User 點擊 rich menu 選擇看過去訊息 / 收到 notification 並點按 2. LINE bot 跳出全頁 LIFF,展示文章列表: - 時間 - 文章內文節錄(即時從 Cofacts API 抓) - 未讀回應數(即時從 Cofacts API 抓回應,然後挑出比 `lastViewedAt` 晚的)+ 未讀標記 3. 點擊文章列表項目,會傳訊息進 Cofacts chatbot,chatbot 列出未讀的回應列表,供使用者點按,進入 `CHOOSING_REPLY` state。若只有一則未讀回應,自動 `skipUser` - 此時要更新 corresponding `UserArticleLink` 的 `lastViewedAt` - 此設計讓使用者能下接「有用沒用」回應 - 此設計亦方便使用者轉傳回應 ### Implementation detail (2020/5/10) 分成下面三個部分: #### 1. LIFF <> chatbot GraphQL API 的新認證方式 目前 LIFF 與 chatbot GraphQL 的 authentication,仰賴 chatbot 在 URL 上帶有的自產 JWT,讓 chatbot GraphQL 知道目前是哪個 `userId` 在使用 LIFF,以及是哪個 search session (by `sessionId`) 的按鈕。 可是,新功能「過去傳過訊息」LIFF 會放在 rich menu,URL 裡無法帶有會過期的 JWT;直接不放 exp 或設定過久的 exp 也有些危險,畢竟 JWT 被 chatbot graphql 視為等同密碼的存在,一但 leak 出去,只要 exp 沒到,攻擊者就可以一直用該 JWT 的身份存取 API。 因此,我們會新增一種新的 authentication:LINE ID token 法。 1. LIFF 透過 [liff.getIDToken()](https://developers.line.biz/en/reference/liff/#get-id-token) 拿到 LINE 發的 OpenID ID token 2. LIFF 發 request 到 chatbot GraphQL 時帶有 `Authorization: line ${ID token}` (`line` 是為了與 JWT 使用的 `bearer` 做出區隔的自訂 authentication type) 3. chatbot GraphQL 收到 `line` 開頭的 authorization header 之後,使用 [verify ID token](https://developers.line.biz/en/reference/social-api/#verify-id-token) API 檢查是否正確,並且取用回傳的 user ID (`sub`)。 以上也是 [LINE 官方文件](https://developers.line.biz/en/docs/line-login/take-over-session/#transfer-login-session)中提到的作法。 由於 LINE ID token 必須呼叫 LINE API 才能取得,不會有洩漏的機會,所以比目前 chatbot 自發 JWT 的機制還更安全。不過,目前有些功能仍然需要保留「從 chatbot 傳資料給 LIFF 回到 GraphQL」的機制(如在按鈕上帶當時的 `sessionId`,LIFF 發 request 時也必須帶有該 `sesionId` 回到 GraphQL,且此 `sessionId` 不該在 LIFF 端被竄改) ,chatbot 自發 JWT 依然有不可取代的功能在,故會兩者並行。 不過,使用此種方式,會有下面的限制: - LINE developer 的 LIFF scope 設定必須要勾選 openid ![](https://s3-ap-northeast-1.amazonaws.com/g0v-hackmd-images/uploads/upload_25d6cd753775a7d5ea75931110150b7f.png) - LIFF channel 的 provider 必須與 chatbot (messaging API) provider 一致,才能共用 user id。 - Chatbot GraphQL 會多一個與 LINE server 發 API 確認 token 的工。 #### 2. LIFF 顯示文章列表的機制 LIFF 從 chatbot GraphQL 取回的資料將不會帶有訊息文字之類的訊息。整個載入流程會是: 1. LIFF 透過 `myArticleLinks` API 取得要顯示的 article links 2. LIFF 顯示表格、可點選的超連結,文字部分則為 loading 樣態 (類似 https://github.com/zalog/placeholder-loading ) 另外,顯示時發送 GA visit event,`utm_source=myArticleLink`, `utm_medium` 從 URL 拿,預設是 `richmenu`。 #### 3. 點擊項目時觸發的東西 點擊表內訊息後,送出「📃 See new replies of `https://cofacts.g0v.tw/article/<articleId>`」 到聊天室,觸發下面動作: - 開始新 search session(發配新 `sessionId`) - 未來 ga 的 `utm_source` 為 `myArticleLink`、`utm_medium` 為 `richmenu` - 從 URL 找出 `articleId`,設定 `selectedArticleId` - 找出相對應的 `userArticleLink` - 從 Cofacts API 找出所有回應之後,過濾掉建立時間比 `userArticleLink.lastViewedAt` 早的,使用 flex carousel 列出這些 reply - 更新 `lastViewedAt` 到現在的時間 - 切換到 `CHOOSING_REPLY` state :::info 未來推播實作時,由 push message 觸發的 LIFF URL 應帶有 `utm_medium=push`, 而發送到聊天室的內容也會稍微不同(如換 emoji 為 📌);chatbot 也使用此 emoji,判斷 utm_medium 之後要設定為何。 如此方可將「自己從 rich menu 點開 LIFF」的動作,與「看了 push message 之後點進來 LIFF」,跟其他一般查詢(完全不帶有 utm_xxx)分開。 ::: #### 2020/7/10 Update 根據 GA 文件 [traffic source dimensions](https://support.google.com/analytics/answer/1033173) 與 [collect campaign data](https://support.google.com/analytics/answer/1033863),`utm_source` 應是「導流量到這個網站的來源」、`utm_medium` 則是媒介。 對於 LIFF 裡的網頁來說,source 就是顯示 URL / button 讓使用者進來的流量來源,對這裡來說就是特定的 LINE channel;而媒介則可能是 push messaage 或是 rich menu。因此: - 若從 rumors-line-bot 的 rich menu 點按進 LIFF: - 來源是 Cofacts 的 bot、媒介是 rich menu - 故為 `utm_source=rumors-line-bot&utm_medium=richmenu` - 若是收到 rumors-line-bot 的 push notification: - 來源是 Cofacts 的 bot、媒介是 push message - 故為 `utm_source=rumors-line-bot&utm_medium=push`。`utm_source` - 若收到 LINE notify 的更新: - 來源是叫做 LINE notify 的 channel(預設),媒介也是 push message - 故為 `utm_source=line-notify&utm_medium=push` - 未來若有其他地方點了可以打開 LIFF,那應該要使用新的 `utm_source`,並且斟酌打開方式來設計 `utm_medium`。 ![](https://s3-ap-northeast-1.amazonaws.com/g0v-hackmd-images/uploads/upload_04aaea5b5442f0426b777244a59dd319.png) ## Cron job 邏輯 1. 從所有 `UserArticleLinks` 找出最大的 `lastRepliedAt` 作為 `lastScannedAt` 3. 去 `ListArticle` API (sort by `lastRepliedAt`)列出所有在 `lastScannedAt` 以後才新建立、且未刪除的 `ArticleReply` - 刪除回應不會被偵測到 - 編輯審閱時間:去掉最近 12 hr 的 articleReply 5. 由 2 整理出 list of unique, updated article ids 6. 從 user <> selected article 關聯表,找出要通知的對象 (list of unique users to update) 7. 比照這些使用者的 `UserSettings`,使用 multicast API & LINE Notify 告知使用者「之前的訊息有回應囉」導引使用者點開 LIFF 8. redis or DB 紀錄這次執行的 timestamp

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lost their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.


Upgrade

All
  • All
  • Team
No template.

Create a template


Upgrade

Delete template

Do you really want to delete this template?

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

Sign in via GitHub

New to HackMD? Sign up

Help

  • English
  • 中文
  • 日本語

Documents

Tutorials

Book Mode Tutorial

Slide Example

YAML Metadata

Resources

Releases

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions

Versions

Upgrade now

Version named by    

More Less
  • Edit
  • Delete

Note content is identical to the latest version.
Compare with
    Choose a version
    No search result
    Version not found

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.