2017年8月15日 星期二

用 Gmail API 讀取 Label 清單

平時的工作中,經常都會在伺服器中加入電郵通知,當有任何值得留意的狀況發生時,我會收到電郵說明內容;因為這樣能比較主動地帶出重要的資訊。然而,很多時候像運行記錄之類的電郵通知都沒有保留在伺服器,電郵一經發出,數據便不再存在。如果拿來作為數據分析或繪畫圖表,相信會有一點啟示。由於公司是使用 Gmail 系統,要做到這點,可以用 Gmail API。

我習慣用 Label 來把電郵分門別類;所以要達到以上目的,我得先取得 Label 的清單。以下是我的代碼:
<?php
//----------------------------------------------------------------------------------------
//  Gmail API DEMO
//----------------------------------------------------------------------------------------
//  Platform: macOS + PHP + Apache
//  Written by Pacess HO
//  Copyright 2017 Pacess Studio.  All rights reserved.
//----------------------------------------------------------------------------------------

header("Access-Control-Allow-Origin: https://www.pacess.com");
header("Access-Control-Allow-Methods: POST");
header("Content-type: text/html");
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Tue, 10 Mar 1987 00:00:00 GMT");

date_default_timezone_set("Asia/Hong_Kong");
mb_internal_encoding("UTF-8");
ini_set("memory_limit", "-1");
ini_set("default_socket_timeout", 10);
set_time_limit(0);

session_start();

//----------------------------------------------------------------------------------------
//  * Order is important
require_once "./libraries/vendor/autoload.php";

//----------------------------------------------------------------------------------------
//  Global variables
$_apiName = baseName(__FILE__);

//----------------------------------------------------------------------------------------
//  Check if logout
if (isset($_REQUEST["logout"]))  {
 unset($_SESSION["access_token"]);
 exit(0);
}

//----------------------------------------------------------------------------------------
//  Check if code provided for authentication
$client = new Google_Client();
$client->setAuthConfig("./client_secret.json");
$client->addScope("https://mail.google.com/");
$client->setRedirectUri("https://www.pacess.com/gmailGrabber");
if (isset($_REQUEST["code"]))  {

 $client->authenticate($_REQUEST["code"]);
 $_SESSION["access_token"] = $client->getAccessToken();

 $auth_url = "https://".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"];
 header("Location: ".filter_var($auth_url, FILTER_SANITIZE_URL));
 exit(0);
}

//----------------------------------------------------------------------------------------
//  Check if access token created
if (isset($_SESSION["access_token"]))  {
 $client->setAccessToken($_SESSION["access_token"]);
}  else  {

 //  Not yet
 $auth_url = $client->createAuthUrl();
 header("Location: ".filter_var($auth_url, FILTER_SANITIZE_URL));
 exit(0);
}

//----------------------------------------------------------------------------------------
//  Create Google service
$service = new Google_Service_Gmail($client);

//----------------------------------------------------------------------------------------
//  OK, everything is ready!
try  {
 if (!$client->getAccessToken())  {
  echo("Something happened...");
  exit(0);
 }

 $accessToken = $client->getAccessToken();  
 echo("Access token: ".print_r($accessToken, true)."<hr>");

 $labelsResponse = $service->users_labels->listUsersLabels("me");
 if ($labelsResponse->getLabels()) {
  $labels = $labelsResponse->getLabels();
  foreach ($labels as $label) {
   var_dump($label);
   echo("<hr>");
  }
 }
}  catch (Google_Auth_Exception $e)  {
 echo("### Error...");
}

?>

2017年8月13日 星期日

為 LINE-Bot 加入鏡頭抓取功能


我的私人助理是一個名為 Sita 的 LINE-Bot 程式,她除了能替我下載 Musical.ly 影片、下載 Packt 免費書、一般通知外,今日還加入了鏡頭抓取功能。這個 LINE-Bot 藏身的電腦是一台 Mac mini server,外接了兩台 USB 鏡頭,平時我會利用 OBS 來串流家中的畫面,但耗費的資源較多,最好是加到 LINE-Bot 中。

