     ___  ___    ______    _______    __    __   __    ___    ___
    |   \/   |  /  __  \  |   __  \  |  |  |  | |  |__ \  \  /  /
    |  \  /  | |  |  |  | |  |  \  | |  |  |  | |  ' /  \  \/  /
    |  |\/|  | |  |  |  | |  |   | | |  |  |  | |   /    \    /
    |  |  |  | |  |__|  | |  |__/  | |  |__|  | |  |____  |  |
    |__|  |__|  \______/  |_______/   \______/  |_______| |__|
    Poradnik v2.1                           Tworzenie i edycja

### Spis treści

### Rejestracja modułu - interfejs BotModuleInit

Należy w katalogu ./modules/ utworzyć folder o dowolnej nazwie, na przykład:

    99_NAZWAMODULU

Zaleca się, by nazwa folderu była postaci 00_NAZWAMODULU, gdzie 00 to cyfry umożliwiające ustawienie kolejności modułów. Jest to przydatne przy wyświetlaniu pomocy lub listy funkcji.

Folder ten powinien zawierać plik o nazwie init.php, zawierający klasę o dowolnej nazwie, implementującą BotModuleInit, oraz co najmniej dwie metody - register i help. Na końcu pliku powinna znajdować się konstrukcja return, zwracająca nazwę klasy:

    ./modules/99_NAZWAMODULU/init.php<?php
    class bot_NAZWAMODULU_init implements BotModuleInit
    {
       function register() {
       }

       function help($params = NULL) {
       }
    }

    return 'bot_NAZWAMODULU_init';
    ?>

By nie powodować konfliktów nazw, zaleca się, by nazwa klasy była formatu bot_NAZWAMODULU_init, jednak nie jest to wymagane.

Błąd składniowy w pliku init.php może powodować błędy w działaniu całego bota!

Informacje zwracane przez plik inicjujący są cache'owane. Po każdej zmianie pliku init.php należy koniecznie usunąć zawartość folderu cache!

#### Metoda register()

Metoda `register()` ma zwracać tablicę następującej postaci:

    array(
       'komenda' => array(
          array(
             'file' => 'komenda.php',
             'class' => 'bot_NAZWAMODULU_module',
             'method' => 'komenda1',
             'params' => 'parametr_do_funkcji',
          ),
          array(
             'file' => 'komenda.php',
             'class' => 'bot_NAZWAMODULU_module',
             'method' => 'komenda2',
          ),
       ),
       '*' => array(
          array(
             'file' => 'test.php',
             'class' => 'NAZWAMODULU_test',
             'method' => 'komenda_test',
          ),
       ),
    )

`'komenda'` to nazwa obsługiwanej przez moduł komendy. `'*'` oznacza, że obsługiwana może być każda wiadomość od użytkownika. `'file'` zawiera nazwę pliku, w którym znajduje się klasa z parametru `'class'`. `'method'` to metoda klasy, która przetwarza daną komendę. Opcjonalny parametr `'params'` zawiera dowolny obiekt (np. string, array), na którym można wykonać funkcję `serialize()`, a który zostanie przekazany do wywoływanej metody. Więcej informacji o sposobie przetwarzania wiadomości od użytkownika znajduje się w kolejnym dziale: Komunikacja z użytkownikiem - interfejs BotModule

`'komenda'` powinna zawierać jedynie małe litery alfabetu angielskiego. Jeżeli zawiera polskie ogonki lub wielkie litery, zostaną one automatycznie zastąpione odpowiednikami. Jeśli równocześnie istnieją indeksy zawierające np. `'ą'` oraz `'a'`, mogą one zostać połączone w dowolnej kolejności.

Przykład - rejestrowanie komendy przyklad:

    ./modules/99_przyklad/init.php<?php
    class bot_przyklad_init implements BotModuleInit
    {
       function register() {
          return array(
             'przyklad' => array(
                array(
                   'file' => 'przyklad.php',
                   'class' => 'bot_przyklad_module',
                   'method' => 'komenda_przyklad',
                   'params' => 'parametr_do_funkcji',
                )
             )
          );
       }

       /* Pominięto tu wymaganą metodę help().
          Jej implementację można znaleźć poniżej. */
    }

    return 'bot_przyklad_init';
    ?>

W dalszej części zakładamy, że tworzymy moduł obsługujący komendę przyklad.

#### Metoda help()

