블로그 이미지
JEEN

서울에 사는 꽃청년의 IT찌질모험기

Rss feed Tistory
IT/Perl 2008.06.18 13:11

[ Perl ] IRC BOT을 만들어봅시다 :-) #번외편

 지난달 IKC에 대한 간단한 소개 이후로 IRC봇 만들기에 관련된 여러 리퍼러를 확인했습니다. 거기에 요근래 봇관련 글에 댓글도 달리는 영광을 누리기도 했었습니다.(아아~ 관심 x2)
 봇 만들기글을 올리고 한 달동안의 경과에 대해서 말해보고자 합니다.

 1. 프로젝트 멤버

 - 오늘까지 6분이 프로젝트 멤버가 되셨습니다.(안습;;)
 - (물론 제가 억지로 집어넣으신 분도 계십니다;)
 - 요즈음 봇에 신경을 너무 많이 써주시는 분(yu**)도 계시고...
 - 달랑 한번 커밋 해주시고는 나몰라라하시는 분(Whi*****)도 계십니다 :-)
 - 나머지 분들은 바쁘신 와중에 관심(만) 보여주시고 계십니다. ;;

 2. 이슈
사용자 삽입 이미지
  - 대충 이런 이미지와 같이 스크립트의 버그나 미약한 기능들에 대해서는 이슈를 등록해놓습니다.
  - 어떤 사소한 것이라도 말이죠 :-)

 3. 리비젼
사용자 삽입 이미지
- 현재 리비젼은 34번까지 올라와 있습니다.
- 버그가 있던 없든, 일단 커밋하고 보자는 생각으로 마구잡이 커밋을 감행하고 있습니다.
- 아, 코드리뷰 할 때도 있습니다. 아래의 예제 처럼요.
   ex> 우왕.. 너무 좋아요. 수고하셨어요.
  (근데 코드얘기는 없...)
- 차후에 전체적인 코드리뷰만으로 포스팅할 생각을 하고 있습니다. :-)

4. 접속자
 * 국제
사용자 삽입 이미지
사용자 삽입 이미지

- Google Code 는 Google Analystic 을 지원하고 있습니다.
- 6개국에서 열혈한 관심을 보이고 있습니다. +_+ (물론 방문자 수는 캐안습입니다)

 * 국내
사용자 삽입 이미지
사용자 삽입 이미지

  - 이렇게 지역적으로 고른 분포를 보이고 있습니다. (물론 방문자수는 안습입니다)

5. 그 외 하고 싶은 이야기

  - 사실 언어를 공부할 때 가장 좋은 방법은, 남의 코드 베끼는  것이나 플젝에 참여하는 게 아닐까 생각합니다.
  - 그리고 이런 저런 새로운 모듈을 갖다 붙이든가, 자신이 하고 싶은 방향으로 뜯어고치든가, 여러가지 새로운 경향을 실험해볼 수 있는 장소가 되기도 하죠(yu** 님께서 Moose 실전용으로 커밋한 경우).
  - 뭐, 결국 하고 싶은 이야기는.. 와서 "왜 이렇게 코드가 개똥 같은 거야!"라고 말해줄 수 있는 사람이 필요합니다. 그리고 그 사람에게 배우고 싶다는 것이죠.
  - 한국형 CodeRepos 같은 것은 어떨까 막 생각하고 있습니다.
  - Perl 에 한정짓지 않고 마구잡이 커밋용 서버가 필요해질지도 모르겠어요
  - 결론은 perl-irc-bot 많은 참여를 부탁드립니다. :-)



신고
IT/Perl 2008.05.29 11:48

[ Perl ] IRC BOT을 만들어봅시다 :-) #6


IRC BOT을 만들어봅시다 :-) #6

 - #5 에서는

  - Encode 모듈을 사용한 한글 취급에 대해서 알아보았습니다.
  - 실제 사용중인 Bot Command 를 예로 하여, 봇 커맨드 추가/사용 법을 알아보았습니다.

 - #6 에서는

  - IKC를 이용해서 외부 이벤트를 IRC에 알리는 방법에 대해서 알아보겠습니다.


 - IKC

  IKC - Inter-Kernel Communication
  자세한 내용은 PoCo::IKC 페이지를 참조해주세요.
 사실 PoCo::IRC 도 그렇지만, PoCo::IKC 도 제대로는 알지 못합니다. 단지 어디서 이런 것을 써서 구현하고 있다는 것만 보고 단순히 따라하기 수준에만 그친 형편이라서요. CPAN 에는 *::Notify::IRC 모듈이 있는데, 그 중에 Plagger::Plugin::Notify::IRC 와 Kwiki::Notify::IRC 를 참조해서 만들었습니다. Plagger 의 경우에는 Plagger 봇 스크립트를  제공해주고 있으니.. 그것을 대충 수정하면 쓸만하게 됩니다. :-)


 - Tutorial

  우선 적절한 사용예제를 Google 에서 찾고는 긁어와 봤습니다.

use warnings;
use strict;
use POE qw(Session);
use POE::Component::IKC::Server;
use POE::Component::IKC::Specifier;

POE::Component::IKC::Server->spawn(
    port => 31338,               # 포트
    name => 'AppServer',      # 서버이름
);

POE::Session->create(
    inline_states => {
        _start        => \&service_start,     # 초기화 작업 정의
        calc_sum      => \&service_calc_sum, # 작업내용 구현
        did_something => \&service_response, # 작업내용을 클라이언트에 전달
      }
);

POE::Kernel->run();
exit 0;

