TwitterのbotをGaucheで書き直してみた

ニコニコ動画VOCALOIDオリジナル曲の動画が投稿されたのをお知らせする」という、Twitterbotを去年の12月ごろに作ったので、それをGaucheで書き直してみました。
「こんなbot公開していいのか」と怒られそうなほど欠陥だらけですが・・・
実は、自分でニコニコ動画RSSを読んでいるわけではなく、VOCALOIDオリジナル曲の新着動画を表示してくれるサイトの「ぼかにゅー」というところのAtomフィードを読んでいます。
(ぼかにゅーの開発者の方には許可取ってるつもりですが・・・)
一応、ぼかにゅーのURLを書いておきます。
ぼかにゅー: http://vocanew.naniyueni.org/
ぼかにゅー開発ブログ: http://d.hatena.ne.jp/vocanew/

一応、Twitter ID「@vnewsong」で動いています。

まず、書き直す前の、Ruby版のコードを出しておきます。

=begin
VOCALOID新曲通知TwitterBot
VOCALOIDの新曲を10分ごとにチェックしTwitterに投稿する。
新曲情報の入手は「ぼかにゅー」(http://vocanew.naniyueni.org/)から。
=end

require "socket"
require "net/http"
require "rexml/document"
require "kconv"
require "uri"

user = "username"
pass = "password"

interval = 60 * 10  #10分

lastSongURL = ""  #前回取得時点での最新の曲のURL

#「ぼかにゅー」の新着情報(Atom)を読み込む
def vocanewLoad
  atom = ""
  req = Net::HTTP.start("vocanew.naniyueni.org", 80) {|http|
    res = http.get("/newsong", {"User-Agent" => "vnewsong"})
    atom = res.body
  }
  return atom
end

#Twitterに投稿
def tweet(text,username,password)
  encodedText = URI.encode(text)
  req = Net::HTTP::Post.new("/statuses/update.json")
  req.basic_auth username,password
  req.body = "status=" + encodedText

  Net::HTTP.start("twitter.com", 80) {|http|
    res = http.request(req)
  }
end

t = Thread.new do
  #telnetを使って外部から終了させられるようにするサーバー
  serverSock = TCPServer.open(44444)

  while true
    Thread.start(serverSock.accept) do |s|
      while command = s.gets
        if command == "stop\r\n" || command == "stop\n"
          exit
          break
        end
        s.puts "プログラム停止: Command \"stop\"\r\n"
      end
      s.close
    end
  end
end

while true
  #puts "Loading Vocanew Atom Data..."
  vocanewAtomElems = REXML::Document.new(vocanewLoad).elements
  songName = vocanewAtomElems["feed/entry/title"].text
  songURL = vocanewAtomElems["feed/entry/link/@href"].to_s
  if songURL !=  lastSongURL
    #puts songName.tosjis
    #puts songURL
    tweet "" + songName + " " + songURL + " #nicovideo", user, pass
    #puts "Tweet success."
    #puts "" + songName + " " + songURL
    lastSongURL = songURL
  end
  sleep interval
end

実は、かなり適当な作りなので、「取得できていない」ということや、「いつのまにか停止している」ということが何度も起きてしまいます。
telnetlocalhostのポート44444に接続して『stop』と入力するとbotを停止できる」という謎の機能がついていますが、
Windows版のGaucheではマルチスレッドが使えないようなので、その機能はGauche版では実装できませんでした。

Gauche版のコードも書いておきます。

; ニコニコ動画に投稿されたVOCALOIDの新曲を通知するTwitter bot
; 情報は「ぼかにゅー」http://vocanew.naniyueni.org/ から取得。

(use rfc.http)
(use rfc.uri)
(use rfc.base64)
(use sxml.ssax)
(use sxml.sxpath)
(use srfi-1)

; Twitter IDとパスワード
(define username "username")
(define password "password")

; 読み込む間隔(60秒 * 10 = 10分)
(define interval (* 60 10))

; atom形式のXMLからエントリーのtitleとlinkのリストを取得
(define (get-title-and-link atom)
  (let ((sxml (ssax:xml->sxml (open-input-string atom)
                             '((feed . "http://www.w3.org/2005/Atom")))))
    (zip ((sxpath '(feed:feed feed:entry feed:title *text*)) sxml)
         ((sxpath '(feed:feed feed:entry feed:link @ href *text*)) sxml))))

; http://vocanew.naniyueni.org/newsong(ぼかにゅーのAtom)を読み込む
(define (load-atom)
  (values-ref (http-get "vocanew.naniyueni.org" "/newsong"
                        :user-agent "vnewsong") 2))

; Twitterに投稿する
(define (twitter-update status username password)
  (http-post "twitter.com"
             (string-append "/statuses/update.json?status="
                            (uri-encode-string status))
             ""
             :Authorization (string-append "Basic "
                                          (base64-encode-string
                                            (string-append username
                                                           ":"
                                                           password)))))

; 無限ループする
; 引数のoldは、最後に取得したatomデータをget-title-and-linkしたもの
(define (loop old)
  (let* ((atom (load-atom))
         (entries (get-title-and-link atom)))
    (begin
      (if (not (equal? entries old))
        (twitter-update (string-append (caar entries) " " (cadar entries)
                                       " #nicovideo")
                        username password))
      (sys-sleep interval)
      (loop entries))))

(define (main args)
  (loop ()))

汚いコードになってしまいごめんなさい。
Twitter投稿は、前に書いたコードのtwitter-update関数をそのまま使っています。(実はTwitter投稿のコードはこのために書きました)
Atomを読むコードも前に書いたコードを少し書き換えただけです。

「取得できていない」などの問題は今度修正するつもりです。

本当にこんな適当なプログラムを公開してもいいんでしょうか・・・