IRC bot v PHP - 5. časť

Michal Pirchala  /  20. 12. 2010, 20:35

V tejto časti vytvoríme ďalší modul, ktorý bude slúžiť, ako som už spomenul minule, na logovanie (zapisovanie) správ do súboru.

V modules vytvoríme skript log s týmto obsahom:

if (preg_match("/^\:([^ ]*)\!([^ ]*) (.*)$/", $msg, $matches)){
    $sender = $matches[1];
    $host = $matches[2];
    $irc_prikaz = $matches[3];
     
    if (preg_match("/^JOIN \:(.*)$/", $irc_prikaz, $matches)){
        $handle = fopen('logs/'.$matches[1], 'a');
        fwrite($handle, date("H:i")." $sender ($host) vstúpil do {$matches[1]}\n");
        fclose($handle);
    }
     
    if (preg_match("/^PART (.*)$/", $irc_prikaz, $matches)){
        $handle = fopen('logs/'.$matches[1], 'a');
        fwrite($handle, date("H:i")." $sender opustil {$matches[1]}\n");
        fclose($handle);
    }
     
    if (preg_match("/^PRIVMSG ([^ ]*) \:(.*)$/", $irc_prikaz, $matches)){
        if ($matches[1][0]!="#") $matches[1] = $sender;
        $handle = fopen('logs/'.$matches[1], 'a');
        if (preg_match("/^ACTION (.*)$/", $matches[2], $matches_)) fwrite($handle, date("H:i")." * $sender {$matches_[1]}\n");
        else fwrite($handle, date("H:i")." $sender: {$matches[2]}\n");
        fclose($handle);
    }
}
?>

Každý prichádzajúci riadok dát od servera preveríme, či nieje v tvare príkazov, ktoré zapíšeme do logov. Do prvého subvýrazu v regulárnom výraze si uložíme nick odosielateľa, do druhého jeho host a do tretieho IRC príkaz. Nick a host nesmú obsahovať medzery. Ďalej budeme overovať premennú $irc_prikaz, či neobsahuje príkaz JOIN, PART alebo PRIVMSG. Ak áno, do logu zapíšeme príslušné údaje (pri príkaze JOIN záznam o vstupe do kanálu, pri príkaze PRIVMSG správu a pri príkaze PART záznam o odchode). Server nám ale nepošle správy, ktoré sme odoslali my. Preto musíme do funkcie sendmsg pridať príkaz, ktorý zapíše aj naše správy.

function sendmsg($channel, $msg){
    global $nick;
    $msg = explode("\n", $msg);
    foreach ($msg as $msg1){
        $msg1 = explode("\r", $msg1);
        foreach ($msg1 as $row){
            if (empty($row)) continue;
            Send('PRIVMSG '.$channel.' :'.$row);
            if ($channel[0]!="#") $channel = $sender;
            $handle = fopen('logs/'.$channel, 'a');
            fwrite($handle, date("H:i")." $nick: $row\n");
            fclose($handle);
            sleep(1);
        }
    }
}

Všetky informácie sa ukladajú do súboru „logs/MENO_KANALA“. Teda každý kanál ma osobitný súbor na ukladanie logov.

Možno ste si všimli, že tento modul nezapíše údaj o odchode užívateľom, ktorí sa odpoja pomocou príkazu QUIT. Pri príkaze QUIT nieje jasné, z ktorého kanálu za užívateľ odpojil. Preto je potrebné uchovávať si zoznam užívateľov v každom kanále v ktorom sa nachádzame. Vždy po vstupe do kanála do tohto zoznamu zapíšeme užívateľov, ktorí sa v ňom nachádzajú a samozrejme, po príchode alebo odchode nejakého užívateľa zoznam upravíme. Na začiatok modulu pridáme tieto riadky, ktoré nám vždy po prijatí zoznamu užívateľov zapíšu mená užívateľov, oddelených čiarkou, do premennej.

if (preg_match("/^\:.* 353 $nick \= (\#.*) \:(.*)$/", $msg, $matches)){
    $nick_list[$matches[1]] = "";
    $names = explode(" ", $matches[2]);
    foreach ($names as $name){
        if ($name[0]=="@" || $name[0]=="+") $nick_list[$matches[1]] .= (empty($nick_list[$matches[1]])) ? substr($name, 1) : ",".substr($name, 1);
        else $nick_list[$matches[1]] .= (empty($nick_list[$matches[1]])) ? $name : ",".$name;
    }
}