sub service_start {
    my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];

    my $service_name = "application";
    $kernel->alias_set($service_name); #서비스 이름을 정의해 줍니다.
    $kernel->call( IKC => publish => $service_name, ["calc_sum"] );
     # 해당 서비스이름과 작업을 공표합니다.
     # 이로써 client 에서는 해당 서비스 이름을 통해서 공표된 작업에만 접근할 수 있습니다.
     # calc_sum 이라고 공표된 작업은 위의 POE::Session안에서 inline_states 안에 기술합니다.
     # 고로 calc_sum 이라는 작업이 클라이언트에서 넘어왔을 때, service_calc_sum 을 호출하게 됩니다.
}

sub service_calc_sum {
    my ( $kernel, $heap, $request ) = @_[ KERNEL, HEAP, ARG0 ];
    my ( $data, $rsvp ) = @$request;

    my $sum = 0;
    if ( ref($data) eq "ARRAY" ) {
        $sum += $_ foreach @$data;
    }

    $kernel->delay_set( did_something => 1, $rsvp, $sum );
}

sub service_response {
    my ( $kernel, $heap, $rsvp, $sum ) = @_[ KERNEL, HEAP, ARG0, ARG1 ];
    $kernel->call( IKC => post => $rsvp, $sum );
    # 작업 결과를 넘겨받아 client 로 넘겨줍니다.
}

 이것은 IKC 서버의 구현입니다. 이것을 적당한 이름( ikc-server.pl )으로 저장합니다.
 다음은 클라이언트의 구현입니다.
 
use warnings;
use strict;

use POE::Component::IKC::ClientLite;

my $remote = create_ikc_client(
    port    => 31338,
    name    => "blahblah",
    timeout => 1,
);
die $POE::Component::IKC::ClientLite::error unless $remote;


my $return_value = $remote->post_respond( 'application/calc_sum', [1,2,3,4,5] );
 # 서버에서 지정한 서비스 이름과, 작업을 여기에서 호출됩니다.
die $POE::Component::IKC::ClientLite::error unless defined $return_value;

print "The sum is: $return_value\n";
exit 0;

 이것 역시나 적당한 이름 ( ikc-client.pl ) 로 저장합니다.
 우선 ikc-server.pl 을 실행한 다음에... 다른 터미널을 열어서 ikc-client.pl 를 실행합니다.
 그러면 해당 ikc-client.pl 을 실행한 터미널에서는
 
The sum is: 15

이라는 결과가 뜨게됩니다.

 Client 에서 1,2,3,4,5 라는 배열값(배열의 리퍼런스)을 서버에서 지정한 서비스 이름과 작업에 보내고, Server에서는 그것들을 전부 더해서 그 값을 클라이언트로 보내주는 겁니다.

 이를 응용하면 어떤 것이 가능할까요?

사용자 삽입 이미지
현재 #perl-kr@irc.freenode.org 에서는 CPAN 에 새로 등록된 모듈,
 perlmania 의 새로운 글들, perldoc-kr 에서의 새로운 커밋에 대해서 통보를 받고 있으며,
 #perl@irc.hanIRC.org  <=> #perl-kr@irc.freenode.org 간의 메시지 전달도 가능하게 되었습니다.
 
사용자 삽입 이미지
사용자 삽입 이미지

 [freenode] 라고 붙은 건 freenode 의 대화를 hanIRC에서 받은 것이고,
 [hanIRC] 라고 붙은 건 hanIRC 에서 freenode 의 대화를 받은 것입니다.

 그외에도 CodePad를 도입하고 있습니다. 특정 언어 코드를 웹페이지에 갖다 붙이면,
 그것을 적절하게 컴파일/인터프리트 해서 그 결과를 웹페이지에 보여주게 됩니다.
 모듈 의존성이 있는 코드들에 대한 결과를 자세하게 보지는 못하지만, 그래도 깨끗하게 코드를 볼 수 있습니다.
 
사용자 삽입 이미지

 (1) codepad 에 코드를 넣어서
 (2) 그 결과가 담긴 URL 을 복사해서
 (3) IRC에 붙여넣는다.

 라는 과정을

  (1) codepad 에 코드만 넣으면 끝

으로 축약했습니다.

  CodePad 의 구성을 그대로 빌려와서, WWW::Mechanize 를 통해서 해당 폼 값을 CodePad 로 날려서
그 결과가 되는 URL 을 받아, 그 결과값을 IKC를 통해서 IRC에 자동으로 통보해주는 것입니다.
(이는 saillinux님과 pung96님께서 수고해주셨습니다.)

 위에서 언급한 이런 것들은 모두 perl-irc-bot 에서 구현되어 있습니다. 정리되지 않은 코드라 더럽기도 하지만, "이따위 코드 눈뜨고 봐줄 수 없어! 내가 수정해주겠어!" 라면서 수정펀치 날려주실 분들과 커미터 분들을 모집하고 있습니다. 물론 사용하실 분들은 그냥 사용하셔도 됩니다. :-)

 - THE &

 이상으로 장장 6편에 걸친 그다지 도움되지 않는 IRC BOT 만들기를 마치겠습니다. :-)
 6편 모두는 IRCBOT만들기 라는 태그로 묶어놨습니다.

 제 자신도 공부가 부족한 편이라 깊숙한 계층에서 어떤 일들이 벌어지는 가 자세히 모르기 때문에, 공부하는 입장에서 이렇게 적어봤습니다. 보다 나은 코드와 보다 나은 기능, 보다 편리한 방법, 그리고 보다 깊은 계층에서의 움직임에 대한 격렬한 논의는 아래에 언급된 IRC나 email 로 부탁드리겠습니다. :-)

