IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

C++ で Buzztter を Growl する

はじめに

BuzztterRSS を持ってきて、新しいキーワードを Growl に表示するものを作ってみた。
C++ でも boost::asio とか、 libxml2 とかを使うとけっこうサクっと書ける。ってこともないか。。

必要なもの

ソース

#include <libxml/xmlreader.h>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <Growl/Growl.h> 

static CFTypeRef notifications[] = {
    CFSTR("Buzz word arrived.")
}; 

static int notifyReading(void *in, char* buf, int len)
{
    return static_cast<std::istream*>(in)->rdbuf()->sgetn(buf, len);
}

int main(int argc, char *argv[]) 
{ 
    Growl_Delegate delegate; 
    InitGrowlDelegate(&delegate); 
    delegate.applicationName = CFSTR("cppclient"); 

    CFTypeRef keys[] = {
        GROWL_NOTIFICATIONS_ALL, 
        GROWL_APP_ID
    }; 

    boost::shared_ptr<const __CFArray> allNotifications(
        CFArrayCreate(
            NULL, 
            notifications,
            sizeof(notifications) / sizeof(notifications[0]),
            &kCFTypeArrayCallBacks
        ),
        &CFRelease
    ); 

    CFTypeRef values[] = {
        allNotifications.get(), 
        CFSTR("com.buzzter")
    }; 

    CFDictionaryRef dict = CFDictionaryCreate(NULL, 
            keys, values, 
            sizeof(keys) / sizeof(CFTypeRef), 
            &kCFTypeDictionaryKeyCallBacks, 
            &kCFTypeDictionaryValueCallBacks); 

    delegate.registrationDictionary = dict; 

    Growl_SetDelegate(&delegate); 

    bool first = true;

    boost::unordered_map<std::string, bool> seen;

    while (true)
    {
        boost::asio::ip::tcp::iostream s("buzztter.com", "http");
        s   << "GET /ja/rss HTTP/1.0\r\n"
            << "Host: buzztter.com\r\n"
            << "\r\n"
            << std::flush;

        std::string line;
        while (std::getline(s, line))
        {
            if (line == "\r")
                break;
        }

        boost::shared_ptr<xmlTextReader> reader(xmlReaderForIO(notifyReading, NULL, &s, NULL, NULL, 0), xmlFreeTextReader);

        while (xmlTextReaderRead(reader.get()) == 1)
        {
            if (xmlTextReaderDepth(reader.get()) == 3 && std::string("title") == reinterpret_cast<const char*>(xmlTextReaderConstName(reader.get())))
            {
                std::ostringstream title;

                while (xmlTextReaderRead(reader.get()) == 1 && xmlTextReaderDepth(reader.get()) == 4)
                {
                    title << std::string(reinterpret_cast<const char*>(xmlTextReaderConstValue(reader.get())));
                }

                if (!first && !title.str().empty() && !seen[title.str()])
                {

                    boost::shared_ptr<const __CFString> word(
                        CFStringCreateWithCString(
                            NULL, 
                            reinterpret_cast<const char*>(title.str().c_str()),
                            kCFStringEncodingUTF8
                        ),
                        CFRelease
                    );

                    Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext( 
                        CFSTR("Buzzter"), 
                        word.get(), 
                        CFSTR("Buzz word arrived."),
                        NULL,
                        0,
                        false,
                        NULL
                    ); 
                }

                seen[title.str()] = true;
            }
        }

        first = false;
        sleep(60);
    }
} 

こんな感じでコンパイル

$ g++ -framework Growl -framework Carbon -lboost_system-mt -lxml2 main.cpp