IRCチャットでWebページのクリップ

http://news.yoosee.net/hiki.cgi?%A5%CB%A5%E5%A1%BC%A5%B9%A5%C8%A1%BC%A5%AD%A5%F3%A5%B0
チャットで出たURLを自動的にニューストーキングにクリップする機能。
これを見て、なかなかいいなと思ったので、似たような仕様で、HikiのBBSプラグインにクリップするものをNadokaさん(http://www.atdot.net/nadoka/nadoka.ja.html)のbotで作ってみた。
仕様はこんなかんじ。

(sshi) 僕の日記 http://d.hatena.ne.jp/sshi
(bot)  http://d.hatena.ne.jp/sshi < (1)

誰かの発言の中にURLが登場すると、それをHikiのBBSの新しいエントリとして追加し、そのURLにバインドしたIDを返す。上の例では、二行目でbotが結果を返している。ここのURLがID1番にバインドされました。この後、IDを利用してそのURLにコメントがつけられます。

(dareka) つまんない日記だね<1
(sshi) …

というふうに、<とIDを使った発言をすると、その発言をコメントとしてさっきのURLのところにつける。この例では「つまんない日記だね」というコメントがdarekaの名前で登録される。ちなみに、#listと発言すると現在管理しているURL一覧が表示される。IDはたんにそのURLが何番目にはいってるかを示しているだけ。

botの設定では、対象のHikiのURLとBBSプラグインがあるページの名前を指定しておく。いまんとこ一つだけしか指定できない。
以下ソース。長いのでおる。HikiのBBSに登録するコードは正直いってひどい。HikiになにかAPIが欲しいなあ。

# BotConfig = {
#   :HikiLog => {
#     :url => 'http://example.com/path/to/hiki/',
#     :page  => 'BBSPageName',
#   }
# }

class HikiLog < Nadoka::NDK_Bot
  require 'kconv'

  module Utils
    require 'net/http'
    require 'open-uri'
    require 'kconv'
    module_function

    def get_title(targeturl)
      resultstr = get(targeturl)
      html = Kconv.toeuc(resultstr)
      if html =~ /<title\s*>(.*?)<\/title\s*>/m
        return $1.to_s
      end
      return nil
    end

    def get(url)
      ret = ''
      open(url) do |f|
        ret = f.read
      end
      return ret
    end

    def post(url,data)
      urlobj = URI.parse(url)
      result = nil
      Net::HTTP.start(urlobj.host,urlobj.port) do |http|
        result = http.post(urlobj.path,data)
      end
      return result
    end
  end

  module HikiBBS
    require 'cgi'
    module_function

    def make_add_data(page,no,name,msg)
      return {"name"=>CGI.escape(name),
        "msg"=>CGI.escape(msg),
        "comment"=>"true",
        "comment_no"=>no,
        "c"=>"plugin",
        "p"=>page,
        "plugin"=>"comment_post",
        "style"=>"0"
      }.map { |name,value| "#{name}=#{value}"}.join('&')
    end

    def make_post_data(page,name,subject,msg=' ')
      return {
        'p'=>page,
        'name'=>CGI.escape(name),
        'subject'=>CGI.escape(subject),
        'msg' => CGI.escape(msg),
        'bbs_num' => '1',
        'c' => 'plugin',
        'comment' => 'true', #button
        'plugin'=>'bbs_post',
      }.map { |name,value| "#{name}=#{value}"}.join('&')
    end

    def comment_add(url,page,name,no,msg)
      Utils.post(url,make_add_data(page,no,name,msg))
    end

    def comment_post(url,page,name,subject,msg)
      Utils.post(url,make_post_data(page,name,subject,msg))
    end
  end

  def bot_initialize
    @url = @bot_config[:url]
    @page = @bot_config[:page]
    @list = [""]
  end

  def get_thread_no_by_url(http)
    host = "#{@url}?#{@page}"
    page = Utils.get(host)
    if page =~ /#{Regexp.escape(http)}.*?<form action.*?"comment_no"\s*value="(\
[^"]*)"/m
      no = $1.to_i
      return no
    end
    return nil
  end

  def comment_add_by_url(name,url,msg)
    no = get_thread_no_by_url(url)
    return false if no == nil
    HikiBBS.comment_add(@url,@page,name,no,msg)
    return true
  end

  def new_comment_post(name,targeturl)
    msg = Utils.get_title(targeturl) || 'by bot'
    HikiBBS.comment_post(@url,@page,name,targeturl,msg)
  end

  def regist_url(msg,prefix)
    result = URI.extract(msg,%w(http https))
    ret = []
    result.each do |target|
      unless @list.include?(target)
        if get_thread_no_by_url(target) == nil
          new_comment_post(prefix.nick.to_s,target)
        end
        @list << target
        ret << [@list.index(target),target]
      end
    end
    return ret
  end

  def url_comment?(prefix,msg)
    if msg =~ /^(.*)<\s*(\d*)(.*)$/
      msg = $1
      no = $2.to_i
      return if no == 0
      http = @list[no]
      if http != nil
        unless comment_add_by_url(prefix.nick.to_s,http,Kconv.toeuc(msg.to_s))
          send_notice(ch,"#{msg}<#{http} (#{no}) failed")
        end
      end
    end
  end

  def on_privmsg(prefix,ch,msg)
    url_comment?(prefix,msg)
    if msg =~ /^#list/
        send_notice(ch,@list.inspect)
    else
      regist_url(msg,prefix).each do |item|
        no,http = item
        send_notice(ch,"#{http} < (#{no})") unless no == nil
      end
    end
  end
end