要在 PHP 下抓取鏡頭畫面,除了 OpenCV 外,似乎沒有太多方法。而我不想用 OpenCV,所以改為使用 exec 執行外部抓取程式 imagesnap。要安裝 imagesnap 只需要在 Terminal 輸入「brew install imagesnap」。以下是 LINE-Bot 的相關程式:
//  Default camera 1: Microsoft
$filename = date("YmdHis").".png";
$filePath = "/Users/Sites/sitaChanBot/".$filename;
$response = exec("/usr/local/Cellar/imagesnap/0.2.5/bin/imagesnap -w 1 $filePath");

$imageURL = "https://127.0.0.1/sitaChanBot/".$filename;
$previewURL = $imageURL;
$_lineBot->replyImage($replyToken, $imageURL, $previewURL);

//  imagesnap -d "USB 2.0 PC Cam"
$filename = "PC-".$filename;
$filePath = "/Users/Sites/sitaChanBot/".$filename;
$response = exec("/usr/local/Cellar/imagesnap/0.2.5/bin/imagesnap -w 1 -d 'USB 2.0 PC Cam' $filePath");

$imageURL = "https://127.0.0.1/sitaChanBot/".$filename;
$previewURL = $imageURL;
$_lineBot->sendImage($senderUserID, $imageURL, $previewURL);

2017年7月31日 星期一

修改 Python 模組


有一個 Django 項目需要搬到另一台伺服器;之前的體驗不好,三次安裝都各有新的問題,Django 並不如想像中好用。就像今次,MySQL 版本只差 0.1,出現了 datetime(6) 問題;出現 Django MySQL 介面問題;Python 模組問題。

把 MySQL 升級後,解決了 datetime(6),能直接匯入 MySQLDump 數據。可是卻出現另一個「Module not found」問題,得把原本的「django.db.backends.mysql」改為「mysql.connector.django」,不然會有「configparser」問題,這是 Python 3 的錯誤。之後就是「Can't multiply sequence by non-int of type 'tuple'」問題。得把 python3.5/site-packages/mysql/connector/django/operations.py 模組內的「bulk_insert_sql(self, fields, num_values)」修改成:
def bulk_insert_sql(self, fields, placeholder_rows):
   placeholder_rows_sql = (", ".join(row) for row in placeholder_rows)
   values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql)
   return "VALUES " + values_sql

2017年7月26日 星期三

SnowNLP


今日嘗試了 SnowNLP 這個 Python Module,用它來進行正體中文分詞。

把先前從 Facebook 抓下來的評語,抽了一少部份放進去;關鍵字得到 base, lasting 服, primavista 及貼, 似乎不太準確。查看學到的詞彚,零零碎碎,同樣沒有多大作用。至於情感分析,「但隻濕粉堅強..控油一流」得出 96.88%, 亦即是正評。這個倒是不錯。

由於上面的測試是用跟來的基本模型,對於特定的行業,應該要用特定的訓練素材。下一次要自行訓練看看,但先要收集同 一行業但不同公司的數據才有下一步。

2017年7月22日 星期六

解決 macOS Server 上的 pgsql: Unable to initialize module 問題


日前為我的私人助理加入擷取 Facebook 社交數據的功能 。本來一切運作良好,可是昨天突然卻抓取失敗。


最奇怪的是擷取程式內發送 LINE 訊息的功能如常運作,但連線的部份卻沒有被執行。於是以 SSH 登入服務器,手動執行 PHP 看看是甚麼問題,得到「pgsql: Unable to initialize module」。可是我一直使用 MySQL,沒有使用過 PGSQL...。


查看 php.ini 發現會導入另一個設定檔。


果真在設定檔內載入了 pgsql 的模組。只要在前面加上分號,儲存並重啟便能解決問題。

2017年7月11日 星期二

解決 Undefined symbols for architecture i386 錯誤


最近在試 Stripe 及 Websocket,發現添加到舊有項目時會出現「Undefined symbols for architecture i386」錯誤;若新建項目則沒有問題。


後來找到原來把「Build Settings」內的「Build Active Architecture Only」設定為「Yes」便能解決。

2017年7月4日 星期二

中文分詞


在機器學習的領域中,英文分詞在很多地方都能學到及應用得到,用 Python 的 TextBlob 也能處理;但是中文卻沒太多的資訊,而且中文的文字素材也少得可憐,得靠自己製作。幸好,還有一個名為 Jieba 的 Python 庫能輕鬆做出中文分詞,又能加入自訂義詞語。今日拿了 Sita 專頁的留言試了一下,不是完美,卻是一個不錯的開始。