- PR

※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl, irc.freenode.org, #perl-kr)에서 JEEN에게 연락하시면 됩니다.


신고
IT/Perl 2008.05.27 16:12

[ Perl ] IRC BOT을 만들어봅시다 :-) #5


 흡, 휴가라서 그런지 시간이 남네요. 그러면 시작해 보겠습니다.


IRC BOT을 만들어봅시다 :-) #5

- #4 에서는...

  - 간단하게 CPAN 상에 올려진 PoCo::IRC::Plugin::(.+) 에 대해서 알아봤습니다.
  - Plugin 추가/사용에 대해서 간단하게 알아봤습니다.

 - #5 에서는

  - Encode 모듈을 사용한 한글 취급에 대해서 알아보겠습니다.
  - 실제 사용중인 Bot Command 에 대한 사용예제를 보여드리겠습니다.
 
 - IRC에서의 한글 사용

 현재 hanIRC는 cp949 라는 문자인코딩을 사용하고 있습니다. freenode 같은 경우는 utf-8 이죠. 최근에 한국 펄 커뮤니티(이러면 뭔가 거창해 보임..) 쪽에서 freenode 와 hanIRC 병행체제로 가게 되어서, BOT도 이에 맞춰서 기존의 인코딩 방식을 바꿔줘야 했습니다.
 그리고 Perl 에서의 이런 Encode 관련된 모듈은 이름 그대로 Encode 입니다. 일본의 Dan Kogai 씨가 이 Encode 모듈의 저자입니다.
사용자 삽입 이미지

 흠.. 사실은 지난 YAPC 에서 사인 받았더랬습니다.

 Encode::KR 페이지에서도 간단하게 사용예제를 볼 수 있습니다. 말 그대로 간단합니다! :-)

   use Encode qw/encode decode/; 
$cp949 = encode("cp949", $utf8); # loads Encode::KR implicitly
$utf8 = decode("cp949", $cp949); # ditto

 Encode::KR 페이지의 SYNOPSIS 부분을 그대로 따왔습니다.

 위의 예제 그대로, 일단은 use Encode; 해주시고...
  cp949 문자열을 utf-8으로 바꾸기 위해서는 cp949 문자열을 cp949 로 decode 해주시면 됩니다. 거꾸로 utf8을 cp949 문자열로 바꾸기 위해서는 utf-8 문자열을 cp949로 encode 해주시면 끝입니다.

 어때요? 참 쉽죠? 간단하죠?
 
 - Tutorial

 그러면 간단한 사용예제를 참고해보겠습니다. #4 플러그인 예제를 축약한 것은 여기서 사용하기 위해서 였습니다. :-)

 # URL Find : Title Notify                                                                                                                                  
sub irc_urifind_uri {
    my ($who, $channel, $url, $obj, $msg) = @_[ARG0 .. ARG4];
   
        my $html = scraper {
            process 'title', title => 'TEXT';
        };
        my $data = $html->scrape(URI->new($url));
        return 0 unless $data->{title};
        my $title = $data->{title}; # Web::Scraper 에서 나온 결과물은 UTF-8으로 반환
        $title = encode('cp949', $title); # UTF-8 인 title 내용을 cp949 로 변경
        $irc->yield( privmsg => $channel => $title ); # 해당 채널에 title을 알려줍니다.
   
}

 대화중에서 URL 이 포함되었을 경우에는 그 URL의 title을 얻어내는 코드입니다. 여기에는 Web::Scraper 라는 모듈을 사용해서 간단하게 title을 뽑아낼 수 있습니다. (물론 use Web::Scraper; 해주세요) LWP::Simple 등을 이용해서 간단히 정규표현 써서  얻는 방법도 있지만... 그거야 쓰시는 분 맘대로 이기 때문에... :-)
 아무튼 Web::Scraper 에서 긁힌 결과는 UTF-8 문자열로 반환됩니다.
 이것을 cp949 로 인코딩하구요. 그리고 그 값을 채널에다가 뿌려줍니다.

사용자 삽입 이미지

그럼 이렇게 나오겠죠?



  그럼 다른 예제를 보여드리겠습니다. 물론 perl-irc-bot 에서 사용하고 있는 예제중에서 긁어오겠습니다.

 최근에 perl-irc-bot 프로젝트에 처음으로 커밋해주신 하얀_고양이 님의 네이버 지도 예제입니다.

  !map [지역] 을 입력하면.. 해당 지역의 지도를 나타내는 그런 기능입니다.
 
사용자 삽입 이미지

이같이 사용합니다.


 그러면...

사용자 삽입 이미지

 이렇게 울릉군의 중심을 나타내게 됩니다. :-)
 코드는 아래와 같습니다.

use URI::Escpae;
my $naver_map_url = 'http://map.naver.com/?query=';

sub irc_public {
    my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
    my $nick = ( split /!/, $who )[0];
    my $channel = $where->[0];

   if ($what =~ /^!([a-z0-9]+)\s?(.*?)?$/) {
        my ($command, $desc) = ($1, $2);
 
        if ($command eq 'map') {
                my $address = $naver_map_url . URI::Escape::uri_escape($desc);
                $irc->yield(privmsg=>$channel=>$address);
        }
   }
}

 여기서는 URI::Escape 모듈을 use 합니다. 2바이트 문자가 URL 에 그대로 들어가면, 어떤 브라우저에서는 자동으로 바꿔서 보내주기도 하지만, 어떤 브라우저는 그것을 인식하지 못하고 글자가 깨지는 경우가 발생합니다.
