2013/04/11

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. errors.Join が入った。
  3. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  4. Re: Go言語で画像ファイルか確認してみる
  5. net/url に JoinPath が入った。

golang_bot 先日、Lingr がとてもホットなので「Go言語」部屋を作ったのですが、Lingr のしきたりとして、その開発言語でbotを書くというのがあったので(要出典)、go言語でbotを書いた。
mattn/go-lingrbot - GitHub
https://github.com/mattn/go-lingrbot
機能としては3つ。
  • URL が貼り付けられたらそのページのタイトルをお知らせする
  • !godoc math という発言があれば、math パッケージの説明とドキュメントの URL をお知らせする
  • mattn++ などという発言があれば、カウントアップして通算++数をお知らせする
Google AppEngine で動いてます。

URLボット

まず URL のタイトルをお知らせする部分、Go言語は内部エンコーディングが utf-8 であり、オフィシャルからはキャラクタセットを扱えるライブラリが提供されていません。かつ、Google AppEngine 上で動かすので CGO を使った物も動かせません。しかし mahonia というパッケージが CGO 無しでキャラクタセットを扱えるのでそれを使う事にしました。
mahonia - Mahonia - a character-set conversion library for Go - Google Project Hosting

Mahonia is a character-set conversion library implemented in Go. All data is compiled into the execu...

https://code.google.com/p/mahonia/
得られた HTML をパースして meta タグを調べ、キャラクタセットを得た後に、title タグの内容を utf-8 に変換しています。 if charset == "" && n.Type == html.ElementNode && n.Data == "meta" {
    kv := make(map[string]string)
    for _, a := range n.Attr {
        kv[strings.ToLower(a.Key)] = strings.ToLower(a.Val)
    }
    if v, ok := kv["http-equiv"]; ok && v == "content-type" {
        if v, ok = kv["content"]; ok {
            for _, t := range strings.Split(v, ";") {
                tt := strings.Split(strings.TrimSpace(t), "=")
                if len(tt) == 2 && strings.ToLower(tt[0]) == "charset" {
                    charset = tt[1]
                    break
                }
            }
        }
    }
    if v, ok := kv["charset"]; ok {
        charset = v
    }
}
if n.Type == html.ElementNode && n.Data == "title" {
    if charset == "" {
        charset = "utf-8"
    }
    title = mahonia.NewDecoder(charset).ConvertString(n.FirstChild.Data)
}

Godocボット

GoDoc

Popular Packages bitbucket.org/jaybill/sawsij/framework code.google.com/p/biogo code.google.com/p/go...

http://godoc.org/
github からGo言語のパッケージだけを集めてドキュメントを表示してくれるサイトを利用しました。
このサイトは Accept: text/plain ヘッダを付けて要求すると、テキスト形式で応答してくれる機能があったので、その結果からパッケージの説明を抽出しています。

プラスプラスボット

まず発言から mattn++
mattn--
mattn+=3
mattn-=5
などという発言を抽出します。 var rePlus = regexp.MustCompile(`^\s*([a-zA-Z0-9_{^}]+)\+\+\s*$`)
var reMinus = regexp.MustCompile(`^\s*([a-zA-Z0-9_{^}]+)--\s*$`)
var rePlusEq = regexp.MustCompile(`^\s*([a-zA-Z0-9_{^}]+)\+=([0-9])\s*$`)
var reMinusEq = regexp.MustCompile(`^\s*([a-zA-Z0-9_{^}]+)\-=([0-9])\s*$`)
func parsePlusPlus(message string, callback func(nick string, plus int)) bool {
    if rePlus.MatchString(message) {
        m := rePlus.FindStringSubmatch(message)
        callback(m[1], 1)
        return true
    } else if reMinus.MatchString(message) {
        m := reMinus.FindStringSubmatch(message)
        callback(m[1], -1)
        return true
    } else if rePlusEq.MatchString(message) {
        m := rePlusEq.FindStringSubmatch(message)
        callback(m[1], atoi(m[2]))
        return true
    } else if reMinusEq.MatchString(message) {
        m := reMinusEq.FindStringSubmatch(message)
        callback(m[1], -atoi(m[2]))
        return true
    }
    return false
}
そしてそこから呼ばれるコールバックで、GAE のデータストアに格納します。 parsePlusPlus(event.Message.Text, func(nick string, plus int) {
    plusplus := &PlusPlus{nick, 0}
    key := datastore.NewKey(c, "PlusPlus", nick, 0nil)
    err := datastore.Get(c, key, plusplus)
    if err == nil || err == datastore.ErrNoSuchEntity {
        plusplus.Count += plus
        _, err = datastore.Put(c, key, plusplus)
        results += fmt.Sprintf("%s (%d)\n", plusplus.Nickname, plusplus.Count)
    }
})
最後に通算++数を応答します。


これでだいたい、一般的な bot としての機能は満たせました。Lingr のGo言語部屋に24時間常駐しています。
お好きな様にお使い下さい。
Posted at by