Make a simple IRC bot.

10

2

Task

Make a simple IRC bot which does simple task like greeting user.

Detail

(You may see this RFC document.)

Write a complete program that perform these:

  1. The program inputs nickname and realname from the user.

    • All nickname and realname which form is [A-Za-z0-9]+ should be allowed, but it isn't important whether other forms are allowed. For example, if the user wants 1234qwerty as nickname and 5319009 as realname, the program should use those names, but it isn't important whether the program can use ^^☆Rick Astley☆^^ (which includes non-alphabet&decimals) as (nickname or realname) or not, even thought this name may be rejected by the server.
  2. The program inputs serverAddress from the user.

    • The form of serverAddress is serverIP:serverPort, where serverIP is the IP address of the server and serverPort is the port num of the server.
  3. The program connects to port serverPort at IRC server serverIP, and it should set it's nickname as nickname and realname as realname by sending USER realname 0 * :realname and NICK nickname.

  4. The program inputs a single channelName which doesn't include #, and join to the channel #channelName.

  5. Send a private message Hello, world! to the #channelName channel like this: PRIVMSG #channelName :Hello, world!

  6. Then, the program does these:

    A. If someone joins, greeting him by saying Hello, @! to #channelName, where @ is the nickname of him/her.

    B. If the program is kicked, try re-joining.

    C. If a person is kicked or banned, say POW HAHA.

    D. If a person leaves (PART or QUIT), say Goodbye, @!, where @ is the nickname of him/her.

    E. If someone says some text including Turing test or turing test, say I'm a human!.

    F. If someone says some text including 6*9, quit by sending QUIT :42 to the server.

    G. If someone says some text including all your base are belong to us(case-insensitive), quit by sending QUIT :What you say! to the server.

    H. If someone says some text preceding with a space and above acts aren't performed by the text, say the sum of charcode of all (except the first space) chars in the text.

If an error is occurred

If an error (such as connection error or invalid nickname) is occurred, you can do what you want to do. For example, if the nickname is already in use, the program may stop, gets a new nickname from the user, or automatically change the nickname and retry.

Restriction