사용자 삽입 이미지

 보시다 시피 "서울시"라고 2바이트 문자가 query 의 값으로 직접 넣었을 경우의 결과입니다. 뭐 서울 주변이기는 하지만, 제대로 된 값이 아닐경우의 기본 표시가 이곳인 듯 합니다. :-) 검색창의 서울시라는 글씨도 막 깨져있죠?

 그래서 URI::Escape 모듈로 해당 문자열을 uri_escape 해줍니다.

 그외 여러가지 아이디어가 있으시다면, Plugin 모듈을 직접 만들어서 많은 사람들이 사용할 수 있게 만들어도 좋을 것이고... 아니면 자신만 필요한 기능이 있다면 직접 이렇게 Bot Command 를 만들어 주고 쓰셔도 됩니다.
 
  - Summary

   - Encode 모듈을 사용해서 UTF-8 => cp949 의 경우에는 encode("cp949", $utf8),
     cp949 => UTF-8 의 경우에는 decode("cp949", $cp949) 를 하면 됩니다.
   - Web::Scraper 를 통해 간단하게 HTML의 내용중의 일부를 긁어올 수 있습니다.
   - 2바이트 문자의 경우는 URI::Escape 모듈을 이용해서 URL을 Escape 시켜줘야 해당 결과를 제대로 파악할 수 있습니다.

 
 - #6 에서는

  - IKC를 통해서 외부 이벤트를 IRC에 넘겨주는 방법에 대해서 이야기 해보겠습니다.

- PR
※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl)에서 JEEN에게 연락하시면 됩니다.

신고
IT/Perl 2008.05.27 12:57

[ Perl ] IRC BOT을 만들어봅시다 :-) #4

 웃하, 주말 합쳐서 나흘간의 휴가를 받았었는데, 그동안 좀 나태한 모습도 있었고, 그동안 perl-irc-bot 플젝에도 여러개의 새로운 리비젼이 올라오게 되었습니다. [미투데이라든가, 트위터라든가...] 그러는 중에 처음으로 하얀_고양이 님께서도 커밋을 해주시면서 점점 불타오르기 시작했습니다.

IRC BOT을 만들어봅시다 :-) #4

  - #3에서는...

  - IRC 의 이벤트 처리에는 여러가지가 있고, 대부분을 처리할 수 있었습니다.
  - IRC 커맨드에는 여러종류가 있고, 상황에 맞춰서 사용할 수 있었습니다.

  - #4에서는...

   - PoCo::IRC에 Plugin 을 붙여보도록 해보겠습니다. :-)


  - PoCo::Plugin::(.*)

 현재 PoCo::Plugin 모듈은 수십개가 CPAN에 올라와 있습니다. 하지만 그 중에서 쓸만한 것은 별로 없는 편입니다. 현재 제가 사용하는 PoCo::Plugin 모듈은 PoCo::Plugin::URI::FindPoCo::Plugin::Google::Calculator 인데요. 근데 실제로 Google::Calculator 는 별로 사용하지 않는 편입니다. 있는 지도 모르시는 분들이 많고..;; 제가 도움말도 안적어 놔서 그렇습니다.

  - Tutorial

 여기서는 URI::Find 플러그인 모듈을 사용해서 그 URL의 타이틀을 얻어내서 찍어주는 간단한 예를 보이려고 합니다.

use POE qw(Component::IRC      Component::IRC::Plugin::URI::Find                                                                                                               
Component::IRC::Plugin::Google::Calculator                                                                                                      
);

  우선은 사용할 플러그인 모듈들을 use 해줍니다.
  여기서는 *::URI::Find, *::Google::Calculator 모듈을 사용해 보도록 하겠습니다.

POE::Session->create(
    package_states => [
        main => [ qw(_default _start irc_001 irc_public irc_urifind_uri) ],
    ],
    heap => { irc => $irc },
);

 URI::Find 의 경우에는 package_states 에 "irc_urifind_uri" 를 넣어줍니다. irc_public에서 URL을 판별하면 그것을 irc_urifind_uri 로 넘겨주게 됩니다.

sub _start {
    my $heap = $_[HEAP];
   
    my $irc = $heap->{irc};

    $irc->yield( register => 'all' );
    $irc->plugin_add(   'UriFind' =>     POE::Component::IRC::Plugin::URI::Find->new         );
    $irc->plugin_add(   'GoogleCalc' =>  POE::Component::IRC::Plugin::Google::Calculator->new );
    $irc->yield( connect => { } );
    return;
}

 _start 에서 plugin_add 를 통해서 해당 플러그인을 등록해 줍니다.

 Google::Calculator 의 경우에는 여기까지 해주시면 끝입니다.  URI::Find의 경우에는 URL을 판별했을 때 그것을 어떻게 사용할 지를 irc_urifind_uri 에 적어줍니다.
                                                                                                                                  