Metoda `help()` przyjmuje argument `$params` - nazwę komendy, dla której ma zwrócić, za pośrednictwem klasy BotMsg, pomoc. Jeśli argument ten jest identyczny z `NULL`, należy zwrócić skróconą listę poleceń z krótkim wyjaśnieniem, zakończoną dwoma znakami nowej linii.

Jeżeli moduł nie ma pomocy lub nie obsługuje danej komendy (szczególnie dla pseudokomendy `'*'`), powinna zostać zwrócona wartość `FALSE`

Jeżeli żaden moduł nie zwrócił wiadomości z pomocą, użytkownik jest informowany o braku pomocy dla danej komendy.

Skrócona lista poleceń jest generowana tylko z tych plików init.php, które rejestrują przynajmniej jedną komendę.

Przykład - funkcja `help()`:

      function help($params = NULL) {
          if($params === NULL) {
             return new BotMsg('<b>przyklad</b> <i>[argument]</i><br />'."\n".
                '   Zwraca przykładowy tekst oraz treść argumentu<br /><br />');
          }
          else
          {
             return new BotMsg('<b>przyklad</b> <i>[argument]</i><br />'."\n".
                '   Komenda zwraca przykładowy tekst oraz treść argumentu <i>[argument]</i> (jeśli istnieje)');
          }
       }

#### Przykład

    ./modules/99_przyklad/init.php<?php
    class bot_przyklad_init implements BotModuleInit
    {
       function register() {
          return array(
             'przyklad' => array(
                array(
                   'file' => 'przyklad.php',
                   'class' => 'bot_przyklad_module',
                   'method' => 'komenda_przyklad',
                   'params' => 'parametr_do_funkcji',
                )
             )
          );
       }

       function help($params = NULL) {
          if($params === NULL) {
             return new BotMsg('przyklad <i>[argument]</i><br />'."\n".
                '   Zwraca przykładowy test oraz treść argumentu<br /><br />');
          }
          else
          {
             return new BotMsg('przyklad <i>[argument]</i><br />'."\n".
                '   Komenda zwraca przykładowy test oraz treść argumentu <i>[argument]</i> (jeśli istnieje)');
          }
       }
    }

    return 'bot_przyklad_init';
    ?>

#### Dalsze informacje

Następne sekcje zawierają informacje dotyczące klas i metod, które umożliwiają przetwarzanie wiadomości od użytkownika i wysyłanie komunikatu zwrotnego.

Jeśli moduł posiada dane, które musi przechowywać (np. słownik, tabelę kursów itp.), należy zapoznać się z sekcją Przechowywanie danych i ich aktualizacja

### Informacje o użytkowniku i treść wiadomości - klasa BotMessage

Wszystkie informacje związane z użytkownikiem są przekazywane jako pierwszy parametr do metody zarejestrowanej komendy pod postacią klasy potomnej BotMessage. Zawiera ona nastepujące pola:

#### Pole $user

Zawiera wszystkie informacje o źródle i celu wiadomości - w postaci klasy BotUser. Zakładając, że `$msg` to nazwa zmiennej zawierającej klasę BotMessage, można uzyskać następujące informacje:

    $msg->user->interface

Zawiera tekst: `'Gadu-Gadu'`, `'IMified'`, `'HTTP'` lub inny, który określa źródło wiadomości

    $msg->user->uid

Zawiera numer użytkownika lub jego identyfikator (screen name).

    $msg->user->network

Jeden z ciągów: `'gadu-gadu.pl'`, `'jabber.imified.com'`, `'aim.imified.com'`, `'msn.imified.com'`, `'yahoo.imified.com'`, `'gtalk.imified.com'`, `'sms.imified.com'` lub inny, identyfikujący sieć użytkownika.

    $msg->user->bot

Informacja o bocie, do którego została wysłana wiadomość. Numer w przypadku Gadu-Gadu lub botkey w przypadku IMified.

    $msg->user->params

Inne parametry. W przypadku IMified zawiera ciąg `'public'` lub `'private'`, pozwalający odróżnić źródło wiadomości na twitterze.

#### Pole $session

Odpowiednik klasy database z poprzedniej wersji bota. Aktualnie instancja klasy BotSession, umożliająca przechowywanie danych przypisanych do użytkownika, m.in. miasta, nazwy kina i tym podobnych.

