Home 使用 Python+Google Cloud Platform+Line Bot 自動執行例行瑣事
Post
Cancel

使用 Python+Google Cloud Platform+Line Bot 自動執行例行瑣事

使用 Python+Google Cloud Platform+Line Bot 自動執行例行瑣事

以簽到獎勵 APP 為例,打造每日自動簽到腳本

Photo by [Paweł Czerwiński](https://unsplash.com/@pawel_czerwinski?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText){:target="_blank"}

Photo by Paweł Czerwiński

起源

一直以來都有使用 Python 做小工具的習慣;有做正經的,工作上自動爬數據、產報表,也有不正經的,排程自動查想要的資訊或是交給腳本完成本來要手動執行的動作。

一直以來「自動」這件事,我都很粗暴直接開一台電腦掛著 Python 腳本讓他掛著跑;優點是簡單方便,缺點是要有台設備接著網路接著電;就算是樹莓派也是要消耗著微量的電費網路錢,還有也不能遠端控制啟動或關閉(其實可以,但很麻煩);這次趁著工作空擋,研究了一下免費&上雲端的方法。

目標

將 Python 腳本搬到雲端執行、定時自動執行、可透過網路開啟/關閉。

本篇以我耍的小聰明,針對簽到獎勵型 APP 撰寫的自動完成簽到的腳本為例,能每日自動幫我簽到,我不用在特別打開 APP 使用;並在執行完成後發通知給我。

完成通知!

完成通知!

本篇章節順序

  1. 使用 Proxyman 進行 Man in the middle attack API 嗅探
  2. 撰寫 Python 腳本,偽造 APP API 請求(模擬簽到動作)
  3. 將 Python 腳本搬到 Google Cloud 上
  4. 在 Google Cloud 設定自動排程
  • 因涉及到敏感領域本篇不會告知是哪個簽到獎勵型 APP,大家可以延伸自行使用
  • 如果只想了解 Python 怎麼串自動執行可跳過前半段 Man in the middle attack API 嗅探部分,從第 3 章看起

使用到的工具

  • Proxyman :Man in the middle attack API 嗅探
  • Python :撰寫腳本
  • Linebot :發送腳本執行結果通知給自己
  • Google Cloud Function :Python 腳本寄存服務
  • Google Cloud Scheduler :自動排程服務

1.使用 Proxyman 進行 Man in the middle attack API 嗅探

之前有發過一篇「 APP有用HTTPS傳輸,但資料還是被偷了。 」的文章,道理類似,不過這次改用 Proxyman 取代 mitmproxy;同樣免費,但更好用。

  • 到官網 https://proxyman.io/ 下載 Proxyman 工具
  • 下載完後啟動 Proxyman,安裝 Root 憑證(為了做 Man in the middle attack 解包 https 流量內容)

「Certificate 」->「 Install Certificate On this Mac」->「Installed & Trusted」

電腦的 Root 憑證裝好後換手機的:

「Certificate 」->「 Install Certificate On iOS」->「Physical Devices…」

依照指示在手機上掛好 Proxy 並完成憑證安裝及啟用。

  • 在手機上打開想要嗅探 API 傳輸內容的 APP

這時候 Mac 上的 Proxyman 就會出現嗅探到的流量,點擊裝置 IP 下想要查看的 APP API 網域;第一次查看需要先點「Enable only this domain」之後的流量才能被解包出來。

「Enable only this domain」後就能看到新攔截的流量就會出現原始的 Request、Response 資訊:

我們使用此方法嗅探 APP 上操作簽到時打了哪隻 API EndPoint 及帶了哪些資料,將這些資訊記錄下來,等下使用 Python 直接模擬請求。

⚠️要注意有的 APP token 資訊可能會換,導致日後 Python 模擬請求失效,還要多了解 APP token 交換的方式。

⚠️如果確定 Proxyman 有正常運作,但在掛 Proxyman 的情況下 APP 無法發出請求,代表 APP 可能有做 SSL Pining;目前無解,只能放棄。

⚠️APP 開發者想知道怎麼防範嗅探可參考 之前的文章

這邊假設我們得到的資訊如下:

1
2
3
4
5
6
7
8
9
10
11
POST /usercenter HTTP/1.1
Host: zhgchg.li
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=dafd27784f94904dd586d4ca19d8ae62
Connection: keep-alive
Accept: */*
User-Agent: (iPhone12,3;iOS 14.5)
Content-Length: 1076
Accept-Language: zh-tw
Accept-Encoding: gzip, deflate, br
AuthToken: 12345

2. 撰寫 Python 腳本,偽造 APP API 請求(模擬簽到動作)

在撰寫 Python 腳本之前,我們可先使用 Postman 調試一下參數,觀察看看哪個參數是必要的或是有時效會改變;但要直接照搬也可以。

checkIn.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import json

def main(args):
    results = {}
    try:
      data = { "action" : "checkIn" }
      headers = { "Cookie" : "PHPSESSID=dafd27784f94904dd586d4ca19d8ae62", 
      "AuthToken" : "12345",
      "User-Agent" : "(iPhone12,3;iOS 14.5)"
      }
      
      request = requests.post('https://zhgchg.li/usercenter', data = data, headers = headers)
      result = json.loads(request.content)
      if result['status_code'] == 200:
        return "CheckIn Success!"
      else:
        return result['message']
    except Exception as e:
      return str(e)

⚠️ main(args) 這邊的 args 用途後面會講,如果要在本地測試直接帶 main(True) 就好。

使用 Requests 套件幫我們執行 HTTP Request,如果出現:

1
ImportError: No module named requests

請先使用 pip install requests 安裝套件。

加上執行結果 Linebot 通知:

這部分我做的很簡單,僅共參考,僅通知自己。

  • 選擇「Create a Messaging API channel」

下一步填好基本訊息後按「Create」送出建立。

  • 建立好之後在第一個「Basic settings」Tab 下面找到「Your user ID」區塊,這就是你的 User ID

  • 建立好之後,選擇「Messaging API」Tab,掃描 QRCode 將機器人加入好友。

  • 繼續往下滾找到「Channel access token」區塊,點擊「Issue」產生 token。

  • 複製下來產生出來的 Token,我們有這組 Token 就能發訊息給使用者。

有了 User Id 跟 Token 之後我們就能發訊息給自己了。

因沒有要做其他功能所以連 python line sdk 都不用裝,直接打 http 發。

串上之前的 Python 腳本後…

checkIn.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests
import json

def main(args):
    results = {}
    try:
      data = { "action" : "checkIn" }
      headers = { "Cookie" : "PHPSESSID=dafd27784f94904dd586d4ca19d8ae62", 
      "AuthToken" : "12345",
      "User-Agent" : "(iPhone12,3;iOS 14.5)"
      }
      
      request = requests.post('https://zhgchg.li/usercenter', data = data, headers = headers)
      result = json.loads(request.content)
      if result['status_code'] == 200:
        sendLineNotification("CheckIn Success!")
        return "CheckIn Success!"
      else:
        sendLineNotification(result['message'])
        return result['message']
    except Exception as e:
      sendLineNotification(str(e))
      return str(e)
      
def sendLineNotification(message):
    data = {
        "to" : "這邊帶你的 User ID",
        "messages" : [
            {
                "type" : "text",
                "text" : message
            }
        ]
    }
    headers = {
        "Content-Type" : "application/json",
        "Authorization" : "這邊帶channel access token"
    }
    request = requests.post('https://api.line.me/v2/bot/message/push',json = data, headers = headers)

測看看通知有沒有發成功:

Success!

小插曲,通知部分我本來是想用 Gmail SMTP 用信件來發,結果上到 Google Cloud 後發現無法使用…

3. 將 Python 腳本搬到 Google Cloud 上

前面基本的講完了,正式進入本篇重頭戲;將 Python 腳本搬上雲端。

這部分我一開始向中的是 Google Cloud Run 但用了下覺得太複雜,我實際懶得研究,因為我的需求太小用不到這麼多功能;所以 我用的是 Google Cloud Function serverless 方案;實際上比較常用來做的是構建 serverless web 服務。

  • 如果沒使用過 Google Cloud 的朋友,請先前往 主控台 新增好專案&設定好帳單資訊
  • 在專案主控台首頁,資源的地方點擊「Cloud Functions」

  • 上方選擇「建立函式」

  • 輸入基本資訊

⚠️記下「 觸發網址」

區域可選:

  • US-WEST1US-CENTRAL1US-EAST1 可享 Cloud Storage 服務免費額度。
  • asia-east2 (Hong Kong) 靠我們比較近,但需要支付微微的 Cloud Storage 費用。

⚠️建立 Cloud Functions 時會需要 Cloud Storage 寄存程式碼。

⚠️詳細計價方式請參考文末。

觸發條件選: HTTP

驗證: 依需求,我希望我能從外部點連結執行腳本,所以選擇「允許未經驗證的叫用」;如果選擇需要驗證,後續 Scheduler 服務也要做相應設定。

變數、網路及進階設定可在變數中設定變數給 Python 使用(這樣參數有變動就不用改到 Python 程式碼):

在 Python 中調用的方式:

1
2
3
4
import os

def main(request):
  return os.environ.get('test', 'DEFAULT VALUE')

其他設定都不需要動,直接「儲存」->「下一步」。

  • 執行階段選「Python 3.x」並將寫好的 Python 腳本貼上,進入點改成「main」

補充 main(args) ,同前述,此項服務比較是用來做 serverless web;所以 args 實際是 Request 物件,你能從其中拿到 http get query 及 http post body 資料,具體方式如下:

1
2
取得 GET Query 資訊:
request_args = args.args

example: ?name=zhgchgli => request_args = [“name”:”zhgchgli”]

1
2
取得 POST Body 資料:
request_json = request.get_json(silent=True)

example: name=zhgchgli => request_json = [“name”:”zhgchgli”]

如果使用 Postman 測試 POST 記得使用「Raw+JSON」POST 資料,否則不會有東西:

  • 程式碼部分 OK 之後,切換到「requirements.txt」輸入有用到的套件依賴:

我們使用「request」這個套件幫我們打 API,此套件不在原生 Python 庫裡面;所以我們要在這裡加上去:

1
requests>=2.25.1

這邊指定版本 ≥ 2.25.1,也可不指定只輸入 requests 安裝最新版。

  • 都 OK 之後點擊「部署」開始部署。

需要花約 1~3 分鐘的時間等他部署完成。

  • 部署完成後可由前面記下的「 觸發網址 」前去執行查看是否正確運行,或使用「動作」->「測試函式」進行測試

如果出現 500 Internal Server Error 則代表程式有錯,可點擊名稱進入查看「紀錄」,在其中找到原因:

1
UnboundLocalError: local variable 'db' referenced before assignment
  • 點擊名稱進入後也可按「編輯」修改腳本內容

測試沒問題就完成了!我們已經順利將 Python 腳本搬上雲端。

補充關於變數部分

依照我們的需求,我們需要能有個地方存放、讀取簽到 APP 的 token;因為 token 可能會失效;需要重新要求並寫入共下次執行時使用。

想要從外部動態傳入變數到腳本中有以下方法:

  • [Read Only] 前述所提到的,執行階段環境變數
  • [Temp] Cloud Functions 有提供一個 /tmp 目錄共執行時寫入、讀取檔案,但結束後就會刪除,詳情請參考 官方文件
  • [Read Only] GET/POST 傳送資料
  • [Read Only] 放入附加檔案

在程式中使用相對路徑 ./ 就能讀取到, 僅限讀取無法動態修改 ;要修改只能在控制台這修改&重新部署。

想要可以讀取、動態修改就需要串接其他 GCP 服務,例如:Cloud SQL、Google Storage、Firebase Cloud Firestore…

  • [Read & Write] 這邊我選擇的是 Firebase Cloud Firestore 因為目前只有此方案有免費額度使用。

按照 入門步驟 ,建立好 Firebase 專案後;進入 Firebase 後台:

在左方選單列找到「 Cloud Firestore 」->「 新增集合

輸入集合 ID。

輸入資料內容。

一個集合可以有多個文件,每個文件可以有各自的欄位內容;使用上非常彈性。

在 Python 中使用:

請先到 GCP控制台 -> IAM與管理 -> 服務帳戶 ,按照以下步驟下載身份驗證私鑰文件:

首先選擇帳號:

下方「新增金鑰」->「建立新的金鑰」

選擇「JSON」下載檔案。

將此 JSON 檔案放到同 Python 的專案目錄下。

本地開發環境下:

1
pip install --upgrade firebase-admin

安裝 firebase-admin 套件。

在 Cloud Functions 上要在 requirements.txt 中多加入 firebase-admin

環境弄好後,可以來讀取我們剛剛新增的數據了:

firebase_admin.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore

if not firebase_admin._apps:
  cred = credentials.Certificate('./身份驗證.json')
  firebase_admin.initialize_app(cred)
# 因若重複 initialize_app 會報以下錯誤
# providing an app name as the second argument. In most cases you only need to call initialize_app() once. But if you do want to initialize multiple apps, pass a second argument to initialize_app() to give each app a unique name.
# 所以安全起見在 initialize_app 前先檢查是否已 init

db = firestore.client()
ref = db.collection(u'example') //集合名稱
stream = ref.stream()
for data in stream:
  print("id:"+data.id+","+data.to_dict())

如果是在 Cloud Functions 上除了可以把 身份驗證 JSON 檔一起上傳上去,也可以在使用時將連接語法改成以下使用:

1
2
3
4
5
6
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred, {
  'projectId': project_id,
})

db = firestore.client()

如果出現 Failed to initialize a certificate credential. ,請檢查身份驗證 JSON 是否正確。

新增、刪除更多操作請參考 官方文件

4. 在 Google Cloud 設定自動排程

有了腳本之後再來是要讓他自動執行才能達到我們的最終目標。

  • 輸入工作基本資料

執行頻率: 同 crontab 輸入方式,如果你對 crontab 語法不熟,可以直接使用 crontab.guru 這個神器網站

他能很直白的翻譯給你所設定的語法實際意思。(點 next 可查看下次執行時間)

這邊我設定 15 1 * * * ,因為簽到每天只需要執行一次,設在每日凌晨 1:15 執行。

網址部分: 輸入前面記下的「 觸發網址

時區: 輸入「台灣」,選擇台北標準時間

HTTP 方法: 照前面 Python 程式碼我們用 Get 就好

如果前面有設「驗證」 記得展開「SHOW MORE」進行驗證設定。

都填好後 ,按下「 建立 」。

  • 建立成功後可選擇「立即執行」測試一下正不正常。

  • 可查看執行結果、上次執行日期

⚠️ 請注意,執行結果「失敗」僅針對 web status code 是 400~500 或 python 程式有錯誤。

大功告成!

我們已達成將例行任務 Python 腳本上傳到雲端&設定自動排成自動執行的目標。

計價方式

還有一部分很重要,就是計價方式;Google Cloud、Linebot 都不是全免費服務,所以了解收費方式很重要;不然為了一個小小的腳本,付出太多的金錢那不如電腦開著掛著跑哩。

Linebot

參考 官方定價 資訊,一個月 500 則內免費。

Google Cloud Functions

參考 官方定價 資訊,每月有 200 萬次叫用、400,000 GB/秒和 200,000 GHz/秒的運算時間、 5 GB 的網際網路輸出流量。

Google Firebase Cloud Firestore

參考 官方定價 資訊,有 1 GB 大小容量、每月 10 GB 流量、每天 50,000 次讀取、20,000 次寫入/刪除;輕量使用很夠用了!

Google Cloud Scheduler

參考 官方定價 資訊,每個帳號有 3 項免費工作可設定。

對腳本來說以上免費用量就綽綽有餘啦!

Google Cloud Storage 有條件免費

東躲西躲,還是躲不掉可能被收費的服務。

Cloud Functions 建立好之後會自動建立兩個 Cloud Storage 實體:

如果剛剛 Cloud Functions 選擇的是 US-WEST1、US-CENTRAL1 或 US-EAST1 這三個地區則可享有免費使用額度:

我是選擇 US-CENTRAL1 沒錯,可以看到第一個 Cloud Storage 實體的地區是 US-CENTRAL1 沒錯,但第二個是寫 美國多個地區我自已估計這項是會被收費的

參考 官方定價 資訊,依照主機地區不同有不同的價格。

程式碼沒多大,估計應該就是每個月最低收費 0.0X0 元(?

⚠️以上資訊均為 2021/02/21 時撰寫時紀錄,實際以當前價格為主,僅共參考。

計價預算控制通知

just in case…假設真的有狀況超出免費用量開始計價,我希望能收到通知;避免可能程式錯誤暴衝造成帳單金額報表卻渾然不知。。。

  • 前往 主控台
  • 找到「 計費功能 」Card:

點擊「 查看詳細扣款紀錄 」進入。

  • 展開左邊選單,進入「 預算與快訊 」功能

  • 點擊上方「 設定預算

  • 輸入自訂名稱

下一步。

  • 金額,輸入「 目標金額 」,可輸入 $1、$10;我們不希望在小東西上花太。

下一步。

動作這邊可以設定當預算達到多少百分比時會觸發通知。

勾選透過電子郵件將快訊傳送給帳單管理員和使用者 」,這樣當條件處發時就能第一時間收到通知。

點擊「完成」送出儲存。

當預算超過時我們就能馬上就能知道,避免產生更多費用。

總結

人的精力是有限的,現今科技資訊洪流,每個平台每個服務都想要榨取我們有限的精力;如果能透過一些自動化腳本分擔我們的日常生活,聚沙成塔,讓我們省下更多精力專心在重要的事情之上!

===

本文首次發表於 Medium ➡️ 前往查看


This post is licensed under CC BY 4.0 by the author.

重灌筆記1-Laravel Homestead + phpMyAdmin 環境建置

揭露一個幾年前發現的巧妙網站漏洞