[Tävling] Skapa en simpel IRC-bot i valfritt språk

Permalänk
Medlem

[Tävling] Skapa en simpel IRC-bot i valfritt språk

Tja allihop!

Det var ett tag sedan man swecade och jag kom och tänka på en liten sak. Nämligen en liten tävling där man skapar en jättesimpel IRC-bot i valfritt språk. Syftet med denna tävling är att jag (och en del andra här på forumet) är nybörjare och intresserade av nätverksprogrammering och därför vill se lite exempel på exempelvis IRC-bottar.

Min personliga erfarenhet av nätverksprogrammering är inte mer avancerad än en "webserver" i Ruby som läser från en enda fil samt räknar besökarna och lägger till en liten ram i översta hörnet av webbläsaren där det står valfri text samt antalet besökare. Jag är som sagt nybörjare och programmerar bara för att det är kul och trevligt med lite hjärngympa.

Men för att återgå till själva tävlingen så finns det inga regler förutom dessa:

  • Den ska koppla upp sig till en IRC-server

  • Den ska joina en/flera kanaler vid inloggningen

  • Idla i kanalen tills dess att man stänger av scriptet/programmet

Plus i kanten till de som implanterar exempelvis följande funktioner:

  • /msg botten så utför den ett visst kommando (Exempelvis chattar/opar osv)

  • Svara på vissa fraser

  • autoexecute command när en viss användare loggar in på kanalen

  • you name it...

Meningen med denna tävling är att göra en SIMPEL bott utan massa onödiga interface och onödiga funktioner, försök att få koden så lättläst och kort som möjligt

Tävlingen avslutas: När någon kommer med ett rimligt förslag på datum.
Pris?: Äran samt ett kärleksmejl från mig om så önskas

Code on, boys!

Permalänk
Hedersmedlem

Fan vad kul!
*börjar koda*

Hoppas att det går bra med olika språk och eventuellt olika versioner i samma språk (en lättläst, en obskyr ).

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Såklart det går bra!

Enda exemplen jag har sett på simpla IRC-bottar är antingen halvfungerande eller överbloatade

Ju mer exempelmaterial desto bättre för oss som vill lära oss

Permalänk
Medlem

Vilken python-modul är bäst för detta? Threading?
EDIT: socket?
Låter riktigt skoj!

Permalänk
Hedersmedlem

ErikHK: socket är det du ska använda (du ska ju skicka nätverksdata). Trådar är otroligt overkill för en enkel bot.

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Jag som just satt och bläddrade i java.net delen i API'n. Får läsa på lite om IRC-protokollet så är jag med också.

edit: Java då som sagt... förmodligen terminalbaserat

Permalänk
Hedersmedlem

Sådär, då har jag två (nästan indentiska) botar, en i Perl och en i OCaml.
Perl:

#!/usr/bin/perl use strict; use warnings; use IO::Socket; # Settings my $server = 'irc.freenode.org'; my $port = 6667; my $channel = '#bot_test'; my $nickname = 'easy_plbot'; my $username = 'easy_perl_bot'; my $realname = 'Easy Perl IRC-bot'; # Connect to server my $s = IO::Socket::INET->new(PeerAddr => $server, PeerPort => $port, Proto => 'tcp') or die('Couldn\'t connect to server.'); # Send a command to the socket sub send_command { my $command = shift; print $s "$command\r\n"; } # Set nickname and user send_command("NICK $nickname"); send_command("USER $username aoeu htns :$realname"); while (my $d = <$s>) { # Check for the message 004 (if there is one, we're connected) if ($d =~ /004/) { # Is the nickname already in use? if ($d =~ /433/) { print 'Nickname already taken.'; } else { last; } } } # Join channel send_command("JOIN $channel\r\n"); # Main loop while (my $d = <$s>) { # Match the IRC-protocol in one regexp, SWEET! if ($d =~ m/^(:([^ ]+))?[ ]*([^ ]+)[ ]+:?([^ ]*)[ ]*:?(.*)$/) { my $prefix = $1; my $command = $3; my $arg = $4; my $args = $5; # Extract nickname from the prefix $prefix =~ m/:(.+)!.+/; my $who = $1; # Some commands and responses if ($command eq 'PING') { send_command("PONG :$args"); print 'PING? PONG!'; } elsif ($command eq 'PRIVMSG') { print "[MSG] $who> $args"; } elsif ($command eq 'NOTICE') { print "[NOTICE] $who> $args"; } elsif ($command eq 'JOIN') { print "[JOIN] $who -> $arg"; } else { print "[$command] $who {$arg} ($args)"; } } } print "\n";