Przed użyciem sesji należy ustawić nazwę klasy, dla której zmienne będą przetwarzane. Jest to odpowiednik drugiego argumentu przekazywanego do metody database::get() z pierwszych wersji bota.

Przykład użycia:

    // Ustawienie nazwy modułu. WYMAGANE!
    $msg->session->setClass('przyklad');

    // Ustawienie pojedynczej wartości
    $msg->session->zmienna = 'To jest test';
    assert($msg->session->zmienna === 'To jest test');

    // Usunięcie pojedynczej wartości
    unset($msg->session->zmienna);
    assert($msg->session->zmienna === NULL);

    // Usunięcie wszystkich danych
    $msg->session->truncate();
    assert($msg->session->zmienna === NULL);

    // Dopisanie (nadpisanie) danych
    $tablica = array(
       'zmienna' => 'To jest test',
       'zmienna2' => new DateTime()
    );
    $msg->session->push($tablica);

    assert($msg->session->zmienna === 'To jest test');
    assert($msg->session->pull() === $tablica);

    // push() nie usuwa istniejących danych
    $msg->session->zmienna3 = 'To jest test';
    $msg->session->push($tablica);
    assert($msg->session->pull() !== $tablica);

    // Usunięcie wszystkich danych
    $msg->session->truncate();
    assert($msg->session->zmienna === NULL);

#### Pole $rawText

Zawiera dane, które zostały otrzymane od API, bez żadnych zmian, z zachowanym oryginalnym formatowaniem.

    $msg->rawText

#### Pole $text

Zawiera dane, które zostały otrzymane od API, wielkie litery zamienione na małe, wszystkie znaki alfabetu innego niż angielski transliterowane. Zobacz: `funcs::utfToAscii()`

    $msg->text

#### Pole $command

Nazwa komendy (pierwszy wyraz z pola $text).

    $msg->command

#### Pole $args

Parametry przekazane do komendy (pozostałe wyrazy z pola $rawText).

    $msg->args

#### Pole $images

Tablica obrazków (obiektów klasy BotImage) przesłanych do bota przez użytkownika.

    if(count($msg->images) > 0) {
        // Do bota zostały przesłane obrazki

        // Pierwszy obrazek
        $img = $msg->images[0];

        // Obrazek w formie gotowej do obróbki za pomocą biblioteki GD
        $gd = $img->getImage();
        imagestring($gd, 2, 0, 0, 'To jest tekst', imagecolorallocate($gd, 255, 255, 255));

        // Obrazek jako ciąg bajtów gotowych do zapisu:
        $data = $img->getImageData();
        file_put_contents('/tmp/obrazek.jpg', $data);
    }

### Komunikacja z użytkownikiem - interfejs BotModule

Zgodnie z danymi zwracanymi przez metodę register, należy utworzyć plik zawierający klasę o podanej nazwie, implementującą BotModule, oraz odpowiednią metodę, przyjmującą dwa parametry:

    <?php
    class bot_przyklad_module implements BotModule
    {
       function komenda_przyklad($msg, $param = NULL) {
       }
    }

Po wysłaniu przez użytkownika do bota polecenia, system wywoła w kolejności moduły, które zarejestrowały otrzymaną komendę (pierwszy wyraz w wiadomości), a następnie spróbuje wykonać moduły, które zarejestrowały się jako właściwe dla wszytkich komend (indeksem tablicy zwróconej w metodzie register była `'*'`). Do metod zostaną przekazane dwa parametry, opisane w powyższym przykładzie jako `$msg` i `$param`. Pierwszy z nich to instancja klasa BotMessage, a drugi - wartość z indeksu `'params'` podawanego podczas rejestracji komendy.

Wywołana metoda powinna zwrócić instancję klasy BotMsg lub `FALSE`, jeżeli nie chce obsłużyć otrzymanej wiadomości. W drugim przypadku obsługa zostanie przekazana kolejnej metodzie/modułowi, który zarejestrował daną komendę, następnie do plików obsługujących komendę "*", a na końcu użytkownikowi zostanie wysłana informacja o nieobsługiwanym poleceniu.

Jeśli moduł ma przechowywać dane dotyczące użytkownika, należy zapoznać się z sekcją Klasa BotMessage - pole $session

    ./modules/99_przyklad/przyklad.php<?php
    class bot_przyklad_module implements BotModule
    {
       function komenda_przyklad($msg, $param = NULL) {
          $reply = new BotMsg('<p>Przykładowy moduł</p>');
          $reply->append('<p>Twój numer/identyfikator: ' . $msg->user->uid  . '<br />'
             . 'Otrzymane parametry: ' . $msg->args  . '</p>');
          return $reply;
       }
    }
    ?>

