2015年12月23日 星期三

開發Slackbot的筆記

source: https://github.com/vampirewalk/vanguard

本來是想用swift開發的,但是後來想到Foundation在Linux上還沒實做 (這裡有進度),只好先用Go做了。

Slack Group設定

要先到Group裡的Apps & Custom Integrations加入Bots Integration,在這裡會得到token,等一下會給bot用。

開發

主要用的是https://github.com/nlopes/slack這個library,它實做了slack的Real Time Messaging API,底層走的是WebSocket。做出來的效果嘛,以我的slackbot為例,如下圖,只要在channel裡面貼出github repo的訊息,bot會自動返回repo的語言、star、issue數。

repobot

簡單的範例開始改,ConnectedEvent就是bot連上group,MessageEvent就是bot接收到訊息,依此類推。我的bot就是在MessageEvent把接收到的訊息做分析,如果包含Git repo,就透過https://api.github.com/repos/[user]/[repo]這個endpoint拿到repo的資料,然後再回傳。

Heroku

Bot做好之後要找個雲端放,目前我用Heroku,可以參考https://devcenter.heroku.com/articles/getting-started-with-go#introduction。要讓Go可以在Heroku上跑需要三個步驟

  1. Godep
  2. Procfile
  3. env

Godep

整個project做好後,用godep管理vender code。

godep save -r

Procfile

這邊用的是worker

worker: slackbot

設定env參數

可以透過Heroku的管理介面或是透過command設定

heroku config:set SLACK_TOKEN=YOUR_REAL_TOKEN

然後在Go裡面用下面的方式拿到實際參數,這樣就可以避免在open source時洩漏token了。

token := os.Getenv("SLACK_TOKEN")

本地測試

可以在本地建立一個.env檔,內容就是

SLACK_TOKEN=YOUR_REAL_TOKEN

然後用heroku local測試,這樣一樣可以拿到參數。

注意事項

有一點要注意的是現在bot似乎不能主動加入channel,必須先在channel裡面用invite指令邀請bot進聊天室才能讀到訊息。

/invite YOUR_BOT_USERNAME

2015年12月17日 星期四

一個關於遵循多個Swift 2.0 protocol extension的問題

問題

最近練習用Protocol-Oriented Programming實做可移動以及縮放的View,遇到一個很有趣的問題,假設有以下兩個protocol

protocol Draggable: class {
    var view: UIView { get }
    var initialLocation: CGPoint { get set }
}

extension Draggable where Self: UIView {
    var view: UIView { get { return self } }
    var parentView: UIView? { get { return self.view.superview } }
}
protocol Scalable: class {
    var view: UIView { get }
}

extension Scalable where Self: UIView {
    var view: UIView { get { return self } }
    var parentView: UIView? { get { return self.view.superview } }
}

然後實做一個View遵循這兩個protocol

public class VWView: UIView, Draggable, Scalable {
    var initialLocation: CGPoint = CGPointZero
}

這時候會發生compile error,告訴你
Type 'VWView' does not conform to protocol 'Scalable'
Type 'VWView' does not conform to protocol 'Draggable'

怎麼一回事呢?

原來是因為兩個protocol都有var view: UIView { get { return self } }的default implementations,編譯器不曉得該用哪一個,乾脆來一個編譯錯誤。

解法

其實解決方法很簡單,就是在class裡實做這個值,這樣編譯器就知道該用class裡面的這個了。

public class VWView: UIView, Draggable, Scalable {
    var view: UIView { get { return self } }
    var initialLocation: CGPoint = CGPointZero
}

Reference:
http://stackoverflow.com/questions/31586864/swift-2-0-protocol-extensions-two-protocols-with-the-same-function-signature-c

2015年12月9日 星期三

Parse Cloud Code初體驗

最近在我的App背包客住宿裡需要新增一個上傳住宿資料的功能,由於App是用Parse.com當後端,所以可以選擇用Client SDK直接上傳或是呼叫Cloud Code去管理Data。這次我選擇使用Cloud Code,這樣之後如果要對上傳上來的資料做篩選或是處理都可以立即生效,不用等下一版App上架。

安裝步驟那些就不說了,網路上資料一大把,紀錄幾個自己遇到的問題。

beforeSave

我一開始以為beforeSave會比function早執行,結果卻相反,是先執行function,在儲存前才執行beforeSave。

Geocode

住宿資料上傳前,需要先對Address做Geocode,這個部份可以使用Google Maps API達成,在Geocoding with Google Maps API via Parse Cloud Code這個stackoverflow上的問題有人提供了作法,但是語法跟現在的有點不同,在原答案是用success: error:去分別處理呼叫成功與失敗,現在要用

Parse.Cloud.httpRequest({
  url: 'http://www.example.com/',
  followRedirects: true
}).then(function(httpResponse) {
  console.log(httpResponse.text);
}, function(httpResponse) {
  console.error('Request failed with response code ' + httpResponse.status);
});

then之後接的第一個function處理成功,第二個處理失敗。
一般在Parse Object裡我們會用GeoPoint表示地理位置,那麼從Maps API回來的資料要怎麼轉換為GeoPoint呢?
假設我的Hostel物件有一個欄位叫做location,而它是GeoPoint型別,那麼用下面這樣的轉換方式。

var lat = geocodeResponse.results[0].geometry.location.lat;
var lng = geocodeResponse.results[0].geometry.location.lng;
var point = new Parse.GeoPoint({latitude: lat, longitude: lng});
hostel.set("location", point)

除錯

目前還沒有找到什麼好方法debug,只能在code裡多log,然後在command line用parse log看結果。

console.log(request.params);

2015年12月4日 星期五

Swiftlint - 維護coding style的好工具

https://github.com/realm/SwiftLint

在多人協作開發專案時,遵守團隊訂出的coding style是一個好習慣,可以讓其他協作者更容易看懂我們產出的程式碼。Realm釋出的這個tool就可以幫助我們將專案中不符合coding style的地方用warning甚至是error標出,利用Xcode幫我們自動把關,節省其他協作者幫我們檢查coding style的時間。目前Swiftlint是遵照GitHub’s Swift Style Guide 做檢查。

安裝

brew install swiftlint

設定

lint

在target的run script加入

if which swiftlint >/dev/null; then
  swiftlint
else
  echo "SwiftLint does not exist, download from https://github.com/realm/SwiftLint"
fi

然後在Xcode裡Build專案,哇,怎麼連Pod裡面的code都檢查了呢?

在專案root path新建一個 .swiftlint.yml 檔案,輸入

included:
  - YourProjectSourceDirectiryName

把YourProjectSourceDirectiryName替換成你的專案原始碼資料夾名稱就可以只檢查自己專案的coding style囉。