OCaml:

open Unix open Str open Printf (* Settings *) let server = "irc.freenode.org" let port = 6667 let channel = "#bot_test" let nickname = "easy_mlbot" let username = "easy_ocaml_bot" let realname = "Easy O'Caml IRC-Bot" let ic = ref Pervasives.stdin let oc = ref Pervasives.stdout (* Connect to the server *) let connect () = let host = gethostbyname server in let addr = ADDR_INET(host.h_addr_list.(0), port) in let fic, foc = open_connection addr in ic := fic; oc := foc ;; (* Send a command to the output stream *) let send_command command = output_string !oc (command ^ "\r\n"); Pervasives.flush !oc ;; (* Print message from server *) let print message = print_endline message; Pervasives.flush_all () ;; (* Connect *) connect ();; (* Set nickname and user *) send_command ("NICK " ^ nickname);; send_command (sprintf "USER %s aoeu htns :%s" username realname) let rec loop () = let line = input_line !ic in (* Check for the message 004 (if there is one, we're connected) *) match string_match (regexp "004") line 0 with | true -> (* Is the nickname already in use? *) (match string_match (regexp "433") line 0 with | true -> print "Nickname already taken." | false -> ()) | false -> () ;; loop ();; (* Join channel *) send_command ("JOIN " ^ channel);; while true do let line = input_line !ic in (* Matches the IRC-protocol in one regexp, SWEET! *) let r = regexp "^\\(:\\([^ ]+\\)\\)?[ ]*\\([^ ]+\\)[ ]+:?\\([^ ]*\\)[ ]*:?\\(.*\\)$" in match string_match r line 0 with (* We got a match *) | true -> (* We don't want to be interupted by some annoying Not_found exception *) let safe_match n s = try matched_group n s with Not_found -> "" in let prefix = safe_match 1 line in let command = safe_match 3 line in let arg = safe_match 4 line in let args = safe_match 5 line in (* Extract nickname from the prefix *) let who = try String.sub prefix 1 ((String.index prefix '!') - 1) with Not_found -> "" in (* Some commands and responses *) (match command with | "PRIVMSG" -> print ("[MSG] " ^ who ^ "> " ^ args) | "NOTICE" -> print ("[NOTICE] " ^ prefix ^ "> " ^ args) | "JOIN" -> print ("[JOIN] " ^ prefix ^ " -> " ^ arg) | "PING" -> send_command ("PING :" ^ args); print "PING? PONG!" | _ -> print ("[" ^ command ^ "] " ^ prefix ^ " {" ^ arg ^ "} (" ^ args ^ ")")) | false -> () done

Fler kommer.

EDIT: Inte så mycket att kommentera men lite kommentarer ditlagda.

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk

Jag är på, funderar på om det blir c eller java.

Visa signatur

System.out.print(madness ? this.is.SPARTA : "");

Permalänk
Medlem

Nice, mOREC. Du har inte lust och förklara exakt vad den har för funktioner.. alltså vad gör den?

Permalänk
Medlem

Har snart klar skalet till min också

Permalänk
Hedersmedlem

Jaha, då kommer en i Java, nu med kommentarer. Ska ta och kommentera de andra också.

import java.io.*; import java.net.*; import java.util.regex.*; public class bot_easy { // Send a command to the socket static void send_command(String string, OutputStream os) throws IOException { os.write((string + "\r\n").getBytes()); os.flush(); } static String group(Matcher m, int group) { return m.group(group) == null ? "" : m.group(group); } public static void main(String argv[]) throws IOException { int port; String server, channel, nickname, username, realname, data; Socket socket; BufferedReader is; OutputStream os; // Settings server = "irc.freenode.org"; port = 6667; channel = "#bot_test"; nickname = "easy_jbot"; username = "easy_java_bot"; realname = "Easy Java IRC-bot"; // Connect to server socket = new Socket(server, port); is = new BufferedReader(new InputStreamReader(socket.getInputStream())); os = socket.getOutputStream(); // Set nickname and user send_command("NICK " + nickname, os); send_command("USER " + username + " aoeu htns :" + realname, os); while (true) { data = is.readLine(); // Check for the message 004 (if there is one, we're connected) if (data.indexOf("004") != -1) // Is the nickname already in use? if (data.indexOf("433") != -1) System.out.println("Nickname already taken."); else break; } // Join channel send_command("JOIN " + channel, os); // Main loop while (true) { String prefix, command, arg, args, who; Matcher m; data = is.readLine(); // Matches the IRC-protocol in one regex, SWEET! m = Pattern.compile("^(:([^ ]+))?[ ]*([^ ]+)[ ]+:?([^ ]*)[ ]*:?(.*)$").matcher(data); // We got a match if (m.matches()) { prefix = group(m, 1); command = group(m, 3); arg = group(m, 4); args = group(m, 5); // Extract nickname from the prefix m = Pattern.compile(":(.+)!.+").matcher(prefix); who = m.matches() ? group(m, 1) : prefix; // Some commands and responses if (command.equals("PING")) { send_command("PONG :" + args, os); System.out.println("PING? PONG!"); } else if (command.equals("PRIVMSG")) System.out.println("[MSG] " + who + "> " + args); else if (command.equals("NOTICE")) System.out.println("[NOTICE] " + who + "> " + args); else if (command.equals("JOIN")) System.out.println("[JOIN] " + who + " -> " + arg); else System.out.println("[" + command + "] " + who + " {" + arg + "} (" + args + ")"); } } } }

EDIT: Dör inte längre av NullPointerException.
EDIT2: Vad jag nu tänkte på när jag gjorde socket global...

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Ghost_Overdoze: Jag är din glada beundrare om du gör en i C. Efterom att jag själv försöker att komma igång med denna sorts programmering, skulle vara kul att se ett inte så advancerat exempel :).

Permalänk
Medlem

Orkar inte ens pastea min färdiga beta i Java än då morecs klarar mer än min

Just nu så använder den en skitful lösning för att splitta upp datat (via String.split() ). Morecs regexp lösning var sexig dock

Permalänk
Hedersmedlem
Citat:

Ursprungligen inskrivet av Buffi
Orkar inte ens pastea min färdiga beta i Java än då morecs klarar mer än min

Just nu så använder den en skitful lösning för att splitta upp datat (via String.split() ). Morecs regexp lösning var sexig dock

Låt oss säga att jag kodat en del IRC-botar i ett antal olika språk.

EDIT: Samtliga botar än så länge har inte gjort mer än att ansluta, joina en kanal och sedan lyssna där, de skriver ut meddelanden i formatet

[MSG] nickname> meddelande

Notice-meddelanden i formatet

[NOTICE] nickname> meddelande

Join-medelanden i formatet

[JOIN] nickname -> kanal

samt skriva ut övriga meddelanden i formatet

[meddelande] nickname {argument} (resterande argument)

Dessutom svarar de på PING.

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Alltså shit... jag känner mig helt knäckt av det där reguljära uttrycket fortfarande...

Jag tar helt enkelt och snor den delen av din kod då den är ruggigt mer optimerad än min och postar när jag har något mer vettigt istället

edit: Morec: Du har en bugg i din kod btw...
programmet kastar NullPointerException när det blir pingat (gissningvis för att PING inte har något prefix).

Knappast svårt att gå runt, men bra att veta.

Permalänk
Hedersmedlem

Oh, har glömt fixa det i Java-koden (gjorde det i OCaml som synes).

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Av någon anledning har jag problem att connecta till vissa servrar med liknande kod också (får samma fel om jag pastear koden rakt av).

Freenode går jättebra men quakenet funkar inte alls... beror det på att servern inte svarar nog snabbt eller vad kan det vara för olustigt?

Permalänk
Hedersmedlem

Beror på att deras servermjukvara inte följer specifikationen.

Specifikationen säger att PING ska skickas när en klient varit inaktiv ett tag för att se så att den lever, Quakenet däremot börjar med en PING, om man inte svarar på den ansluts man inte. Med andra ord måste man koda om sättet att ansluta på. Jag valde ett sätt som fungerar för standarden men inte för Quakenet.

Men det går självklart att fixa till den för alla nät.

EDIT: Och visst är det värdelöst med folk som avviker från standarder?

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem

Hade(/Har) ingen större kunskap om hur irc-protokollet är uppbyggt, så jag lät mig inspireras väldigt(med betoning på väldigt ) mycket av mOREc:s lösningar.. (Har heller aldrig arbetat med sockets, och det språk jag valde kanske inte är det bästa språk att börja leka med sånna saker i...)

En irc-bot i php:

<?php $server="irc.freenode.org"; $port="6667"; $chan="#myIRCBOT"; $nickname="myIRCBOT7"; $username="myircbot"; $realname="Irc Bot"; $textFile=fopen("textDump.txt","w"); //skriptet måste ha skrivrättigheter till filen! $socket = stream_socket_client("tcp://$server:$port",$errno, $errstr, 30); $runSystem=true; if (!$socket) { logText($errstr.' ('.$errno.")<br />\n"); } else { sendText("NICK ".$nickname,$socket); sendText("USER ".$username." aoeu htns :".$realname,$socket); while(true) { $result=fgets($socket); if(strpos($result,"004")!==false) { logText("[NOTE]Successful login!",$textFile); break; } elseif(strpos($result,"433")!==false) { logText("[NOTE]Unsuccessful login! Nick in use",$textFile); $runSystem=false; break; } } if($runSystem) { //Skip the motd.. while(strpos(fgets($socket),"376")===false); sendText("JOIN ".$chan,$socket); } //Main loop while($runSystem) { $data=fgets($socket); if(strpos($data,"die bot")!==false)//Allowing clean shutdown of bot $runSystem=false; $matches=array(); if(preg_match_all("/^:(.*?) (.*?) (.*?)(\s(.*))?$/",$data,$matches)>0) { $prefix=$matches[1][0]; $command=$matches[2][0]; $arg=$matches[3][0]; $args=$matches[5][0]; $who=(preg_match("/^(.*?)!/",$prefix,$match)>0)?$match[1]:""; if($command=="PING") { sendText("PONG :".$args,$socket); logText("PING? PONG!",$textFile); } elseif($command=="PRIVMSG") logText("[MSG] $who > $args",$textFile); elseif($command=="NOTICE") logText("[NOTICE] $who > $args",$textFile); elseif($command=="JOIN") logText("[JOIN] $who -> $arg",$textFile); else logText("[$command] $who > {$arg} ($args)",$textFile); } } sendText("QUIT",$socket); } function sendText($string,$handle) { fwrite($handle,($string."\r\n")); fflush($handle); } function logText($string,$handle) { fwrite($handle,$string."\n"); fflush($handle); } fclose($textFile); fclose($socket); ?>

(Eftersom php inte kan skriva ut text till användaren sammtidigt som den processerar data (leker med sockets), så fick jag lov att dela upp botten i två delar, bot-delen (ovan) skriver ut all output till en fil, och den del som användaren använder (nedan), skriver ut det på skärmen..

<?php echo('<?xml');?> version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>myIRCBOT</title> <meta http-equiv="refresh" content="1" /> </head> <body> <div> <p> <?php echo nl2br(file_get_contents("textDump.txt")); ?> </p> </div> </body> </html>

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Medlem

Riktigt clean Java kod m0rec, ruskigt snyggt och enkelt. Äntligen en person som kan indentera ordentligt. Fick tag på lite kod från UU, där verkade som att de aldrig hade hört talas om indentering och radbyte.

Visa signatur

En fisk...

Permalänk
Medlem

mOrec: Tack så mycket för infon ang quakenet och ping.
Funkar fint för mig nu!

Och ja, helt fucking worthless att inte följa standards.

edit: Kan väl skriva hur jag gjorde det också om någon undrade... fast ingen lär väl ha problem med det direkt...

while (true) { data = is.readLine(); if(data.startsWith("PING")) send_command("PONG :"+ data.split(":")[1],os); // Check for the message 004 (if there is one, we're connected) if (data.indexOf("004") != -1) // Is the nickname already in use? if (data.indexOf("433") != -1) System.out.println("Nickname already taken."); else break; }

004-loopen byts ut mot det där alltså...

Permalänk
Medlem

En IRC-bot i Petrovich:

Petrovich> do something 14:21:37 1 March 2006 Petrovich> punish Petrovich> do something Generating IRC-bot Petrovich> reward Petrovich> do something with IRC-bot Starting IRC-bot Petrovich> reward

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Don_Tomaso
En IRC-bot i Petrovich:

Petrovich> do something 14:21:37 1 March 2006 Petrovich> punish Petrovich> do something Generating IRC-bot Petrovich> reward Petrovich> do something with IRC-bot Starting IRC-bot Petrovich> reward

Grattis! du får min röst utan tvekan
Detta vore något

Citat:

And in case you think this is entirely a joke, imagine a Petrovich layer over another operating system, such as Microsoft Windows (TM). Every time Windows does something you don't like, you could punish it, and it would never do it again...

Visa signatur

"Sleep is one of those funny things about being a human being" - Marshall Brain
"Mike broke the hubble, Mike broke the hubble" - Mystery Science Theater 3000
"Det finns inget som kan kallas skrot!" säger Mulle Meck, "bara gamla saker som går att använda på nya sätt. Rulla ut och rulla på."

Permalänk
Medlem

Ah, nice. Har en irc-klient skriven i java. Bara att slakta ut allt skräp.

Visa signatur

Nej är också service.

Permalänk
Medlem

Jag har skrivit en bot i PHP som körs via cmd (kommandotolken), men den bygger på en (halv)färdig, så jag skiter i att posta den

Vad jag inte fattar är hur man gör för att skriva ut som mIRCs "/me"-kommando.

Visa signatur
Permalänk
Medlem
Citat:

Ursprungligen inskrivet av dotar
Jag har skrivit en bot i PHP som körs via cmd (kommandotolken), men den bygger på en (halv)färdig, så jag skiter i att posta den

Vad jag inte fattar är hur man gör för att skriva ut som mIRCs "/me"-kommando.

Någon får gärna rätta mig om jag har fel men jag tror att man göra så här

PRIVMSG #kanal :ACTION äter bullar

Permalänk
Hedersmedlem

PRIVMSG #kanal :\001ACTION äter bullar\001

http://www.irchelp.org/irchelp/rfc/ctcpspec.html

Det tillhör inte IRC-protokollet utan CTCP (Client-To-Client Protocol).

Visa signatur

Vim
Kinesis Classic Contoured (svart), Svorak (A5)
Medlem i signaturgruppen Vimzealoter.

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av m0REc

PRIVMSG #kanal :\001ACTION äter bullar\001

http://www.irchelp.org/irchelp/rfc/ctcpspec.html

Det tillhör inte IRC-protokollet utan CTCP (Client-To-Client Protocol).

Hmm.. kom fram till att jag behövde använda något specialtecken som ser ut som en svart smilie med vilket teckensnitt nu kommandotolken visar. =/
Men det funkar nu iaf

Fick också till VERSION- och TIME reply.

Visa signatur
Permalänk
Medlem

Oj, trevlig tävling.
Fixa in ett bidrag i C nu någon, så jag får se varför min inte ville funka

Ska nog försöka göra en i Ruby, PHP eller Python.

Visa signatur

Också känd som eml
* C99, OSX86, PHP, Python, Warsow

Permalänk
Medlem

Ok, här kommer en tråkig bot helt utan features, får se om jag orkar fixa till det, ajja, här är den iallafall, i favoritspråket python :):

#! /usr/bin/env python # -*- coding: iso-8859-1 -*- import sys,os,re,socket host = 'irc.freenode.net' port = 6667 nick = '' chan = '' user = '' realname = '' socket = socket.socket() socket.connect((host, port)) socket.send("NICK %s\r\n" % nick) socket.send("USER %s %s bla :%s\r\n" % (user,host,realname)) socket.send("JOIN %s\r\n" % chan) while 1: line=socket.recv(500) print line

Det mesta säger sig självt, så jag utelämnar kommentarer.