sub irc_urifind_uri {
    my ($who, $channel, $url, $obj, $msg) = @_[ARG0 .. ARG4];
   
    # (LWP::UserAgent|LWP::Simple) 로 여차저차 해서 <title>(.*)</title> 의 내용 뽑아서
    $title 에 담아서... 이렇게..
 
        $irc->yield( notice => $channel => $title );
    }
}

  URL을 판별했을 때의 irc_urifind_uri 에서 받는 인수는 ARG0 - 누가, ARG1 - 어떤 채널에서, ARG2 - 판별한 URL, ARG3 - URI::URL 의 객체, ARG4 - 무슨 말을 했는지 입니다.

 자, 그럼 그 URL을 받아서 타이틀을 뽑아내서, 넘겨주면 됩니다.
 LWP::Simple, LWP::UserAgent ... 뭐 여러가지가 있습니다. LWP 관련해서 한번 찾아보시면 줄줄줄 엮여져 나옵니다. There's More Than One Way To Do It 이라고 하잖아요.
 이 부분의 구현은 직접 하시면 됩니다. 물론 perl-irc-bot 에는 직접 구현되어 있으니, 참조하셔도 됩니다.

 대부분의 Plugin::(.*) 페이지에 가시면, 간단한 사용 예제를 함께 볼 수 있습니다.

사용자 삽입 이미지

 이렇게 http://www.daum.net/ 이라고 치면 Perl_^^ 이라는 봇은 daum의 타이틀을 따오는 것이죠. 뭐, 어떻게 받아들이느냐에 따라서 별로 쓸데없는 기능이기도 합니다만...

사용자 삽입 이미지

  그리고 아까 위에서 말하다가 그만둔 Google::Calculator 의 사용법입니다.
  봇을 호충하고 calc [ 적당한 식 ] 을 써주시면 됩니다. 여러가지 사용법에 대해서는
PoCo::IRC::Plugin::Google::Calculator 페이지를 참조하시면 됩니다.

 - Summary

  - 간단하게 CPAN 상에 올려진 PoCo::IRC::Plugin::(.+) 에 대해서 알아봤습니다.
  - Plugin 추가/사용에 대해서 간단하게 알아봤습니다.

 - #5 에서는

  - Encode 모듈을 사용한 한글 취급에 대해서 알아보겠습니다.
  - 실제 사용중인 Bot Command 에 대한 사용예제를 보여드리겠습니다.

 - PR
※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl)에서 JEEN에게 연락하시면 됩니다.
신고
IT/Perl 2008.05.23 12:20

[ Perl ] IRC BOT을 만들어봅시다 :-) #3

하루에 한 번씩 쓸려고 했는데.. 역시 계획대로 쉽게 되지는 않는군요.

IRC BOT을 만들어봅시다 :-) #3

 - #2 에서는...

   - PoCo::IRC 를 이용해서 실제 IRC서버에 접속하고, 채널입장을 할 수 있었습니다.
   - IRC에서의 여러 이벤트들이 있다는 것을 알았습니다.

 - #3 에서는...

   - IRC에서 벌어지는 여러가지 이벤트 처리를 어떻게 하는 지에 대해서 알아보겠습니다.
   - IRC 에서 쓰이는 커맨드의 종류에 대해서도 함께 알아보겠습니다.

 - IRC Events

  자주 쓰이는 Event 를 우선으로 하겠습니다. 그리고 이벤트에서 받을 수 있는 인수에 대해서도 함께 알아보겠습니다.

  - irc_public (ARG0 - "닉!호스트", ARG1 - "채널이름", ARG2 - "메시지")
 채널 상에서 일반적인 대화시에 발생하는 이벤트 입니다.
sub irc_public {
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
my ($nick, $host) = split /!/, $who; # nick 과 호스트를 분리
my $channel = $where->[0]; # $where는 배열 참조
$irc->yield( privmsg => $channel => $nick . " : WTF?" );
# 건방진 봇이 되는 Lifehack
}
  - irc_join ( ARG0 - "닉!호스트", ARG1 - "채널이름" )
 누군가 채널에 입장했을 때 발생하는 이벤트 입니다.
sub irc_join {
     my ($sender, $who, $where) = @_[SENDER, ARG0, ARG1];
my ($nick, $host) = split /!/, $who; # nick 과 호스트를 분리
my $channel = $where->[0]; # $where는 배열 참조
$irc->yield( privmsg => $channel => $nick . " : Hello" );
# 언어에서 Hello World 가 기본이듯, Bot 에서도 Hello Bot 만들기는 가장 기본적인 예제입니다.
}

  - irc_msg ( ARG0 - "닉!호스트", ARG1 - "MSG 받은 당사자", ARG2 - "메시지" )
 귓속말 이벤트(1:1 대화)가 발생했을 때 입니다. 물론 여기서 메시지를 받는 대상은 "봇"이 됩니다.
sub irc_msg {
     my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2 ];