Ako sa dočítame v dokumentácii, správa obsahujúca nicky ma číslo 353 a jej syntax je:
:[[@|+] [[@|+] […]]]
Teda zoberieme všetko čo nasleduje za dvojbodkou, „rozbijeme“ to na časti oddeľujúce sa medzerou a odoberieme z každého nicku znak „@“ (značí že vlastník nicku je operátor kanála) alebo „+“ (používaný v kanáloch kde môžu rozprávať iba vybraní ľudia). Následne nicky uložíme do premennej „$nick_list[NÁZOV KANÁLA]“, oddelené čiarkou. Po každom príchode, odchode alebo zmene nicku požiadame server, nech nám pošle zoznam nickov opäť. Vyhneme sa tak zložitému upravovaniu zoznamu nickov uloženého vo vyššie spomínanej premennej.

Konečný modul


if (preg_match("/^\:.* 353 $nick . (\#.*) \:(.*)$/", $msg, $matches)){
    $nick_list[$matches[1]] = "";
    $names = explode(" ", $matches[2]);
    foreach ($names as $name){
        if ($name[0]=="@" || $name[0]=="+") $nick_list[$matches[1]] .= (empty($nick_list[$matches[1]])) ? substr($name, 1) : ",".substr($name, 1);
        else $nick_list[$matches[1]] .= (empty($nick_list[$matches[1]])) ? $name : ",".$name;
    }
}

if (preg_match("/^\:([^ ]*)\!([^ ]*) (.*)$/", $msg, $matches)){
    $sender = $matches[1];
    $host = $matches[2];
    $irc_prikaz = $matches[3];
     
    if (preg_match("/^JOIN \:(.*)$/", $irc_prikaz, $matches)){   //prichod do kanalu
        $handle = fopen('logs/'.$matches[1], 'a');
        fwrite($handle, date("H:i")." $sender ($host) vstúpil do {$matches[1]}\n");
        fclose($handle);
        Send("NAMES {$matches[1]}");
    }
     
    if (preg_match("/^PART (.*)$/", $irc_prikaz, $matches)){   //odchod z kanalu
        $handle = fopen('logs/'.$matches[1], 'a');
        fwrite($handle, date("H:i")." $sender opustil {$matches[1]}\n");
        fclose($handle);
        Send("NAMES {$matches[1]}");
    }
     
    if (preg_match("/^PRIVMSG ([^ ]*) \:(.*)$/", $irc_prikaz, $matches)){    //sprava
        if ($matches[1][0]!="#") $matches[1] = $sender;
        $handle = fopen('logs/'.$matches[1], 'a');
        if (preg_match("/^ACTION (.*)$/", $matches[2], $matches_)) fwrite($handle, date("H:i")." * $sender {$matches_[1]}\n");
        else fwrite($handle, date("H:i")." $sender: {$matches[2]}\n");
        fclose($handle);
    }

    if (preg_match("/^NICK \:(.*)$/", $irc_prikaz, $matches)){    //zmena nicku
        foreach ($nick_list as $kanal=>$nicky){
            $nicky = explode(",", $nicky);
            foreach ($nicky as $uzivatel){
                if ($uzivatel==$sender){
                    $handle = fopen('logs/'.$kanal, 'a');
                    fwrite($handle, date("H:i")." $sender si zmenil nick na {$matches[1]}\n");
                    fclose($handle);
                }
            }
            Send("NAMES $kanal");
        }
    }
     
    if (preg_match("/^QUIT \:(.*)$/", $irc_prikaz, $matches)){    //odpojenie od servera
        foreach ($nick_list as $kanal=>$nicky){
            $nicky = explode(",", $nicky);
            foreach ($nicky as $uzivatel){
                if ($uzivatel==$sender){
                    $handle = fopen('logs/'.$kanal, 'a');
                    fwrite($handle, date("H:i")." $sender opustil $kanal [{$matches[1]}]\n");
                    fclose($handle);
                }
            }
            Send("NAMES $kanal");
        }
    }
}

?>

Pridali sme logovanie zmien nickov a odpojení sa od servera (príkaz QUIT, nie po odchode z kanála - príkaz PART). Vždy, keď sa pravdepodobne zmení zoznam nickov v kanále (čo nastane vždy okrem poslania správy), si vyžiadame od servera nový zoznam nickov (príkazom NAMES), ktorý nám pri následnom cykle kód na začiatku modulu spracuje.

Záver


Doprogramovali sme modul na ukladanie logov do súboru. Pre niektoré účely je však ukladanie logov do databázy výhodnejšie. Tento spôsob si ukážeme nabudúce.

Neprehliadnite: