用 VPS 建 Webhook Server 小筆記

在以前,我可能會用爬蟲的方式去拿一些資訊,但沒串接到一些通訊軟體上面
這次簡單紀錄一下在串接第三方服務的一些指令與流程

背景

使用情境的話,大概會是 :

  1. 某個 Application 偵測到特定條件,通知 Webhook Server
  • 例如我寫了爬蟲,發現 あみた 出了新影片
  1. Webhook Server 收到通知,然後進行相關訊息轉送
  • 例如我有一支程式,在收到通知後他會寄一封信到我的 Mail,同時在 Line 通知我。
  • 也有可能這個 Webhook Server 是通訊軟體商提供,你直接送過去,他就會有訊息通知。

你可能會說,那為何不要我寫爬蟲時同時 run 一些 bot 然後直接做多方面通知 ?
其實是可以,但是發通知的可能是多種程式 (例如你今天有爬 あみた 影片的、可能也有商品開賣的)

而且今天也許並不需要你自己寫程式,而是某個 SaaS 會做特定工作,然後提供一個功能 :

" 你可以給我們一個 webhook url,如果我工作完成了就告訴你 ! "

那你還是得自己弄 webhook Server 來做接收,或是直接找一些提供 webhook url 的通訊軟體。

在市面上有一些已經整合的不錯的服務,例如 IFTTT
他就可以達成上面的 "Webhook Server" 的工作,只要你寫的程式去觸發 webhook,
IFTTT 可以幫你用 Line、Telegram、Mail 等等方式通知你。

但最主要的缺點就是會有時間週期的限制,一點也不即時。

準備工作

  1. 你需要有一支程式負責做某項工作,然後讓他來發通知。
  2. 你需要一個 VPS 或是能夠建立實體 IP / 或 DNS 網址的 Server 來當作 Webhook 的觸發平台
  3. 你需要有 "你想接收通知" 的通訊軟體的 bot 或廠商提供的 webhook url。

我這邊只舉例我用到的,但既然是工程師的話,我想您一定能夠推敲出更改的方案 XD
如果不知道怎麼做,想了很久仍然找不出合適方式,也可以在下方提出討論。

建立發通知的程式

這邊舉三個例子。
其實,不必想太多,Webhook 的觸發基本上 只是發送一個 POST
幾乎就只是在實作 post 這件事而已,用 js 甚至都能寫。

Python

1
2
3
4
5
6
7
8
9
10
import requests
url = "webhook_url"
data = { "content": "Hello Webhook !" }
result = requests.post(url, json = data)
try:
result.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
else:
print("Payload delivered successfully, code {}.".format(result.status_code))

透過現有服務方

像是這個服務方,他提供讓我們輸入 webhook url 來讓他們去做 request 動作
我們在後面的過程建好 Webhook Server 後,就可以將網址填到欄位裡面。

透過 cURL

如果有當過網管、或是因為某些需求需要測試網路,應該對這個不陌生。
(我沒當過,所以我很陌生其實)

但總之呢,Linux 內通常都會內建這樣的工具,讓你去做一些基本的 GET/POST 或測試操作。

這邊提供一個工具,可以讓你直接模擬 POST 的動作
REQBIN: How do I POST JSON with Curl?

填好對應欄位,再按 Run,就能直接做測試。
也就是說,想要確認你的 Webhook Server 有沒有問題,可以直接用這個來當作測試
(用 Postman 也可以,不過很多 Webhook 服務方會用 cURL 做舉例,剛好搭配這個工具)

1
2
3
curl -X POST http://your_server/webhook
-H "Content-Type: application/json"
-d '{"content":"Hello Webhook !"}'

建立能夠串接的通訊軟體 Bot

這邊要做的是,我們在中介層中,接到通知後,要去啟動相對應的 bot 然後在通訊軟體裡面告訴你。
(中介層 : 負責接收服務方打過來的 POST 的程式,也就是 webhook server,下一個段落會細說)

常見的通訊軟體 :

  1. Line (之後會有一篇文章記錄,也非本次會提及)
  2. Telegram
  3. Discord (但這個建議直接用頻道內建的 url,所以這邊不提及。)

這次我是使用 Telegram 來申請相關服務,所以主要是記錄這部分的。

流程 :

  1. 搜尋好友 @BotFather@getidsbot,將他們加入好友。
  2. 對 @BotFather 輸入指令 : /newbot,然後輸入 你的bot想取的名稱,再輸入 他的專有id且一定要bot三個字結尾,然後你就會得到一組 token 像是 123456789:ABCd_qgergrgjeigroXgrG_cGEGge_GEGeeAY 之類的。
  3. 打開 Telegram -> 建立頻道(Channel) -> 邀請你剛剛建立的 bot
  4. 在頻道隨便打一則訊息,然後按出現在訊息旁邊的 "分享",分享給 GetIDs Bot
  5. 然後上面那個 Bot 會告訴你 Origin chat 的 id 是多少,記錄下來。

再來就是 Bot 怎麼發送訊息到 Channel 內
有兩種方式 : 一種是直接用基礎的 GET/POST 來發送,用 request 就能做了,且各種程式語言都能。

另一種則是使用相關 Library,例如 Python 的。
下面會給一個範例,但根據版本也許不一定能正確執行,僅供參考。

當然,下面這樣的程式,是在 Webhook Server 收到通知後,要去執行的
這個段落僅僅是為了講解 Telegram Bot 相關使用的流程,所以提及下面的程式碼。

