代码分享
http://git.oschina.net/for-1988/osc-robot
说明
关于这两天osc上出现的自动回复机器人【小香蕉】是我提供给大家娱乐的一个东西,这就和Simsimi差不多的东西,有的人觉得比较好玩,但肯定也打扰到了一些人,这个在这里跟大家说不好意思。【小香蕉】的服务我还会开着,如果红薯,虫虫那边觉得不好的话,那我就给停掉。刚看到了红薯发了一个说明文章,这里感谢osc的理解,一切听从 @红薯 安排。。
关于【小香蕉】
小香蕉其实就是和小黄鸡 Simsimi ,小i机器人差不多的东西。因为Simsimi,小i收费了,31号晚上正好发现了国外的一个提供api的机器人 CleverBot ,小香蕉的英文聊天就是用的它,应该是不支持中文的。然后不少人在问能不能支持中文,后来有人推荐了国内的小九机器人。昨天就加了小九,可以通过@ 小香蕉,然后 #中文 开始中文聊天。如果是中文前面没有加# ,那么很容易出现不回复的情况,我这样没有做恢复机制的处理。
【小香蕉】的代码
考虑了一下,还是不完全把代码分享出来了。其实我写的代码很简单的,大概300行,明白的朋友就知道是怎么回事。大概来说就是一个多线程的来调用osc的api获取@ 的信息,然后把内容传给机器人的接口获取回答,最后在通过osc的接口完成回复。
这里我用的编程语言和框架是 scala+akka。 Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Java 和 Scala 的 Actor 模型应用。用来编写多线程异步的并发程序很是方便简单。
小香蕉目前跑在一个阿里云的服务器上,最便宜的那种,高并发处理的时候大概内存占用在70m左右。
下面是部分的代码
1. Runable Jar 的入口方法,这里初始化了Akka 的actor system。这个方法用java编写是因为开始通过maven打包失败,就直接用eclipse 的expend打包了,那就必须是一个java来写的main方法了。
public class Main {
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("system");
ActorRef actor = system.actorOf(Props.create(OscActor.class),
"listener");
actor.tell("login", actor);
actor.tell("check", actor);
}
}
2. 处理调用osc相关接口的actor
class OscActor extends Actor {
val client = new HttpClient
val username = "xxxxx@126.com"
val password = "****"
var u: User = _
var last_count = 0
def receive = {
// 处理登陆
case "login" => {
val login_url = OscUrl.LOGIN_VALIDATE_HTTP
val login = _get(login_url + "?username=" + username + "&pwd=" + password + "&keep_login=1")
val head = new Header
client.executeMethod(login)
println(login.getResponseBodyAsString())
u = User.parse(login.getResponseBodyAsString())
login.releaseConnection()
}
// 这里递归想当前actor发送check消息,来检查是否有新的 @ 消息
case "check" => {
last_count = checkatmeCount()
if (last_count > 0)
self ! last_count
else {
Thread.sleep(3000)
self ! "check"
}
}
// 查询前count条active
case count: Int => {
val active_url = OscUrl.ACTIVE_LIST
val active = _get(active_url + "?uid=" + u.uid + "&catalog=2&pageIndex=0&pageSize=" + count)
client.executeMethod(active)
if (active.getStatusCode() == 200) {
val list = Active.parseList(active.getResponseBodyAsString())
list.foreach(active => {
// 判断是否#开头的消息,如果是 创建一个新的 小九的actor 来处理这个active。否则创建CeleverBot的actor来处理
if (active.getMessage.startsWith("#")) {
val chatter = context.actorOf(Props[XiaojiuActor])
chatter ! active
} else {
val chatter = context.actorOf(Props[ChatterActor])
chatter ! active
}
})
// 这里是为了处理出现 漏回复的情况,由于是我主动去拉osc的信息,在这个checkatmeCount 的损耗时间内,如果有新的@消息
// 那么就很可能出现漏回复,这是无法避免的了
val count = checkatmeCount()
if (count <= last_count) {
clearNotice()
self ! "check"
} else {
last_count = count
self ! (count - last_count)
}
} else {
self ! "check"
}
active.releaseConnection()
}
// 回复消息
case active: Active => {
if (active != null) {
val reply_url = OscUrl.COMMENT_REPLY
val url = reply_url + "?catalog=" + active.getCatalog + "&id=" + active.getId + "&uid=" + u.getUid + "&content=" + URLEncoder.encode(active.getReplymsg) + "&replyid=" + active.getReplyid + "&authorid=" + active.getAuthorid
val reply = _get(url)
client.executeMethod(reply)
reply.releaseConnection()
}
sender ! PoisonPill
}
}
def _get(url: String): GetMethod = {
val method = new GetMethod(url)
method.setRequestHeader("Host", "www.oschina.net")
method.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
method.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8")
method.setRequestHeader("Connection", "keep-alive")
method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36")
method
}
def checkatmeCount(): Int = {
val notice_url = OscUrl.USER_NOTICE
val notice = _get(notice_url + "?uid=" + u.getUid)
client.executeMethod(notice)
val xml = notice.getResponseBodyAsString()
val atme = Jsoup.parse(xml)
var count = 0
try {
count = atme.select("atmeCount").first().html().toInt
} catch {
case e: Exception => count = 0
}
notice.releaseConnection()
count
}
def clearNotice(): Boolean = {
val clear_notice_url = OscUrl.NOTICE_CLEAR
val clear = _get(clear_notice_url + "?uid=" + u.getUid + "&type=" + 1)
client.executeMethod(clear)
val flag = clear.getStatusCode() == 200
clear.releaseConnection()
flag
}
}
3. 处理机器人回复接口的actor,这里创建了3种bot来平衡处理回复
class ChatterActor extends Actor {
val factory = new ChatterBotFactory();
val bot1 = factory.create(ChatterBotType.CLEVERBOT);
val bot1session = bot1.createSession();
val bot2 = factory.create(ChatterBotType.PANDORABOTS,
"b0dafd24ee35a477");
val bot2session = bot2.createSession();
val bot3 = factory.create(ChatterBotType.JABBERWACKY);
val bot3session = bot3.createSession();
def receive = {
case active: Active => {
val s = think(active)
active.setReplymsg(s)
val actor = context.actorOf(Props[OscActor])
actor ! active
actor ! PoisonPill
}
}
def think(active: Active): String = {
(active.getId.toInt % 3) match {
case 0 => {
bot1session.think(active.getMessage)
}
case 1 => {
bot2session.think(active.getMessage)
}
case 2 => {
bot3session.think(active.getMessage)
}
}
}
}
这些就是我写的主要代码了,具体关于机器人的实现等等,可以去查一查有关 AIML 的资料。