my ($nick, $host) = split /!/, $who; # nick 과 호스트를 분리
my $me = $where->[0]; # $where는 배열 참조
$irc->yield( privmsg => $nick => "What's wrong?" );
# 이번에는 privmsg 의 대상은 채널이 아니라, 닉네임이 됩니다.
# 귓속말 했는데 채널에 대고 떠들면 안되겠죠? 물론 인수에는 채널 정보를 얻을 수 없기도 하지만요.
}
  - irc_connected, irc_disconnected (ARG0 - "서버 이름" )
 
 IRC 서버에 접속하거나 나갔을 때의 이벤트 처리입니다.  강제종료가 아니라 정상적인 disconnect 가 되었다면, irc_disconnected 도 호출됩니다. 서버의 문제로 튕기거나 하는 상황이 적절한 예가 될 것입니다. irc_disconneted 에 다시 IRC 접속하게 해두면... 강제종료 하지 않는 이상은 계속해서 접속하려고 하겠죠. (물론 서버에서 부정한 접속이라고 튕겨낼 가능성이 있습니다)

  - irc_kick, irc_mode, irc_notice, irc_nick.... 
 채널 내에서 누구를 쫓아낼 때의 이벤트라든지, 누구에게 옵을 줬는지, 누군가 notice로 알릴 때나, 누군가 자신의 닉네임을 바꿀 때의 이벤트도 처리할 수 있습니다. 이에 대해서는 POE::Component::IRC 페이지에서 자세하게 확인하실 수 있으실 겁니다. 전부 다 쓰는 것은 역시나 힘들군요.

 - IRC Commands

 IRC에서 사용하는 기본적인 커맨드에 관한 이야기입니다. 이런 커맨드로 인해 이벤트가 발생하고, 그렇게 발생한 이벤트를 위에서 기술한 이벤트 처리에서 다루게 됩니다. 내용은 자주 사용하는 일부의 커맨드만을 다루도록 하겠습니다. 그리고 역시 마찬가지로 POE::Component::IRC 페이지에서 보다 더 자세한 내용을 알 수 있습니다.

  - privmsg
  주로 대부분의 IRC 클라이언트에서 이 명령어를 생략해도 됩니다. 일반적인 말하기 커맨드입니다.

  - join
  IRC 서버 접속 이후, 채널에 입장할 시에 쓰이는 커맨드 입니다.

  - kick
  맘에 안드는 대상을 쫓아냅니다. kick을 사용하는 당사자는 옵을 가지고 있어야 합니다.

  - mode
  채널 내의 어떤 사용자에 대해 권한을 설정할 수 있습니다. 엄격한 정책을 가지고 있는 채널에서는 여러가지 권한을 설정할 수 있습니다.

  - nick
  자신의 닉네임을 변경할 수 있습니다.

  - notice
  통보 메시지를 채널이나 유저에게 알립니다.

  - topic
  방의 주제를 설정할 수 있습니다.


 - Tutorial

 자신이 처리할 이벤트는 미리 POE::Session을 만들 때 등록해 둘 필요가 있습니다.
POE::Session->create(
                     package_states => [
                                        main => [ qw(_default _start irc_001 irc_public irc_msg ) ],
                                        ],
                     heap => { irc => $irc },
                     );

 이렇게 해두면 irc_public 과 irc_msg 를 처리할 수 있게 됩니다.
sub irc_public {
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
my ($nick, $host) = split /!/, $who; # nick 과 호스트를 분리
my $channel = $where->[0]; # $where는 배열 참조
$irc->yield( privmsg => $channel => $nick . " : WTF?" );
# 건방진 봇이 되는 Lifehack
}

 그리고 irc_public 에서 이벤트를 위와 같이 기술합니다.

  $irc->yield ( <blahblah> => <target> => <do something> );

 이와 같이 IRC에 이벤트를 날릴 수 있습니다.
 <blahblah> 에는 위에서 말한 IRC Command 를,
 <target> 에는 채널이름이나 유저를,
 <do something> 에는 말하고 싶은 무언가를...

 물론 커맨드마다 필요한 인수는 다를 수 있으니, 그에 맞게 사용하시면 됩니다.
 실제 사용예제에 대해서는 차후에 다룰 예정이지만, 맘이 급하신 분들은 아래의 -PR 을 따라오시면 됩니다.

  - Summary

  - IRC 의 이벤트 처리에는 여러가지가 있고, 대부분을 처리할 수 있습니다.
  - IRC 커맨드에는 여러종류가 있고, 상황에 맞춰서 사용할 수 있습니다.

  - #4 에서는...

  - POE::Component::IRC::Plugin::(.*) 모듈을 사용해서 이미 만들어져 있는 Plugin 들을 붙여보겠습니다.

  - PR
※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl)에서 JEEN에게 연락하시면 됩니다.

신고
IT/Perl 2008.05.21 13:23

[ Perl ] IRC BOT을 만들어봅시다 :-) #2

 어제 저녁 엔트리에 이어서, 계속해서 써나갑니다.

  IRC BOT을 만들어봅시다 :-) #2

  - #1 에서는... ( 2008/05/20 - [IT/Perl] - IRC BOT을 만들어봅시다 :-) #1 )

   - IRC 를 구현한 모듈은 Net::IRC, PoCo::IRC 가 있다는 것을 알았습니다.
   - PoCo::IRC 모듈을 설치하는 법을 알았습니다.

  - #2 에서는

   - PoCo::IRC를 이용해서 실제로 IRC 서버 접속을 해봅니다.

  - Tutorial

   PoCo::IRC 의 CPAN Module 페이지로 들어가보면 자세한 예제가 나옵니다. 여기서는 이 예제를 바탕으로 시작해보고자 합니다.
 
use strict;
use warnings;
use POE qw(Component::IRC);

my $nickname = 'I_Like_Perl';
my $ircname = 'Bot-Likes-Perl';
my $server = 'irc.blahblahblah.irc';

my @channels = ('#perlbot_test', '#perlbot', '#perl');

my $irc = POE::Component::IRC->spawn(
nick => $nickname,
ircname => $ircname,
server => $server,
) or die "Oh noooo! $!";

POE::Session->create(
package_states => [
main => [ qw(_default _start irc_001 irc_public) ],
],
heap => { irc => $irc },
);

$poe_kernel->run();

sub _start {
my $heap = $_[HEAP];

my $irc = $heap->{irc};

$irc->yield( register => 'all' );
$irc->yield( connect => { } );
return;
}

sub irc_001 {
my $sender = $_[SENDER];

my $irc = $sender->get_heap();

print "Connected to ", $irc->server_name(), "\n";

$irc->yield( join => $_ ) for @channels;
return;
}

sub irc_public {
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];

return;
}

sub _default {
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );

