0%

建立 LINE Notify 服務

第一步先到 LINE Notify 管力登入服務去建立一個服務

接著按下登入服務

把該填的填一填,要注意的是 Callback Url 這邊先暫時先打 localhost:5500,等頁面上傳後再回來改 domain


建立完成之後就到剛剛填的信箱去收信點下網址並啟動服務

這樣第一步就建立完成嚕!

動工

接著上次的透過上次的專案並使用 flask_restful 來改造成 Restful 格式的專案(後面開發會比較輕鬆)
因為我們會讓前端呼叫,CORS 設定成*比較輕鬆,若是有要設定跨域問題的在這邊設定哦 👏
加入套件到 requirements.txt

1
2
3
4
flask==1.0.2
Flask-RESTful==0.3.6
Flask-Cors==3.0.6
requests==2.22.0

透過使用 flask_restful 幫忙轉發並管理路由
將 api.py 改為以下的 code

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from flask_restful import Api
from flask_cors import CORS
from controller.notify_controller import NotifyController

app = Flask(__name__)
CORS(app, resources={r"*": {"origins": "*", "supports_credentials": True}})
api = Api(app)

api.add_resource(NotifyController, '/notify')

if __name__ == '__main__':
app.run(debug=True)

這邊我使用 Postgresql,並在專案底下建立一個 lib/ 的資料夾,裡頭放著名為 db.py (這裡還是要記得建立 init.py 哦!)
這邊只簡單建立 Context Manager 讓連線可以被我呼叫。
db.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import psycopg2
import psycopg2.extras

class Database():
conns = []
def __enter__(self):
return self

def connect(self):
conn = psycopg2.connect(
database=PG_DB,
user=PG_USER,
password=PG_PWD,
host=PG_HOST,
port=PG_PORT
)
self.conns.append(conn)
return conn

def __exit__(self, type, value, traceback):
for conn in self.conns:
conn.close()
self.conns.clear()

新增一下資料夾名為 controller,建立 init.py & notify_controller.py,這邊搭配 with 做資源管理器,
DB 部分則見黎一個 notify 表,然後一個 token 的欄位。

在這個 class 類別下方法只要輸入 def get/post/put/patch/delete 的函式就可以對應 CRUD 的功能了!

notify_controller.py code is:

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
from flask_restful import Resource, reqparse
import requests
import json
from lib.db import Database
import psycopg2.extras

class NotifyController(Resource):
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('code', required=True, help='code can not be blank!')
args = parser.parse_args()
code = args['code']
client = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': 'YOUR_REDIRECT_URI',
'client_id': 'YOUR_CLIENT_ID', 'client_secret': 'YOUR_CLIENT_SECRET'}
r = requests.post(
'https://notify-bot.line.me/oauth/token', data=client)
req = json.loads(r.text)
if req['status'] == 200:
token = req['access_token']
# Here is use PostgreSQL, you can change your love db
with Database() as db:
with db.connect() as conn:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
f"INSERT INTO notify(token) VALUES ('{token}')")
return {'access_token': req['access_token']}, 200
else:
return {'message': r.text}, 200

以下這三個要設定成你的當時輸入的字串

  • YOUR_REDIRECT_URI
  • YOUR_CLIENT_ID
  • YOUR_CLIENT_SECRET

若不清楚 Client_id & Client_secret 的話就回到 Notify 頁面並點選剛剛建立的服務就能看到 token 了

建立一個 views/ 的資料夾,並建立兩個檔案

  • notify_index.html

    這邊需要修改 client_id 以及 redirect_uri
    這個檔案主要功能是讓使用者有個可以按按鈕觸發的地方,當然這只是個範例,實際上線可能就會直接 call API 導向 LINE Notify 頁面等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Link Notify</title>
</head>
<body>
<button onclick="Auth();">點選這裡連結到LineNotify</button>