The program

  • Should not use any internal functions or libraries which is for IRC client/bot making. i.e. something like IRC client libraries
  • Should prompt what it'll input, before it gets an input. For example, before inputs serverAddress, it should prompt Write the address of server:, serverIP:serverPort > , serverAddress >, or some text that the user can recognize.
  • Should work on several IRC servers using UTF-8. (i.e. not for single server)
  • 's messages send to server shouldn't be NOTICE. (RFC 2812, 3.3.2 Notice : The difference between NOTICE and PRIVMSG is that automatic replies MUST NEVER be sent in response to a NOTICE message.)
  • Should send a message where the request came from (#channelName or nickname).
  • Should PONG when PINGed, of course.

PS

Well, I just made this, because there was no puzzle about IRC bot.

Edit

After reading @dmckee's comment, I changed the rule for choosing winner.

  1. Default bonus = +0

  2. If somehow the program can connect to a channel (even if it's not #channelName), bonus = 1

  3. If the program can perform step 1~5, bonus *= 2.

  4. For each tasks A~H in step 6, if the program implements, bonus *= 1.2.

  5. For each six restrictions, if the program follows that, bonus *= 1.05.

and score = int(codelength/bonus).

Program with lowest score is winner. If two answers have same score, then the answer with highest votes wins.

EDIT Edit : I think 1.2 and 1.05 in step 4 and 5 should be adjusted slightly bigger.. What do you think about this?

The winner is decided, however, I think there was too few entries.

Anyone still may submit the code.. :P

JiminP

Posted 2011-06-11T08:52:51.063

Reputation: 3 264

@lunixbochs: as a person who uses IRC waaay too much, kicks are channel only. If you're not on the server anymore, you got killed. – Wug – 2012-10-09T19:45:59.643

4

Frankly we have not been very accepting of "There is no metric, just vote.". There are things for you to work with here in terms of setting up an objective metric for this task. See Fake error message for a possible example.

– dmckee --- ex-moderator kitten – 2011-06-11T16:18:49.620

@dmckee : Sorry, I didn't know that votes was quite subjective.. :( – JiminP – 2011-06-12T00:36:09.150

@JiminP: Nice save. – dmckee --- ex-moderator kitten – 2011-06-12T00:38:35.863

irc bot sounds like a longer golf - I'm in though :) – lunixbochs – 2011-06-12T01:01:00.737

clarification: "If the program is kicked, try re-joining." - does this mean from the server or the channel? – lunixbochs – 2011-06-12T01:40:13.213

@lunixbochs From the channel. – JiminP – 2011-06-12T01:52:32.110

no other comers? sadness. playing golf by myself is lonely :) – lunixbochs – 2011-06-15T06:09:20.083

I think I can't decide winner now, because there isn't many codes... :( – JiminP – 2011-06-17T05:53:16.547

Answers

1

PHP - 121 points

  • 1396 characters
  • Follows all steps (including extra) and restrictions

Score

<?php
$bonus = 1;                             //connects to channel
$bonus *= 2;                            //completes steps 1-5
for ($x=0;$x < 8;$x++) $bonus *= 1.2;   //all 8 extra steps
for ($y=0;$y < 6;$y++) $bonus *= 1.05;  //all 6 restrictions
echo (int)(1396 / $bonus);

> 121

Code

<?php function w($t){global$s;echo$t."\n";socket_write($s,$t."\r\n");}function m($c,$t){w("PRIVMSG $c :$t");}@$y=fgets;$z=STDIN;echo"nickname>";$n=trim($y($z));echo"realname>";$r=trim($y($z));echo"serverIP:Port>";$a=explode(":",trim($y($z)));$s=socket_create(2,1,6);socket_connect($s,$a[0],$a[1]);w("NICK $n");w("USER $n 0 * :$r");while(1){if((!($l=trim(socket_read($s,512,1))))||(!preg_match("/^(?:\:(\S+)[ ]+)?([^: ][^ ]*)(?:[ ]+([^:].*?))?(?:[ ]+:(.+))?$/",$l,$m)))continue;echo$l."\n";if(preg_match("/(.+)!(.+)@(.+)/",$m[1],$o))$m[1]=$o;if(!empty($m[3]))$p=explode(" ",$m[3]);else$p=array();if(isset($m[4])) $p[]=$m[4];$p=array($m[1],$m[2],$p);$b=$p[2][0];@$e=$p[0][1];@$f=$p[2][1];if($p[1]=="PING")w("PONG :".$b);elseif($p[1]=="376"){echo"#channel>";$c=trim($y($z));$c=(!empty($c))?$c:"#rintaun";w("JOIN $c");}elseif($p[1]=="JOIN")if($e!=$n)m($b,"Hello, $e!");else m($b,"Hello, world!");elseif($p[1]=="KICK")if($f!=$n)m($b,"POW HAHA");else w("JOIN ".$b);elseif(($p[1]=="PART")&&($e!=$n))m($b,"Goodbye, $e!");elseif(($p[1]=="QUIT")&&($e!=$n))m($c,"Goodbye, $e!");elseif($p[1]=="PRIVMSG")if(preg_match("/[Tt]uring test/",$f))m(($b==$n)?$e:$b,"I'm a human!");elseif(strstr($f,"6*9")){w("QUIT :42");break;}elseif(stristr($f,"all your base are belong to us")){w("QUIT :What you say!");break;}elseif($f[0]==" "){$q=str_split(substr($f,1));$u=0;foreach($q AS $d)$u+=ord($d);m(($b==$n)?$e:$b,$u);}}

Side Note

My answer actually differs from the instructions very slightly; the instructions say to use the realname as the first parameter to the USER command when registering, but I used the nickname instead. This is because the realname is allowed to have spaces, while this parameter is not. It's something of a moot point, though, since switching in the variable for realname would be the exact same character count.

rintaun

Posted 2011-06-11T08:52:51.063

Reputation: 751

5

Python - 125 points

  • 304 chars
  • follows steps 1-5 (works for me on irc.freenode.net:6667. if you try there, remember IDENT might take a while, so give it 20+ seconds to connect and speak)
  • follows 4 restrictions (assuming the NOTICE restriction which is a bit unclear. the UTF restriction counts because python won't choke if it doesn't need to parse any UTF).

score math (python):

bonus = 1 # connects to channel
bonus *= 2 # steps 1-5
for i in xrange(4):
    bonus *= 1.05 # 4 restrictions

int(304.0/bonus)
> 125

code:

import socket
i=raw_input
u=i('user# ')
n=i('nick# ')
h,p=i('host:port# ').split(':',1)
p=int(p)
c='#'+i('chan# ')
z=0
while 1:
 try:
  def s(m): z.send(m+'\r\n')
  z.recv(9)
 except:
  z=socket.socket();z.connect((h,p));s('USER '+n+' 0 * :'+n);s('NICK '+n);s('JOIN '+c);s('PRIVMSG '+c+' :Hello, world!')

lunixbochs

Posted 2011-06-11T08:52:51.063

Reputation: 151

5

Perl, 66 points

  • 666 characters
  • all substeps
  • half of the restrictions

Score

use 5.010;
$bonus = 1;              # connects to channel
$bonus *= 2;             # steps 1 to 5
$bonus *= 1.2 for 1..8;  # substeps A to H
$bonus *= 1.05 for 1..3; # restrictions 3, 4, 6
say int(666 / $bonus);
> 66

Code

(newlines for presentation only, not counted let alone acceptable)

use POE"Component::IRC";$_='sub _start{Zregister,all);Z"connect")}subX001{
Zjoin=>$c);ZY"Hello, world!")}*Xquit=*Xpart=*Xjoin=sub{$g=$_[STATE]=~/t/?G
oodbye:Hello;$_=$_[ARG0];/\w+/;ZY"$g, $&!")};subXkick{$_=$_[ARG2];/\w+/;Z$
&eq$n?"join":Y"POW HAHA")}subXpublic{$_=$_[ARG2];if(/turing test/i){ZY"I\'
m a human!")}elsif(/6\*9/){Zquit,42)}elsif(/all your base are belong to us
/i){Zquit,"What you say!")}elsif(/^ /){$t=-32;$t+=ord for/./g;ZY$t)}}chop(
($n,$r,$s,$c)=<>);$c="#$c";$i=POE::Component::IRC->spawn(nick,$n,ircname,$
r,server,$s)';s/Z/\$i->yield(/g;s/Y/privmsg,\$c,/g;S/X/ irc_/g;eval;POE::S
ession->create(package_states,[main,[grep*$_{CODE},%::]]);POE::Kernel->run