for my $arg (@$args) {
if ( ref $arg eq 'ARRAY' ) {
push( @output, '[' . join(' ,', @$arg ) . ']' );
}
else {
push ( @output, "'$arg'" );
}
}
print join ' ', @output, "\n";
return 0;
}
이와같은 코드를 뽑아왔습니다. 위의 코드를 "bot.pl"로 저장합니다.

그리고 한 부분씩 조심조심 설명 들어가겠습니다.
my $nickname = 'I_Like_Perl'; # 당연히 IRC 상에서의 닉입니다.
my $ircname = 'Bot-Likes-Perl'; # IRC 상에서의 이름입니다. 적당하게 대충쓰시면 됩니다 :-)
my $server = 'irc.hanirc.org'; # 접속할 IRC 서버입니다.
 - 국내의 대표적인 IRC 서버는 hanIRC(irc.hanirc.org)와 단군넷(irc.dankun.net) 이 있습니다.
 - 해외의 대표적인 IRC 서버는 Freenode(irc.freenode.net) 와 Perl IRC(irc.perl.org) 이외에 여럿 있습니다. :-)

 각자 자신이 사용하실 IRC 서버를 넣으시면 되겠습니다.
my @channels = ('#perlbot_test', '#perlbot', '#perl'); # 입장하실 채널을 정해주세요.
 @channels 라는 배열에 처음에 입장하실 IRC 채널을 넣으시면 됩니다. 이렇게 하시면 #perlbot_test, #perlbot, #perl 방에 입장하시게 되는 겁니다. (그 채널에 아무도 없다면 IRC 프로토콜의 정책 상 채널이 생기는 것이 됩니다)
my $irc = POE::Component::IRC->spawn(
nick => $nickname, # 위에서 설정한 닉
ircname => $ircname, # 위에서 설정한 이름
server => $server, # 위에서 설정한 IRC서버
) or die "Oh noooo! $!";
  PoCo::IRC 모듈에 적절한 인수를 맞춰서 호출합니다. 만약 여기서 문제가 생긴다면 "Oh noooo!" 를 외치면서 스크립트는 종료됩니다.

 POE::Session->create(
  package_states => [
  main => [ qw(_default _start irc_001 irc_public) ], # POE 상에서 어떤 상태별로 호출될 서브루틴입니다.
 ],
 heap => { irc => $irc },
 );
 $poe_kernel->run(); # POE Kernel 을 실행합니다.

 위의 _default, _start_ irc_001, irc_public 은 해당되는 상태정보에 따라 호출이 됩니다.
 이에 대해 하나하나 알아볼까요?
sub _start {
my $heap = $_[HEAP];

my $irc = $heap->{irc};

$irc->yield( register => 'all' );
$irc->yield( connect => { } );
return;
}
 _start 메소드의 구성입니다. 여기서는 시작할 때에 IRC 접속을 위한 초기화 작업을 수행합니다. IRC 접속 이전에 각종 플러그인을 여기에서 추가할 수 있습니다.
sub irc_001 {
my $sender = $_[SENDER];

my $irc = $sender->get_heap();

print "Connected to ", $irc->server_name(), "\n";

$irc->yield( join => $_ ) for @channels; # 위에서 정의한 @channels 변수에 담긴 채널에 입장합니다.
return;
}
  irc_001 의 구성입니다. 서버에 접속한 뒤에 한번 호출이 됩니다. IRC 서버에 접속하고서 가장 먼저 해야할 일들을 여기에서 정의해 줄 수 있습니다. 가장 기본적으로 해야할 것은 역시 채널에 접속하는 것입니다.
sub irc_public {
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];

return;
}

irc_public 은 유난히도 받아들이는 인수가 많습니다. 이것은 일반적인 대화시에 호출이 됩니다. 대화시에 발생되는
어떤 이벤트들을 여기에서 구현해 줄 수 있습니다. (누가, 어디서, 무슨 말을 했는가)에 대한 인수를 얻게 됩니다.

sub _default {
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );

for my $arg (@$args) {
if ( ref $arg eq 'ARRAY' ) {
push( @output, '[' . join(' ,', @$arg ) . ']' );
}
else {
push ( @output, "'$arg'" );
}
}
print join ' ', @output, "\n";
return 0;
}

_default 는 어떤 이벤트 발생시의 기록을 남깁니다. 서버로 부터 ping을 받았을 때는 irc_ping, 서버접속시의
상태정보에 대한 것이거나, irc_353 등의 정보로 현재 채널에 입장한 사람이 누구인지 여부를 알 수 있습니다.
irc_join, irc_quit 등의 정보로 지금 막 입장한 사람에 대한 정보도 얻을 수 있구요.

모든 처리는 이런 정보를 기본으로 합니다.
누가 어떤 말을 했을 때, BOT에게 어떻게 하게 한다거나...
어떤 사람이 입장했을 때 그 사람에게 "안녕"이라고 말한다거나 말이죠.

이 부분의 처리에 대해서는 다음 포스트에 계속하겠습니다.

  - Summary

- PoCo::IRC를 이용해서 IRC 서버 접속할 수 있다.
- 해당 변수의 내용을 바꿔서 Bot의 Nick을 바꿀 수 있다.
- irc 상태에 따라 여러 메소드들을 호출 할 수 있다.
- _start, _default, irc_001, irc_public 등의 메소드에서 해당 이벤트들을 처리한다.

  - #3 에서는...

- IRC의 상태에 따른 이벤트 처리를 알아보겠습니다.
- euc-kr, UTF-8 등의 언어코드에 대한 내용을 알아보겠습니다.

  - PR
