tokuhiromさんがnanowwwという、C++から簡単にHTTPが扱えるクライアントライブラリを書いてくれたので、botを書いてみた。
tokuhirom's nanowww at master - GitHub
C++ lightweight, fast, portable HTTP client library
http://github.com/tokuhirom/nanowww
picojsonは
以前ご紹介した通り、kazuhoさんが書いたC++からSTLと親和性の高いJSONパーサです。
今回はtokuhiromさんが、C++で、もちろんSTLと親和性の高いHTTPクライアントライブラリを書いてくれました。
これを使えば、例えばhttp://example.com/fooというURLにPOSTで"name=hasegawa"を送信するとした場合
nanowww::Client client;
nanowww::Request req("POST", "http://example.com/foo", "name=hasegawa");
nanowww::Response res;
req.set_header("Content-Type", "application/x-www-form-urlencoded");
if (client.send_request(req, &res))
std::cout << res.content() << std::endl;
else
std::cout << client.errstr() << std::endl;
これだけのコードでHTTP通信出来てしまいます。
C++らしくていいですね。CURLよりはインタフェースが優れていると思います。ただ現状はPROXYに対応していなかったり、Windowsで使えなかったりしますので、用途を絞られますが、今後に期待したいと思います。
さて今日はこれを使って、IRCに常駐し、指定のチャネルに入り、指定の単語が現れると「それXXX関係ないだろ!jk」と発言する、「関係ないbot」を書きました。実際にIRCプロトコルを喋っている訳でなく、webchat.freenode.netのAjax通信を、nanowwwとpicojsonによる擬似Ajax通信を行って実現しています。
以下、少々長いですが全ソースコード。
#include "picojson.h"
#include "nanowww.h"
using namespace std;
using namespace picojson;
using namespace nanowww;
#ifdef _WIN32
static char* utf8_to_str_alloc(const char* utf8) {
size_t in_len = strlen(utf8);
wchar_t* wcsdata;
char* mbsdata;
size_t mbssize, wcssize;
wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, in_len, NULL, 0);
wcsdata = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, in_len, wcsdata, wcssize + 1);
wcsdata[wcssize] = 0;
mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsdata, -1, NULL, 0, NULL, NULL);
mbsdata = (char*) malloc((mbssize + 1));
mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsdata, -1, mbsdata, mbssize, NULL, NULL);
mbsdata[mbssize] = 0;
free(wcsdata);
return mbsdata;
}
#endif
value get_json(string url, map<string, string>& postdata) {
value v;
char error[256];
string data;
map<string, string>::iterator it;
for (it = postdata.begin(); it != postdata.end(); it++) {
if (data.size()) data += "&";
data += it->first + "=";
data += it->second;
}
Client client;
Request req("POST", url.c_str(), data.c_str());
Response res;
req.set_header("Content-Type", "application/x-www-form-urlencoded");
if (client.send_request(req, &res)) {
const char* ptr = res.content().c_str();
string err = parse(v, ptr, ptr + strlen(ptr));
if (!err.empty()) cerr << err << endl;
}
return v;
}
int
main(int argc, char* argv[]) {
map<string, string> postdata;
value v;
bool loop;
if (argc != 4) {
std::cerr << "usage: kankeinaibot [nick] [channel] [word]" << std::endl;
return -1;
}
string nick = argv[1];
string channel = argv[2];
string word = argv[3];
postdata.clear();
postdata["nick"] = nick;
v = get_json("http://webchat.freenode.net/e/n", postdata);
string sid = v.get<array>()[1].get<string>();
loop = true;
postdata.clear();
postdata["s"] = sid;
while (loop) {
v = get_json("http://webchat.freenode.net/e/s", postdata);
array a = v.get<array>();
for (array::iterator it = a.begin(); it != a.end(); it++) {
if (!it->is<array>()) continue;
array line = it->get<array>();
if (line[0].to_str() == "c") {
if (line[1].to_str() == "443") {
postdata["nick"] += "_";
v = get_json("http://webchat.freenode.net/e/n", postdata);
sid = v.get<array>()[1].get<string>();
break;
}
if (line[1].to_str() == "376") {
loop = false;
break;
}
}
}
}
loop = true;
postdata.clear();
postdata["c"] = "JOIN #";
postdata["c"] += channel;
postdata["s"] = sid;
v = get_json("http://webchat.freenode.net/e/p", postdata);
while (loop) {
v = get_json("http://webchat.freenode.net/e/s", postdata);
array a = v.get<array>();
for (array::iterator n = a.begin(); n != a.end(); n++) {
array line = n->get<array>();
if (line[0].to_str() == "c") {
array aa = line[3].get<array>();
if (aa.size() > 1 && aa[0].to_str() != nick) {
cout << aa[0].serialize() << endl;
cout << aa[1].serialize() << endl;
if (!strncmp(aa[1].to_str().c_str(), word.c_str(), word.size())) {
map<string, string> saydata;
saydata["s"] = sid;
std::stringstream s;
s << "PRIVMSG #" << channel << " :" <<
"それ" << word << "関係ないだろ!jk";
saydata["c"] = s.str();
cout << saydata["c"] << endl;
v = get_json("http://webchat.freenode.net/e/p", saydata);
}
}
string s = line[3].serialize();
#ifdef _WIN32
char* p = utf8_to_str_alloc(s.c_str());
cout << p;
cout << " ";
free((void*)p);
cout << endl;
#else
cout << s << endl;
#endif
}
}
}
return 0;
}
CURLを使った場合のレスポンスwriterを書かなくてもいいので、かなりスッキリしましたね。
ソースはgithubにも置いてあるのでよろしければ試してみて下さい。
mattn's kankeinai-bot at master - GitHub
関係ないbot
http://github.com/mattn/kankeinai-bot
えっ?全然botじゃない?
bot関係ないだろ!jk