LingrRadar もどきを作り始めてみた

とりあえず、超適当版はこんな感じ

object lingr_test {
  import scala.io._
  import scala.xml.parsing._
  import scala.util.DynamicVariable

  import jp.ryugate.net.Http
  
  val LINGR_SERVER = "www.lingr.com"
  val APIKEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

  val EMAIL = ("email", "xxxx@b.com"
  val PW = ("password", "himitsu")
  val NICKNAME = "hoge-san"

  val ROOMID = "ryugate"

  type Arg = Tuple2[String, String]
  val HTTP = new DynamicVariable[Http.Http](null)

  def req(method:(String, List[Arg]) => String, resource:String, args:List[Arg]) = {
    val res_xml = method(resource, args)
    ConstructingParser.fromSource(Source.fromString(res_xml),false).document
  }

  def session(apikey:String)(f:List[Arg] => Unit) = {
    val res = req(HTTP.value.post, "/api/session/create", List*1
    println(session)
    try {
      f(session)
    } finally {
      HTTP.value.post("/api/session/destroy", session)
    }
  }

  def room(session:List[Arg], roomid:String, nickname:String)(f:(Arg,Arg) => Unit) = {
    val rid = ("id", roomid)
    val nname = ("nickname", nickname)
    val res = req(HTTP.value.post, "/api/room/enter",  rid :: nname :: session)

    val ticket = ("ticket", (res \\ "ticket").text)
    val counter = ("counter", (res \\ "counter").text)
    println(ticket)
    println(counter)
    try {
      f(ticket, counter)
    } finally {
      HTTP.value.post("/api/room/exit", ticket :: session)
    }
  }

  def observe(session:List[Arg], ticket:Arg, counter:Arg):String = {
    val res = req(HTTP.value.get, "/api/room/observe", counter :: ticket ::  session)
    val c = ("counter", (res \\ "counter").text)
    println(c)
    println((res \\ "text").text)
    observe(session, ticket, c)
  }

  def main(args:Array[String]) {
    Http.start(LINGR_SERVER) { http =>
      HTTP.withValue(http) {
        session(APIKEY) { session =>
          http.post("/api/auth/login", EMAIL :: PW :: session)
          room(session, ROOMID, NICKNAME) { (ticket, counter) =>
            observe(session, ticket, counter)
          }
        }
      }
    }

  }
}

上記で使っている、HTTPのラッパは以下のとおり

package jp.ryugate.net
  
object Http {
  import java.io._
  import java.net._

  class Http(host:String) {
    var _read_timeout = 0
    def read_timeout:Int = _read_timeout
    def read_timeout_=(timeout:Int) { _read_timeout = timeout }

    var _proxy_host = ""
    def proxy_host:String = _proxy_host
    def proxy_host_=(proxy_host:String) { _proxy_host = proxy_host }

    def makeargs(args:List[(String,String)]) = {
      val x :: xs = args
      val head = x._1 + "=" + x._2
      xs.foldLeft(head) {(acc, v) =>
        acc + "&" + v._1 + "=" + URLEncoder.encode(v._2, "UTF-8")
      }
    }

    def get(resource:String):String = {
      val url = new URL("http://" + host + resource)
      val con = url.openConnection.asInstanceOf[HttpURLConnection]

      if (_read_timeout > 0) con.setReadTimeout(_read_timeout)
      con.setRequestMethod("GET")

      val br = new BufferedReader(new InputStreamReader(con.getInputStream))
      try {
        readContents("", (br.readLine _))
      } finally {
        br.close
      }
    }
    def get(resource:String, args:String):String = get(resource+"?"+args)
    def get(resource:String, args:List[(String,String)]):String = get(resource, makeargs(args))

    def post(resource:String, args:String):String = {
      val url = new URL("http://" + host + resource)
      val con = url.openConnection.asInstanceOf[HttpURLConnection]

      con.setDoOutput(true)
      val pr = new PrintWriter(con.getOutputStream())
      try {
        pr.print(args)
      } finally {
        pr.close
      }

      if (_read_timeout > 0) con.setReadTimeout(_read_timeout)

      val br = new BufferedReader(new InputStreamReader(con.getInputStream))
      try {
        readContents("", (br.readLine _))
      } finally {
        br.close
      }
    }
    def post(resource:String, args:List[(String,String)]):String = post(resource, makeargs(args))


    def readContents(acc:String, reader:()=>String):String = reader() match {
      case null => acc
      case s => readContents(acc + s, reader)
    }
  }

  def start(host:String)(f: Http => Unit) {
    val http = new Http(host)
    f(http)
  }
}

問題点:

  • hoge has joined」というシステムメッセージが出てしまう。(LingrRadarだと出ない)
  • たまに、全メッセージが戻ってくることがある。
  • たまに、接続が切れる。(もしかしたら、2分程度でタイムアウトさせて、もう一度APIを発行する必要があるのかもしれない)

予定:

  • 必要なメッセージだけを表示する。
  • GUI
  • scala.xml.NodeSeq の「\」と「\\」の違いについて理解する

*1:"api_key", apikey), ("client_type", "automaton"))) val session = List( ("session", (res \\ "session").text