※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl)에서 JEEN에게 연락하시면 됩니다.
신고
IT/Perl 2008.05.20 21:56

[ Perl ] IRC BOT을 만들어봅시다 :-) #1


 YAPC::ASIA 2008 의 광풍이 불고난 다음, Perl 을 사용하는 사람에게 남은 숙제는, Perl에 대해 제대로 알리는 것입니다. 그리고 어떻게 하면 그것이 가능하느냐에 대해 수없이 고민을 해보았습니다. 어떻게 하면 좋을까 해서 나온 게 일단 use strict 가 무엇이고 문법이 어떻고를 떠나, 어떻게 하면 Perl에 재미를 느낄 수 있을 까 하는 것입니다.
 그래서 생각한 것이 현재 irc.hanirc.org, #perl 에서 움직이고 있는 Perl_^^ BOT에 대한 내용을 한번 올려보면 어떨까 하는 것에서 시작합니다.
 
사용자 삽입 이미지

 IRC BOT을 만들어봅시다 :-) #1

 - Warning!!

 - 우선 이 내용에 대해서 불건전한 사용에 대한 책임은 지지 않습니다.
 - 본 내용은 "난 Perl 을 들어봤다. Linux 커맨드는 기본적인 것은 알고 있다." 의 조건에 부합하신 분들에게 최적화 되어 있습니다.
 - IRC Port가 열려 있어야 합니다.
 - 여기에서는 제 시스템 환경인 CentOS 5, Perl 5.8 이상의 환경을 기반으로 합니다.
 - 특히 Windows 의 경우에는 사용해 본 적이 없기에 장담할 수 없습니다.

 - Intro...


 IRC BOT 을 만들 기 위해, IRC Protocol 을 미리 다 알 필요는 없습니다(물론 저도 전부 모릅니다).
 미리 IRC 프로토콜에 맞춰서 만들어진 모듈이 이미 존재하고, 이에 대한 내용이 구성되어 있기 때문이죠.

  Perl CPAN 모듈에서 대표적인 IRC 클라이언트 모듈은

  Net::IRCPOE::Component::IRC 가 있습니다.

  이중에서 Net::IRC는 2004년 이후로는 관리되지 않고 있으며,
  POE::Component::IRC 는 2008년 4월까지 꾸준히 계속해서 관리되어 오고 있고, 이 모듈을 위한 여러가지 플러그인들이 존재하고 있습니다.

  그리고 이제부터 다루고자 할 모듈도 POE::Component::IRC(이하 PoCo::IRC) 입니다.

  - Tutorial

  우선적으로 해야할 것은 CPAN Module 의 인스톨입니다.

  대부분의 리눅스계열의 OS는 Perl은 기본적으로 들어가 있고, cpan 역시 기본적으로 지원하고 있습니다.
  만약, root 계정을 사용할 수 없으시거나, 웹 호스팅을 이용하고 계신다면, CPAN Module을 로컬 계정에 설치할 수 있습니다.
  이에 대해서는 펄매니아 위키에 자세히 적혀져 있으므로 참고하세요.

$ cpan
cpan shell -- CPAN exploration and modules installation (v1.9205)
ReadLine support enabled
                                                                                                                                              cpan[1]>

  cpan 을 실행하면 cpan용 터미널이 뜹니다. 이때 인스톨 하실 모듈을 넣어주시면 됩니다.

cpan[1]> install POE::Component::IRC

  그러면 모듈을 설치합니다. 중간에 어떤 것을 물어보신다면 그냥 엔터만 믿고 눌러주시면 됩니다.

cpan[2]> quit

  설치가 종료하면 "quit"으로 나옵니다.

  이상으로 POE::Component::IRC 모듈의 설치가 끝났습니다.

 - Summary

  - IRC 를 이용하기 위한 CPAN Module에는 대표적으로 Net::IRC, POE::Component::IRC 가 있습니다.
  - 웹 호스팅을 이용하시거나, root 계정을 사용할 수 없으실 때에는 이곳을 참조하세요.
  - CPAN Module 을 인스톨 하기 위해서는 "cpan 터미널에서 install [모듈이름]을 입력합니다."
    (혹은 cpan [모듈이름])

 - #2에서는

  - PoCo::IRC를 이용해서 BOT의 Nick 을 설정합니다.
  - IRC 서버에 접속합니다.
  - PoCo::IRC를 이용해서 어떤 처리를 할 수 있는지 알아봅니다.

※ 현재 POE::Component::IRC를 사용한 Perl Bot 만들기 프로젝트가 진행중입니다.
- http://code.google.com/p/perl-irc-bot
※ 더불어 perldoc을 한글로 번역하기 위한 프로젝트도 진행중입니다.
- http://code.google.com/p/perldoc-kr
※ 거기에 덧붙여 me2day Perl API를 만드는 프로젝트도 진행중입니다.
- http://code.google.com/p/me2day-perl-api

 참여를 원하시면 댓글을 달아주시거나, email: aiatejin@gmail.com
 혹은 IRC(irc.hanirc.org, #perl)에서 JEEN에게 연락하시면 됩니다.

신고

'IT > Perl' 카테고리의 다른 글

[ Perl ] IRC BOT을 만들어봅시다 :-) #2  (4) 2008.05.21
[ Perl ] IRC BOT을 만들어봅시다 :-) #1  (6) 2008.05.20
YAPC::Asia 2008 이야기 #3  (2) 2008.05.18
YAPC::Asia 2008 이야기 #2  (0) 2008.05.18
TOTAL 488,169 TODAY 38

티스토리 툴바