2017年6月30日 星期五

正確安裝 Python 模組的指令


感冒及腸胃炎連環發生,連續兩晚充裕的自療後能消除感冒,但第一次得到腸胃炎,還未感覺到身體能擊退病毒的方法。今天唯有去見醫生,吃過幾劑藥,腸胃未見好轉,但精神已經恢愎,可以繼續研究。

停了一輪的 Machine Learning 是時候重拾學習的階段。我一直想開發出一個能分辨中文留言是否正評的模型;這個應該算是初級難度。說是應該,是因為英文的有很多,中文的卻一個也沒有;初級之餘,動手做的比較多,但又不是太過困難,所以應該是初級。然而,在我家中的 Mac mini 安裝 Python 模組,就算用 pip3 也會把模組安裝到 Python 2.7 的目錄下,實在奇怪。找了很久,才發現有人指最確切的做法是用「python3 -m pip install module」指令;畢竟 pip3 有可能被捷徑影響而變成 Python 2.7 的 pip。試過後果真有效。

2017年6月28日 星期三

「Sita 陳僖儀」Facebook 專頁的數據

今日編寫了一個小程序去讀取「Sita 陳僖儀」的 Facebook 專頁,嘗試找出一點啟發。

截止目前為止,整個專頁有 662 個貼文。


留言共 26,496 個。即平均每個貼文有 40.02 個留言。


留言最多的貼文有 17,926 個留言,大家估到是哪一個吧 (T_T)。


有 15,667 人留過言,即平均每個貼文有 23.67 個人留言;而留言得最多的粉絲是:


我同時也有拿反應數據,總共有 40,121 個,即平均每個貼文有 60.61 個反應:


有 12,455 人給過反應,即平均每個貼文有 18.81 個人有反應:


而各反應最多的是「讚好」,那是因為 Sita 離開後一段時間才出現「讚好」以外的反應:

以下是根據貼文時間以小時為單位,可以看出 Sita 最愛貼文的時間為:

2017年6月24日 星期六

用 LINE-Bot 下載 musical.ly 高清影片


女兒近兩個月來愛上玩 musical.ly 這個應用程式,拍了很多音樂影片。作為爸爸,很想保留她的成長片段,於是研究了一下。

試過用 Charles Proxy 作為中間人觀看影片流量,想從中找出影片的路徑;也是過把 iPhone 連接 iTunes,希望應用程式能把下載的影片分享到 iTunes,可惜沒有;用了好幾個方法也找不到。後來發現 musical.ly 自帶的分享到電郵功能,不過片中會加入帳號名字,且影片比原來的小,變成 480x852;但如果分享到 LINE 或 Copy Link 的話,則會是轉介到網頁畫面,內裡的影片沒有帳號名字之餘,解像度達 540x960。

要找出影片檔案,只要在網頁上的影片位置點擊滑鼠右鍵,選「Inspect」後會彈出視窗,反白了的文字。下方能發現影片的 MP4 地址:

於是我把女兒的影片,一個一個地分享到 LINE,慢慢地找出 MP4 地址,再一段一段地貼到瀏覽器的地址列並下載。

可是,這個方法太慢,女兒有些影片轉頭便刪了,就算的得到地址也無法下載。於是,我想到把下載影片的工作自動化;只要分享到 LINE 入面,我之前寫好的 Sita LINE-Bot,她便自動為我下載。要達到這個目的,得找出影片地址的方法。在 Chrome 中打開開發人員工具,觀察載入地址,會發現「https://www.musical.ly/rest/v2/musicals/shareInfo?key=Mzg4MTEyND...」這個連線;key 後面的就是影片分享地址中的 HTML 名字。觀看這個連線內容,看到 MP4 的實際地址:

於是我把 LINE-Bot 收到訊息的程式改了一下,只要是 musical.ly 的地址,便假設是影片分享,把地址的 HTML 名字解拆出來,然後加在「https://www.musical.ly/rest/v2/musicals/shareInfo?key=」後面,繼而讀取新地址內容;再把新內容的 JSON 解開,根據 videoUri 的地址進行下載。結果非常成功,以後我只要把影片地址分享到 LINE-Bot,Sita 便會自動下載影片。