首先,yum安装各种需要的东西,比如pidgin,以及后面需要用到的依赖包,其中dbus是用来通信的
yum install pidgin-devel finch screen dbus dbus-devel dbus-x11 libev-devel zlib-devel glibc-devel cmake sqlite-devel libcurl-devel
其次,通过git clone pidgin-lwqq来进行编译,详细的见https://github.com/xiehuc/pidgin-lwqq,不过这里需要注意两点:
- 切换到dev分支,不然没法编译,master分支需要依赖一个mozjs的东西,centos上可以搞,但是相当麻烦
- 因为是在一个没有图形界面的系统(比如VPS)上编译,需要将验证码输出到一个web可访问的路径去,否则登录不了。修改方法大致如下:
diff --git a/lib/login.c b/lib/login.c
index 8fe25c0..a1ae11b 100644
--- a/lib/login.c
+++ b/lib/login.c
@@ -141,6 +141,7 @@ static LwqqAsyncEvent* check_need_verify(LwqqClient *lc,const char* appid)
static int request_captcha_back(LwqqHttpRequest* req,LwqqVerifyCode* code)
{
int err = 0;
+ FILE *captcha_fp;
if(req->http_code!=200){
err = -1;
goto done;
@@ -148,6 +149,9 @@ static int request_captcha_back(LwqqHttpRequest* req,LwqqVerifyCode* code)
LwqqClient* lc = req->lc;
code->data = req->response;
code->size = req->resp_len;
+ captcha_fp = fopen("/path/to/qqcaptcha.png", "wb");
+ fwrite(code->data, code->size, 1, captcha_fp);
+ fclose(captcha_fp);
req->response = NULL;
lwqq_call_action(lc,need_verify2)(lc,code);
done:
然后,编译好之后,就可以先试试登录了,直接敲入finch试试,目测就能看到登录界面了
选择webqq进行登录,使用快捷键还是很方便的,不会使用可以man一下
下面就是机器人部分了,pidgin支持dbus通信,于是我们可以用利用dbus的接口来监听qq的消息,并作出回应,抑或是主动发出消息。要使用dbus,那么finch需要运行在screen下,这也是前面安装screen的原因,简单点,在命令行里敲入
dbus-launch screen
即可启动dbus,并进入screen,然后在screen下运行finch即可。友情提示,某些terminal里,screen下的finch排版混乱,至今没找到解决方案。另外,screen的使用方法可以man一下,你的通信脚本也必须运行在screen里。
dbus通信可以用多个语言实现,c、python、php等等都可以,个人熟悉php,因此使用php进行编写机器人脚本。首先安装php扩展dbus:
pecl install dbus-0.1.1
装完记得在php.ini里加入dbus的配置
[dbus]
extension=dbus.so
网上能找到很多示例代码,比如PHP官网就有很多example:传送门在此
下面贴个简单自动重复消息的机器人代码
<?php
Robot::getInstance()->run(200);
/**
* class Robot
* @author Baiqiang Dong<qiyuuu@gmail.com>
*/
class Robot {
protected $interface = 'im.pidgin.purple.PurpleInterface';
protected $signals = array(
'ReceivedImMsg',
'ReceivedChatMsg',
);
private static $_instance;
private $_dbus;
private $_proxy;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
private function __construct() {
//connect to dbus
$this->_dbus = new IDBus(DBus::BUS_SESSION);
$this->_proxy = $this->_dbus->createProxy(
'im.pidgin.purple.PurpleService',
'/im/pidgin/purple/PurpleObject',
'im.pidgin.purple.PurpleInterface'
);
//listen to specified signals
foreach ($this->signals as $signal) {
$this->_dbus->addSignalReceiver(array($this, lcfirst($signal)), $this->interface, $signal);
}
}
public function receivedImMsg($signal) {
$this->responseMessage($signal);
}
public function receivedChatMsg($signal) {
$this->responseMessage($signal);
}
public function responseMessage($signal) {
list($receiver, $sender, $message, $conversation, $flags) = $signal->getData()->getData();
//send what you received
$this->sendMessage($conversation, $message, $sender);
}
public function sendMessage($conversation, $message) {
$proxy = $this->_proxy;
try {
$type = $proxy->PurpleConversationGetType($conversation);
switch ($type) {
//im
case 1:
$im = $proxy->PurpleConvIm($conversation);
$proxy->PurpleConvImSend($im, $message);
break;
//chat
case 2:
$chat = $proxy->PurpleConvChat($conversation);
$proxy->PurpleConvChatSend($chat, $message);
break;
default:
# code...
break;
}
} catch (Exception $e) {
echo $e->getMessage(), "\n";
return false;
}
return true;
}
public function run($time = 1000) {
$this->_dbus->mainLoop($time);
}
}
/**
* extend the DBus class to implement methods like mainLoop and addSignalReceiver
*/
class IDBus extends DBus {
private $_signalReceivers = array();
public function __construct($type) {
parent::__construct($type);
}
public function addSignalReceiver($callback, $interface, $signal) {
if (!is_callable($callback)) {
return;
}
if (!isset($this->_signalReceivers[$interface])) {
$this->addWatch($interface);
}
$this->_signalReceivers[$interface][$signal] = $callback;
}
public function mainLoop($time) {
while (true) {
$signal = parent::waitLoop($time);
$called = false;
if ($signal instanceof DbusSignal) {
foreach ($this->_signalReceivers as $interface=>$callbacks) {
foreach ($callbacks as $method=>$callback) {
if ($signal->matches($interface, $method) && is_callable($callback)) {
$called = call_user_func($callback, $signal);
//break to main loop if callback return true
if ($called) {
break 2;
}
}
}
}
}
}
return $signal;
}
}