<script>
function Auth() {
var URL = "https://notify-bot.line.me/oauth/authorize?";
URL += "response_type=code";
URL += "&client_id=YOUR_CLIENT_ID";
URL += "&redirect_uri=YOUR_REDIRECT_URI";
URL += "&scope=notify";
URL += "&state=nostate";
window.location.href = URL;
}
</script>
</body>
</html>
  • notify_confirm.html

    修改 ajax 裡的 url,將網址換成之前部署的網址,若忘了可以在部署一次 sls deploy
    當導回來的時候透過 ajax 來呼叫 serverless api 讓後端發 requests 給https://notify-bot.line.me/oauth/token來獲取access_token

因為這個路由有 CORS 的問題,若透過瀏覽器來的話會出錯,這邊則是讓後端去處理

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>message check</title>
</head>
<body>
verify messate:
<div class="message"></div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>

<script>
var urlParams = new URLSearchParams(window.location.search);
var notify_code = urlParams.get("code");
var state = urlParams.get("state");
$(function() {
$.ajax({
url: "https://SLS_URI.execute-api.us-east-2.amazonaws.com/dev/notify",
type: "POST",
dataType: "json",
data: { code: notify_code },
success: function(response) {
console.log(response);
$(".message").text(response["access_token"]);
},
error: function(xhr) {
console.log(xhr);
alert("Ajax request 發生錯誤");
}
});
});
</script>
</html>

接著就是測試程式碼是不是正常的,把這兩個檔案丟到 S3 上面


把 object url 複製到 LINE Notify 上的 callback url 欄位,這樣就可以實機測試了 🤣

接著按下 index page 的按鈕應該就會看到連動成功(進資料庫應該也會被 insert 一筆)

到了這邊如果沒問題真的是恭喜你,我踩了好多坑 Orz

參考

資源管理器
flask CORS
CORS

前言

Amazon Simple Storage Service(簡稱 AWS S3)是 AWS 的一個線上儲存服務,用戶能夠輕易把檔案儲存到網路伺服器上,可能有些朋友會把它當成雲端硬碟看待,但其實還能像是傳靜態檔案上去,若有寫前端框架的朋友通常都會需要把程式碼打包成靜態檔案再部署,然而這邊把他打包送上去後,S3 會給一個名為 Object Url 的網址,可以透過這個網址去執行網頁裡面的內容(如 AJAX、Axios…),並可以透過 cloudfont 去弄成 CDN(扯遠了),簡單說他還有很多不一樣的功能應用等著大家去用~

因為 serverless 只能放關於 API 的程式碼,因此我們需要把 html 靜態檔案放在 S3 裡,

接下來就來建立一個公開的 S3 容器,這邊就不會提到權限如何控管哦!

建立一個公開的 S3 容器

建立一個名為2019-it-30-bucket的容器,地區就選與 Lambda 同樣的地區,直接按下左下角的create來建立。

建立完會像這樣

接著點進去後在點到第三個選項會看到 Block of public access 是啟動的狀態,因為我們接下來需要讓我們的頁面可以外部使用

就把 check box 的按掉,在按下 Save

要輸入confirm就修改完成嚕

接著一樣點到第三個個選項,選到Access Control List,看到Access for bucket owner這個選項的Canonical ID,選項都全部打勾之後再來調整,這樣之後的 HTML 檔案外面就看得到了。

測試

接著上傳一個index.html的檔案來測試

1
2
3
<html>
<h1>Hello world</h1>
</html>

上傳後下拉式選單選擇public read的選項,因為一個檔案在 S3 就算是一個 Object,所以一定要讓它能對外,然後按下左下的 Upload

成功後點進這個檔案,並按下方的Object URL,看到 Hello World 的話就代表成功囉,這時可以開個無痕測一次,確認一下沒有錯誤 😃

參考

AWS S3 wiki

LINE Notify 顧名思義就是通知屬性的服務,這個服務不是 LINE 的 Message API,千外別把這兩個搞在一起哦!

在實作前要先認識一下在接的 api 服務原理
首先先參考LINE Notify 官網
開頭的介紹:

Overview: Becomes a provider based on OAuth2 (https://tools.ietf.org/html/rfc6749). The authentication method is authorization_code. The access token acquired here can only be used for notification services

不負責任翻譯: 這個服務是基於 OAuth2 實作的,授權模式(grant_type)是 authorization_code 參考
access_token 則是只能讓通知服務所使用的一個鑰匙

1
更詳細的流程可以參考 https://blog.yorkxin.org/2013/09/30/oauth2-4-1-auth-code-grant-flow.html

The host name for authentication API endpoint is notify-bot.line.me.

然後 API 的網址是 notify-bot.line.me

接著我們來看看流程圖
https://notify-bot.line.me/doc/en/

  • 當使用者拜訪你的網站時,會導向 LINE 請求認證
  • 認證過了之後會回傳一個名為 code 的參數
  • 接著網站需要持這個 code 在去找 LINE 討東西
  • 討成功後就會拿到一個 access_token
  • 網站就會知道這個 access_token = 來註冊的使用者
  • 然後就可以透過 access_token 發送通知給使用者了?

事前準備

首先就是要先加入他好友,如果之前有不小心封鎖的話要記得解除封鎖哦,不然後續會收不到消息。
https://ithelp.ithome.com.tw/upload/images/20190903/20111481Zno98NSHwL.png

下一篇會帶著時做出簡單的 index.html + 使用 Serverless 蓋我們第一個 API 來做認證。
會使用到 LINE Notify 的 API 為以下三個,不清楚裡面實際上功能的朋友可以嗑一下官網文件

1
2
3
GET https: //notify-bot.line.me/oauth/authorize -> 前往認證拿到 code 參數
POST https://notify-bot.line.me/oauth/token -> 拿 code 參數換 access_token
POST https://notify-api.line.me/api/notify -> 發送訊息

其他

今年中有帶著朝陽的學弟妹手把手實作 LINE Notify,如果只想自己用的話可以參考我之前簡報

LINE Notify
如何快速建置一個 LINE Notify 的服務

透過 npm 全域安裝 serverless 指令

1
npm install serverless -g

因為我是用 python3 在 AWS 上實作

  • --template可以選擇你想要的模板 (參考)
  • name service 的名稱
  • --path 專案路徑,若找不到資料夾時會自動新增
1
serverless create --template aws-python3 --name aws-wsgi-flask --path aws-wsgi-flask

加入套件

接著後面會需要使用套件,而 python 這邊會認得 requirements.txt 這個檔案,因此我們需要用 serverless 的 plugin 幫我們處理,讓專案在 deploy 上去之後會透過 requirements.txt 去安裝套件。

而接下來我們會需要一個介面幫我們處理一些麻煩事,這邊就選用 wsgi 來處理。詳細參考

1
2
sls plugin install -n serverless-python-requirements
sls plugin install -n serverless-wsgi

接著去看 serverless.yml 檔案最後出現下面這樣就成功安裝了。

1
2
3
plugins:
- serverless-python-requirements
- serverless-wsgi

然後新增一個 setup.cfg 並加入以下內容:

1
2
[install]
prefix=

由於 AWS Lambda 上都會將套件安裝在本地端(Lambda 上),因此在執行 pip install -t . -r requirements.txt 時會需要 setup.cfg。

接著選用 Flask 框架作我們開發的

1
2
echo "Flask==1.0.2" > requirements.txt
pip install -r requirements.txt -—user

serverless-python-requirements 同樣也支持 pipenv 哦,如果想完全擋掉的話可以參考

更改設定檔

置換以下內容至 serverless.yml,讓 WSGI 去處理路由。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
service: aws-wsgi-flask
provider:
name: aws
runtime: python3.7
functions:
api:
handler: wsgi_handler.handler
events:
- http: ANY /
- http: ANY {proxy+}
custom:
wsgi:
app: api.app
plugins:
- serverless-python-requirements
- serverless-wsgi

測試 wsgi

因為我們不是單純只用 python 架設,所以要將 handler.py 替換成 api.py,並加入以下內容

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route("/", methods=['GET'])
def handler():
return 'hello world'

if __name__ == '__main__':
app.run(debug=True)

到這邊基礎設施都弄好了,但總不能部署才測試吧,所以這時候我們就出動

1
serverless wsgi serve

透過 wsgi 去幫忙起一個 local server 供使用者可以測試,而且每次儲存都重新整理一次哦!詳細用法請參考

接著用 postman 來測試一下

燈燈,hello world 出來了~

最後也是最重要的一個步驟『部署』
只是說部署前需要有 AWS 的 secret key & token

建立 AWS 存取金鑰

首先要先有個 AWS 帳號(廢話) ,如果沒有綁定信用卡記得去綁定才能繼續,接下來就可以去就可以去建立鑰匙囉!

如上圖,按下紅色框框的部分,接著按箭頭指的按鈕,就會建立一個我們要的金鑰囉!

如上圖所示,可以下載 key,AWS 不會幫忙保存 Secret key,若這視窗關掉的話 key 就不見了,所以使用者可以自行下載檔案保存,以免哪天要部署的時候找不到 key。
接著就將這兩個參數加入環境變數中:

1
2
export AWS_ACCESS_KEY_ID=<your-key-here>
export AWS_SECRET_ACCESS_KEY=<your-secret-key-here>

或是藉由 serverless 指令來幫忙加入參數:

1
serverless config credentials --provider aws --key <your-key-here> --secret <your-secret-key-here>

這步驟很重要哦,不然 AWS 不認識你就不給你上傳了 ?

最後就是下 serverlesss deploy進行部署

這邊 AWS 幫忙建立一個含有 SSL 的 domain,可以直接對這個網址下

1
2
endpoints: ANY - https://4omvn4z7re.execute-api.us-east-1.amazonaws.com/dev
ANY - https://4omvn4z7re.execute-api.us-east-1.amazonaws.com/dev/{proxy+}

接著把網址丟到瀏覽器上來試試看

出現 hello world就成功囉!

參考

前言

那因為現任公司的服務都是基於 AWS,如此這般我就接觸到 Serverless(以下簡稱 sls) 這個框架啦 (想更深入了解 FaaS 架構可以參考 AWS)


為什麼要 Serverless

Serverless 架構是一個基於 FaaS(Function as a Service) 實作的一個服務,讓開發者可以更專注在開發功能,將 yml 檔設定好其餘維運的問題都交給 AWS、Google、Azure 這些服務商去處理,只要把信用卡準備好就好(?),讓開發者在寫完程式之後不用煩惱部署得問題,減少的不少的麻煩事。

以用一個 Ruby on Rails 寫的機器人為例,在寫完個 webhook function 後,設定路由然後部署,一般主機或是虛擬機就很麻煩,要安裝佈署環境以及一安裝堆相關套件,完了之後設定 Domain 什麼的有夠花時間。但若是使用 heroku 部署就很方便,Login-Create-Deploy 馬上就結束,機器人馬上就上線了,實在是俗又大碗呀~

但我覺得像是 LINE Message 這種 stateless 的服務,且只要一個 function + route 就能實作的程式最適合 Serverless 了,讓專案可以簡潔有力,只要寫一個 function+yml 設定並打個指令就部署了,而且 domain 還附贈 SSL,將服務交給 AWS 也不需要擔心,整個就是超級方便啊!

serverless.yaml 是該專案的設定檔,可以把它想像成是 CloudFormation 的 wrapper,事實上也的確是這樣,serverless 背後會把他轉成 CloudFormation 的 template 去發佈。這個設定檔是 serverless 的精髓所在,一切有關 API Gateway 和 Lambda 的設定都在這邊,而底層所需要的資源,他都幫你配置好了,不需要操心。 ref


Cold start 問題

依照我目前使用的結果下來,在 heroku 以及 AWS Lambda 同時睡著的情況下,AWS 起床的回應速度大概 1 秒左右,而 heroku 則大概落在 10~15 秒(參考)。

如下圖所示的免費方案,基本上開發階段應該是不至於到 100 萬/月 個請求吧 ?,所以這部分就別擔新大力地給他用下去!

AWS Lambda 限制

安裝與使用

serverless.yaml 的設定參考
serverless.yaml 的變數傳遞


結論

接下來會是使用 Serverless 這個框架去實作的,使用的語言為 Python,至於為什麼會選它呢,最主要原因還是在官網上有很多 plugin 可以用 ( NodeJS & Python ),開發時資源相對多,接著是 AWS Lambda 的 Code-start 最快的就屬於 NodeJS & Python 了,而我原本就是寫 Ruby 來的,自然就選一個寫法相近的語言啦(好像有點牽強),如果說你想了解 runtimes 相關的問題的話可以參考這篇

環境

參考

前言

本次小聚是 COSCUP 之後的第一場,趁著熱血玩兩個大型研討會的空檔舉辦了這次的小聚,先感謝各位的參與,別忘了定期關注社群,我們會在上面放出第一手 chatbot 小聚消息。
Chatbot Developers Taiwan

小聚開始

開場由 Evan Lin 為大家帶來 LINE 的第一手消息,首先是 LINE 不在信義區啦(抓錯重點),是 Flex message 更新啦 ,如下圖所示,他可以做出更多種不同的應用了,也有些實例可以供大家參考 🎉

Flex Messages
另外出了一個模擬器,如果再使用 Flex message 不是太熟悉整個主體的話可以參考 ⬇️,目前還在 beta 階段,若有問題直接寫信去 “問候“ 一下 🤣
Flex Message Simulator

中文版的文件也上線了,可以到官網去搜尋哦!

議程

簡報連結

第一位由 LAE — 郭冠宏 (ggm)為我們介紹 Confacts 訊息查證系統,在裡頭把使用者分為兩類

  • 收消息的人 🙋‍♂️
  • 編輯訊息的人 👨‍💻

在上面看得到的資料都是透過 「工人智慧」誕生的,所以看到每一個真假消息都要歸功於這些編輯們 🎉
而 Confacts 也秉持的 g0v 開放原始碼的精神把東西都 open,供大家去使用,像是 美玉姨 以及 趨勢科技防詐達人 都是使用他們的資料庫建立出來的,如果有需要請去找 GGM 大大~
上次參加 g0v 小聚有稍微了解 Confacts 是做什麼的,聽到這次的講題就讓我更深入了解整個運作流程了,光想到要控制狀態就覺得苦惱,也因 Confacts 讓我瞭解到更多詳細流程~以下是他們的網站,討厭看到人家亂轉發消息嗎?直接加入消息並一起打造工人智慧的平台~
了解更多: https://cofacts.g0v.tw


接著由 酮好的創辦人 撒景賢 為我們帶來 聊天機器人與社群經營,主要是透過機器人來幫忙定時推播訊息給讓使用者下單後會收到提醒訊息而不會跑單,也建議大家把機器人就定義他為機器人,別讓使用者誤會他是一個活生生的人,要定義好自己的機器人取向,最後提到了一個問題就是他認為工程師的毛病就是想要產品做完才去找市場,可能的意思是且戰且走(?),或許直接吸收市場的經驗也是個不錯的方法(筆記)。


再來就是台北商業大學的 溫明輝 教授為我們帶來 商用聊天機器人 UX 的十三種設計原則

商用聊天機器人 UX 的 13 個設計原則 (heuristics)

首先介紹了微股力與記帳雞上面應用中的實例以及一些與使用者互動的案例分享,兩隻機器人都被 LINE 收為官方帳號囉!有興趣的朋友可以去搜尋一下~ 🔍

  1. 確認目標族群的關鍵需求:要找到用戶滾動增長的點
  2. APP 的成功是無法直接複製的:找到 app 上的缺點去卡市場定位點。
  3. 人們可能並不想「聊天」:這邊分享的案例是讓 記帳雞 出來湊人鬧目的是讓使用者記得他並去引導記帳 。
  4. 擬人化 vs 機器主張定位:若在一開始實作時是以特定主題去做開發的話就該讓他是對應功能機器人,別想著機器人可以做原本的事又要去回應使用者的任何話。
  5. 對話是互動並不限於對話(dialog):有時候適時地在圖片上加入一些訊息也能讓使用者去自然觸擊。
  6. 讓使用者隨時握有主導權:因為人們可以隨時關掉它,就也意味著使用者的抗拒心不會那麼大,主要還是別讓使用者封鎖他~
  7. 不一眛追求介面的簡潔性:根本的需求滿足之後使用者還是會去用,把互動愉悅感弄出來,這邊例子是放在群組使用久了,有些使用者會自然地去記憶這他的指令。
  8. 考量對話訊息的時間堆疊:使用者因為訊息過多而演變成去點訊息已成敷衍的行為了。
  9. 避免一視同仁的廣播訊息:這邊分享的做法是透過工具去紀錄使用者的互動軌跡,且每個每個感應區塊都會有相對應的 tag,當後台在準備做推薦時可以針對分眾去做對應內容的推播。
  10. 用數據驅動的同理心:將機器人加入群組並紀錄使用者打過的指令,當今天已經封鎖的使用者在特定情況下看到群組裡面有其他使用者在打相關指令時或許會勾引出以前的興趣藉此解除封鎖並回歸。
  11. 用 UI 來補償 AI 的能力極限:藉由專家寫的文章以及當使用者點選到特定的內容時會直接把文章透過 LINE 嵌在訊息下方,讓使用者透過 UI 去看到相關內容,讓他看起來有點類似 AI。
  12. 個人化是 chatbot UX 極致:或許是因為客製化的關係所以當然個人化是最好的 🤣
  13. chatbot 是無所不在的意識:隨著運算越來越快,人可以隨心所欲去取用環境資訊,需要有個個人化服務,就能透過 chatbot 來搜集並歸納出對應的訊息內容。

閃電秀

最期待的其中一個部分就是閃電秀啦,因為每次閃電秀不僅可以展謝自己的練習成果,也可以發現到其他使用者對於同樣開發內容所想出不一樣的應用!
第一個由 斑斑 為大家帶來市容幫手,主要是因為現在各種公家機關幾乎都用 LINE 在做聯繫,但一個里長擁有上百個群組會造成很多收集訊息上的困擾,那這邊講者就希望透過提案的方式來尋求參與者一起參與這個提案,有興趣的可以參考一下他的簡報哦!

接著就是由小弟我帶來的閃電秀,這次主題是使用 kamigo 打造的一個紀錄健身記錄的機器人,透過 Restful 格式的內容以及對應網址的樣式做出一個類似網頁版的機器人來記錄成績,以下是我的簡報以及 Github,有興趣可以參考一下 😃

> [louis70109/muscle_man](https://github.com/louis70109/muscle_man)

這是一個基於 kamigo 實作 LINE bot 來紀錄重訓數據的 肌肉仔 聊天機器人 在健身的過程中飲食固然重要,但往往訓練的人都是透過通靈的方法來選取適合自己的訓練重量,而透過數據的記載則是突破不可或缺的因素,因此產生了這個…

再來由 Fly Chang 帶來的在宅醫療服務,主要是幫助各地需要居家醫療的人讓他們有資源可以透過地圖的方式去找到,並想整合機器人提供在宅醫療診所情報、長期照護、藥物使用、衛教資訊…等等的資訊,有興趣的朋友可以點選以下網址去觀看哦!
台灣在宅支援診所聯盟
HSCA 在宅支援診所聯盟是秉持開放、共享、共學精神的網路平台社群。 資源地圖邀請在宅醫療團隊提供服務資訊,讓診所服務可以變得更透明、更公開,產生正面的循環。

最後由 kevin 介紹一下有關於 dialogflow 串接 LINE bot 的簡單介紹,並順便推廣了 voice hackthon,有興趣的朋友可以參考一下哦

若是 dialogflow+LINE bot 可以參考我之前寫的文章
Chatbot Taichung #2 工作坊心得分享

結論

雖然每次下班都從台中直接殺上台北辦小聚很累,但往往在小聚結束時總是收穫滿滿,因為大家的熱情交流讓我學到了不少東西,謝謝各位的支持與參與,小弟拜託大家有報名要來啊~看在火山孝子的份上(咦?)就來參加吧 🙏
Chatbot TW 很樂意接收到不同的意見,若有好的建立不好意思公開可以私訊我哦!很樂意為大家解答 😁