### Tworzenie wiadomości zwrotnej - klasa BotMsg

Aby utworzyć wiadomość dla użytkownika, należy stworzyć instację klasy BotMsg, która jako opcjonalny parametr przyjmuje kod HTML, który ma zostać wysłany użytkownikowi:

    $reply = new BotMsg();
    // lub
    $reply = new BotMsg('<p>Akapit z tekstem <b>pogrubionym</b> i <i>pochylonym</i></p>');

W wiadomości przekonwertowanej na tekst zachowywane są białe znaki, za wyjątkiem znaków nowej linii!

#### Metoda append()

W razie konieczności dodania większej ilości treści, należy wywołać metodę `append()` lub `a()` z parametrem będącym kodem HTML:

    $reply = new BotMsg();

    $reply->a('<ol>
    <li>pierwszy element listy</li>
    <li>drugi element listy</li>
    </ol>');

    $reply->append('<p>Kolejny akapit</p>');

Zaleca się, by dodawany kod nie był "urwany", tzn. wszystkie tagi winny być poprawnie otwarte i zamknięte we wstawianym HTML-u. W przyszłości takie błędy mogą powodować odrzucenie dodawanej treści.

#### Dodawanie obrazków

Aby dodać obrazek należy do kodu HTML dodać tag img, z atrybutem src zawierającym ścieżkę do obrazka względem głównego katalogu bota lub bezwzględną ścieżkę dostępu do niego:

    $reply->a('<p>Tu będzie obrazek: <img src="data/przyklad/obrazek.png" /></p>');

Gadu-Gadu nie obsługuje więcej niż jednego obrazka w wiadomości.

#### Wspierane tagi HTML

Wspierane tagi HTML:

- a (tylko atrybut href)
- b
- br
- h1
- h2
- h3
- i
- img (tylko atrybut src)
- li
- ol (także atrybut start)
- p
- span
- strong
- sub
- sup
- u
- ul

Wspierane atrybuty:

- color (tylko w formacie: #ABC lub #AABBCC)
- style

### Funkcje pomocnicze

Poniższe klasy zawierają funkcje często używane w modułach.

#### calendar::parse_date()

Metoda `parse_date` klasy `calendar` jest zdefiniowana następująco:

    static function parse_date($date);

Przetwarza ona tekst `$date` (np. `'wczoraj'`, `'jutro'`, ale także `'1 stycznia 2000'`) na uniksowy znacznik czasu, który zwraca.

Przykład:

    $data = calendar::parse_date('29 stycznia 2009');
    $wzor = mktime(0, 0, 0, 1, 29, 2009);
    assert($data == $wzor);

#### funcs::utfToAscii()

Metoda `utfToAscii` klasy `funcs` jest zdefiniowana następująco:

    static function utfToAscii($utf);

Polskie znaki w zmiennej `$utf` zamienia w litery alfabetu angielskiego (ó => o, ź => z itd.), wykonuje na tekście `strtolower()` oraz `trim()`, po czym zwraca tak zmodyfikowaną treść.

### Przechowywanie danych i ich aktualizacja

Jeśli istnieje potrzeba okresowego aktualizowania danych (np. kursów walut) można użyć do tego przygotowanego specjalnie dla tego bota narzędzia.

W folderze /data/(nazwa modułu)/ należy utworzyć plik crontab, zgodny ze składnią crontaba.

Przykład pliku crontab:

    # To jest komentarz - znak # musi być pierwszym znakiem w linijce!

    # Uruchamiaj się codziennie, co dwie godziny, między północą a dwudziestą.
    0 0-20/2 * *   *  co_dwie_godziny.php
    # Uruchamiaj się o 4:00 w każdą niedzielę
    0      4 * *   0  niedziela.php
    # Uruchamiaj się w każdy poniedziałek i środę o 10:00
    0     10 * * 1,3  pon_sr_10.php

Pierwsze pole (minuty) jest pomijane, tj. nie można uruchamiać programu częsciej niż raz na godzinę!

W pole 6 należy, zamiast pełnej komendy, wpisać jednynie nazwę pliku z rozszerzeniem .php, znajdującego się w katalogu z plikiem crontab (lub względną ścieżkę do niego).