Side Note

The "ALL YOUR BASE ARE BELONG TO US" substep is most probably not worth its character count, but if I dropped it I wouldn't have the nice character count. Bugger.

J B

Posted 2011-06-11T08:52:51.063

Reputation: 9 638

+1 for 66 points and 666 characters. Is that intentional or did you get that by accident? – nyuszika7h – 2014-04-28T12:55:25.730

@nyuszika7h I honestly don't remember. – J B – 2014-04-28T13:18:21.107

2

Ruby, 65 points

  • 574 Chars
  • Steps 1-5, 6(A-G) and restrictions on libs, UTF-8, no NOTICE & PONG
  • Takes commandline args in the form of server:6667 botnick botuser channel

Score

bonus = 0       # We've done nothing yet
bonus += 1      # Connects to channel
bonus *= 2      # Step 1 - 5

for n in 1..7
    bonus *= 1.2    # A - G
end

for n in 1..4
    bonus *= 1.05   # 4 constraints
end

puts (574/bonus).to_i
> 65

Code

require'socket'
a=ARGV
c="##{a[3]}"
h,p=a[0].split':'
s=TCPSocket.open(h,p)
m="PRIVMSG #{c} :"
s.puts"USER #{a[2]} 0 * :#{a[2]}\nNICK #{a[1]}\nJOIN #{c}\n#{m}Hello, world!"
while l=s.gets
case l
when/\:(.+?)!(.+)JOIN/
s.puts"#{m}Hello, #{$1}!"
when/KICK (.+?) (.+?) \:(.+)/
if $2==a[1]
s.puts"JOIN #{c}"
else
s.puts"#{m}POW HAHA"
end
when/\:(.+?)\!(.+)(PART|QUIT)/
s.puts"#{m}Goodbye, #{$1}!"
when/turing test/i
s.puts"#{m}I'm a human!"
when/6\*9/
s.puts"QUIT :42"
when/all your base are belong to us/i
s.puts"QUIT :What you say!"
when/PING \:(.+)/
puts"PONG #{$1}"
end
end

coolfire

Posted 2011-06-11T08:52:51.063

Reputation: 41

2

Ruby, 28 points

Score

bonus = 0       # We've done nothing yet
bonus += 1      # Connects to channel
bonus *= 2      # Step 1 - 5

for n in 1..7
    bonus *= 1.2    # A - G
end

for n in 1..4
    bonus *= 1.05   # 4 constraints
end

puts (249/bonus).to_i
>  28

Code

require 'zlib'
b=<<'E'
x�]�_o�0���)��q�-KFBcdf��߃�P�S�B�����(N�Л��s~�� ��(�_$U6��5G�)�r�BB������J�{��� nNhlO�f)QQdJ�g��'�yP�!!����K�ɫ��[Ё�Ə{0�F]ѽ�m�2���GŐP��p`   ��I����E�+�* z�                                                                                                                                                           )jrmKR�ˮ�%�
#��nQaJ�H��<�ZT���虦T3.�$D('�hw��a�/'��&�_ei�}o��1���M$����H��J�$��������V"���"��'��|A�`<��3L)Y��Z|� e�� ���m�é��ǚ�ڎu��J�����Vq~(ح�
E
eval Zlib::Inflate.new.inflate b

coolfire

Posted 2011-06-11T08:52:51.063

Reputation: 41