1
2
3
4
5
6
7
8
9
10
from telegram import Bot
tg_bot = Bot(token=your_tg_token)
context = "Hello Bot !"
try:
tg_bot.sendMessage(
your_channel_id,
context,
)
except Exception as e:
print("[Warning] Telegram Error:\n>", e)

建立 Webhook Server

這邊則是要講解要建一個 Webhook Server 需要哪些要素。

  1. 要有獨立 IP 或是能夠被其他 Client 觸及的域名之類的。
    最好的方法是用 VPS,VPS 可以申請一個靜態域名。
    我自己是有 VPS,所以寫完之後,再改 Port 就完事了。
    雖然我沒有試過,但是像是宿舍沒有個人 IP 的電腦也許可以用 Ngrok 解決。
  2. 要寫一支可以監聽 POST 的程式。
    我自己是使用 Python Flask。
  3. 該程式在聽到 POST 動作後,可以做一些判斷,然後去執行上面提供的 Telegram 程式碼。

Flask 的監聽範例

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
import json
from flask import Flask, request

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
try:
if request.method == "POST":
data = request.get_json()
# Do something
send_text_to_channel() # connect bot
return "Sent alert", 200
else:
print("[Warning] Server Received & Refused!")
return "Refused", 400

except Exception as e:
print("[Warning] Error:\n>", e)
return "Server error", 400

if __name__ == "__main__":
from waitress import serve

serve(app, host="0.0.0.0", port=80)

上面這支程式會監聽您的 VPS 的 80 port,且只會對 POST 的動作有反應。
假設您的 IP 是 100.100.100.100,那您的 webhook url 就是 100.100.100.100/webhook
(在 @app.route 有定義,只監聽 /webhook route。)

但如果您的 VPS 有使用到 Apache 之類的來做 HTML Server 之類的用途,
那可能會因為 80 Port 被占用而相衝。

解決 80 Port 被佔用問題

這邊有兩個解法 :

  1. 在程式內把 port=80 改成 port=8888 之類的。
    但缺點是,有些服務商在送 Webhook 時拒絕送 80 port 以外的。
    這種時候就需要下面的解法,
    把占用 80 port 的程式給改掉設定,例如 Apache 預設是聽 80 port。

  2. 如何去改掉 Apache 的預設監聽值 ?
    可以參考這篇文章 : How to Change Apache HTTP Port in Linux

1
2
3
4
5
6
7
8
9
10
11
# 1. 編輯 ports.conf 
vim /etc/apache2/ports.conf
# 2. 然後將 Listen 80 改成 Listen 8888
# 3. 編輯 sites-enable 檔案
vim /etc/apache2/sites-enabled/000-default.conf # 或是您是另外寫在其他 conf
# 4. 將 <VirtualHost *:80> 改成 <VirtualHost *:8888>
# 5. 重啟 apache2
systemctl restart apache2
# 6. 查看相關設定是否被應用
netstat -tlpn| grep apache
ss -tlpn| grep apache

如何讓程式常駐

如果是透過 ssh 連到 VPS,那在關閉連線後可能會關閉程式。
您可以透過 pm2 或是內建的 tmux 來達成在背景執行。

原本我想嘗試 PM2,但是他的 deb 安裝方式 ... 官方的 Lib 有問題
後來忙於其他事就先用 tmux 用用。

Tmux 部分,可以參考我的文章 我終於會用 tmux 啦 !
這邊也簡單的複習一下使用方式 :

1
2
3
4
5
6
7
8
9
10
# 進入 tmux
tmux
# 然後 run 程式
python main.py
# 想離開 tmux 可以按 Ctrl+b 之後再按 d
# 想再次進入該 session 可以使用以下指令
tmux ls
tmux a -t 0
# 想刪除該 session 可以用 kill-session
tmux kill-session -t 0

測試

統整一下上面的資訊 :

訊息傳遞流 :

  1. 從觸發端(自己寫的程式、服務商) 發送 POST
  2. Webhook Server 收到 POST,然後做相對應處理,可以是寄 Mail 或是 Bot 傳訊息
  3. Bot 會在你的頻道內輸出訊息。

觸發端,可以用 cURL 測試最簡單,或是自己用 Python、Javascript 去發送 Request。
Webhook Server,可以自己建或是使用網路上既有的服務如 IFTTT。
在 Webhook Server 接收到 POST 之後,可以再串通訊軟體提供的 API 這樣。

結語

這邊先道歉一下,並沒有將整個完整的程式碼提供出來
主要是有些 local 設定,上面那樣的貼法將各個功能簡單化我覺得不錯
如果對流程的概念熟悉,其實上面的資訊很夠了;

作為參考,希望能給您帶來一些靈感。
(而且其實幾乎可以直接用上面的程式碼去串,要改一些參數傳遞 config 就是)

Telegram 拿來當作 Bot 的接收其實挺不錯的
因為 Line 和 Discord 比較常拿來聯繫朋友,
每次有通知看到橘色的 icon 亮在那邊就會有強迫症要點掉

而且 Bot 通知太多的話聲音很煩。

Reference

How to Change Apache HTTP Port in Linux
Telegram Bot機器人申請與Webhook指令全紀錄
用 Python 打造自己的 Telegram Bot!

Github 也有很多已經做好的 Repo,建議直接找找看您使用的服務有沒有可以串的 !

End

![](https://i.imgur.com/888jFLr.gif)
2021.07.11