2018年6月27日 星期三

親愛的奇利,謝謝你來到我們家

20090124 - 撒嬌

幾天前在辦公室得知奇利過逝,遲疑著幾分鐘後,就請個特休搭高鐵返鄉。一路上淚也止不下來,沒想到早上加油時送的面紙派上了用場。和奇利的緣分近 13 年吧!在我們家處境最糟的時候,加入的成員。

還記得剛來我們家時,傲氣的奇利還不准人摸他的頭,每次要摸就立刻轉身,隨著彼此的生活步調同步著,我教會奇利如何上下樓梯,奇利甚至就學會了不在家裡亂尿尿,忍不住時就在廁所方便,實在貼心。

回顧起來,奇利過得也不錯,只有一開始一兩年住在籠子裡,後來家裡環境比較乾淨後,就不在需要籠子,可以隨處挑喜歡的地方睡覺,甚至跟我們一起吹冷氣,接著陪著我們到處輕旅行,追過每一年的燈會、國際上知名的黃色小鴨以及不少台灣老少咸宜的景點,或是來北部待過我租的每一間房間,這種情誼不是寵物兩字可道盡,只有家人可以詮釋情誼。

近一年奇利健康老化,來台北時,不知是不是長途關係,走著走著就倒下了!而後才知道,他的生命到了末曲,老化帶來的心臟病,心臟瓣膜脫垂會讓他喘,吸不到空氣,開始吃藥、開始緩慢的步調。

回顧起來,我們是不是用著藥物強制延續著緣分?安樂死真的是個課題。但人竟然可以這樣決定別人的生命?
定期進入全氧室能得到紓解、每天早晚的藥物控制可以穩定作息,卻都換不回那可以快樂奔跑的奇利。

喜歡那爽朗的笑容,這樣癡癡的過生活。
非常感謝和奇利的緣分。

他可能只是你的一個寵物,但你卻是他的全部。
越多的互動,真的越難釋懷,只能朝著,奇利,你終於不用在吃藥了,可以開心自在的到處散步

謝謝你來到我們家


回矇一笑

2018年6月2日 星期六

[Python] Line Chatbot 開發筆記 - Echo Service @ Ubuntu 18.04

LineChatbot01

入口:https://developers.line.me/en/
文件:https://developers.line.me/en/docs/messaging-api/overview/

登入開發者後,先來個 Add new provider ,此例為 StudyLineChatbot ,接著選擇 Messaging API,填寫完基本資料後,再按幾下就創建了

其中,重要的資訊:
  • Channel secret
  • Channel access token (long-lived)
而 Channel access token (long-lived) 預設為空,要按個 Issue 來產生一組。

接著,Webhook URL 要填寫對接的 API 位置,該 API 必須提供 https 服務,另外再把 Auto-reply message 關閉。如此一來就完成 Line Chatbot 的設定。

接著談談 Webhook URL 的實作方式,此例用 Python 的 Flask 和 line-bot-sdk-python 完工,簡短程式 hello.py 如下(其實就是 https://github.com/line/line-bot-sdk-python README 範例程式,多增加一點輸出訊息):

from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

app = Flask(__name__)

line_bot_api = LineBotApi('YOUR_LINE_CHATBOT Channel access token (long-lived)')
handler = WebhookHandler('YOUR_LINE_CHATBOT Channel secret')

@app.route("/", methods=['POST'])
def callback():
    print("[INFO] Get request:")
    print(request.__dict__)
    print()

    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']
    # get request body as text
    body = request.get_data(as_text=True)
    print("[INFO] Request Body: ---\n"+body+"\n---\n")

    # handle webhook body
    try:
        events = handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    print("[INFO] Handle Events \n")
    for event in events:
        if event is None:
            continue
        if not isinstance(event, MessageEvent):
            continue
        if not isinstance(event.message, TextMessage):
            continue
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text=event.message.text)
        )
    return '{}'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080)


然後,選擇在 Ubuntu server 上,透過 nginx 跟 uWSGI 設定。

Nginx:

location / {

try_files $uri @chatbot;
}

location @chatbot {
include uwsgi_params;
uwsgi_pass unix:/tmp/chatbot-uwsgi.sock;
}


uWSGI:

$ vim /etc/systemd/system/chatbot.uwsgi.service
[Unit]
Description=uWSGI Emperor
After=syslog.target

[Service]
ExecStart=/usr/local/bin/uwsgi --ini /path/project/chatbot-uwsgi.ini
RuntimeDirectory=uwsgi
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

$ vim /path/project/chatbot-uwsgi.ini
[uwsgi]
base = /path/project/

app = hello
module = %(app)
home = %(base)/venv
pythonpath = %(base)
callable = app

socket = /tmp/%n.sock
chmod-socket = 666
logto = /var/log/uwsgi/%n.log


後續都統一靠以下招數更新:

$ sudo systemctl restart chatbot.uwsgi

以及 hello.py 輸出的資料和運行結果,都可以在 /var/log/uwsgi/chatbot-uwsgi.log 找到:

[INFO] Get request:
{'environ': {'QUERY_STRING': '', 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'application/json;charset=UTF-8', 'CONTENT_LENGTH': '237', 'REQUEST_URI': '/', 'PATH_INFO': '/', 'DOCUMENT_ROOT': '/etc/nginx/html', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_SCHEME': 'https', 'HTTPS': 'on', 'REMOTE_ADDR': '###.###.###.###', 'REMOTE_PORT': '34994', 'SERVER_PORT': '443', 'SERVER_NAME': 'chatbot.example.com', 'HTTP_X_LINE_SIGNATURE': '#####################', 'HTTP_CONTENT_TYPE': 'application/json;charset=UTF-8', 'HTTP_CONTENT_LENGTH': '237', 'HTTP_HOST': 'chatbot.example.com', 'HTTP_ACCEPT': '*/*', 'HTTP_USER_AGENT': 'LineBotWebhook/1.0', 'wsgi.input': <uwsgi._Input object at 0x7f0d8a4477c8>, 'wsgi.file_wrapper': <built-in function uwsgi_sendfile>, 'wsgi.version': (1, 0), 'wsgi.errors': <_io.TextIOWrapper name=2 mode='w' encoding='UTF-8'>, 'wsgi.run_once': False, 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.url_scheme': 'https', 'uwsgi.version': b'2.0.17', 'uwsgi.node': b'###########', 'werkzeug.request': <Request 'https://chatbot.example.com/' [POST]>}, 'shallow': False, 'view_args': {}, 'url_rule': <Rule '/' (POST, OPTIONS) -> callback>, 'url': 'https://chatbot.example.com/'}

[INFO] Request Body: ---
{"events":[{"type":"message","replyToken":"###############","source":{"userId":"##############","type":"user"},"timestamp":###########,"message":{"type":"text","id":"###########","text":"HelloWorld"}}]}
---