19 listopada 2009
Bot SMS pod Linuksem
Ostatnio w firmie pojawił się temat stworzenia SMS bota. Zacząłem więc szukać rozwiązań. Jeden z pomysłów, to postawienienie komputera z Linuksem na pokładzie, podłączenie do niego telefonu z ofertą, w której jest miliard darmowych SMS-ów i uruchomienie czegoś magicznego, co zajmie się odbieraniem i wysyłaniem wiadomości. I to magiczne coś właśnie zamierzam opisać.
Rozwiązanie które można zastosować, okazało się prostsze niż myślałem. Można użyć do tego świetnego programu, który jest dostępny zarówno pod Linuksem jak i Windowsem - mowa o Gammu. Jest to program, który komunikuje się z telefonem, lub modemem i służy do różnego rodzaju cznności, takich jak odbieranie/wysyłanie SMS-ów, zarządzanie kontaktami, zadaniami, kalenarzem itp. Ja oczywiście skupię się na odbieraniu i wysyłaniu SMS-ów.
Tak więc do komunikacji użytkownika z botem posłużę się Gammu - to już ustaliliśmy. Żeby było ciekawiej, Gammu posiada demona do wysyłania i odbierania SMS-ów - gammu-smsd. Jako backend do przechowywania danych można użyć zwykłych plików lub bazy danych (obsługiwany jest MySQL, PostgreSQL i SQLite). Pliki odradzam, bo to nieco ogranicza funkcjonalność - przykładowo nie jest możliwe wysyłanie wiadomości przy użyciu polecenia gammu-smsd-inject lub nie można przekazać żadnego id wiadomości do skryptu, który można uruchomić po otrzymaniu wiadomości (o tym w dalszej części). Lepiej zatem skorzystać z bazy danych. Ja wybrałem PostgreSQL.
Konfiguracja Gammu oraz SMSD
Najpierw trzeba zacząć od komunikacji Gammu z telefonem. Mój telefon to Sony Ericsson k800i. Pomijam fakt, że trzeba podłączyć telefon do komputera przez kabel USB ;) Poniżej zamieszczam źródła plików konfiguracyjnych:
~/.gammurc
[gammu] port = /dev/ttyACM0 model = connection = at19200 synchronizetime = no logfile = logformat = errorsdate use_locking = gammuloc =
/etc/gammu_smsdrc
[gammu] port = /dev/ttyACM0 model = connection = at19200 synchronizetime = no logfile = logformat = errorsdate use_locking = gammuloc = [smsd] Service = pgsql LogFile = syslog DebugLevel = 255 DeliveryReport = log RunOnReceive = /home/jaro/bin/sms_trigger.py User = uzytkownik_bazy Password = haslo_bazy PC = localhost Database = gammu
Poprawność konfiguracji połączenia z telefonem można sprawdzić przez polecenie gammu identify. U mnie wyjście wyglądało tak:
# gammu identify Manufacturer : Sony Ericsson Model : K800i (AAD-3022031-BV) Firmware : R8BF003 080130 2133 CXC1250212_ORANGE_WI IMEI : WOLE_NIE_POKAZYWAĆ Kod produktu : AAD-3022031-BV SIM IMSI : WOLE_NIE_POKAZYWAĆ
Przed uruchomieniem demona smsd, trzeba utworzyć odpowiednie tabele w bazie danych. W dokumentacji znajduje się przykładowy plik ze skryptem SQL, który tworzy te tabele. U mnie znajduje się on w lokalizacji: /usr/share/doc/gammu/examples/sql/pgsql.sql Wystarczy uruchomić skrypt i voila!
Skrypt (bot)
W pliku gammu_smsdrc jest opcja RunOnReceive, która, jak się można domyślić, wskazuje skrypt uruchamiany po odebraniu wiadomości. Skrypt jest uruchamiany z parametrem, który zawiera id wiersza w bazie danych w tabeli inbox. Wiersz zawiera dane przesłanej wiadomości. Napisałem w Pythonie przykładowy skrypt, który odczytuje odebraną wiadomość, sprawdza jaki tekst wysłano i na jego podstawie wysyła odpowiedź.
#!/usr/bin/python
import psycopg2 as db, sys, os
db_user = 'nazwa_uzytkownika'
db_password = 'haslo_do_bazy'
db_host = 'localhost'
db_name = 'gammu'
def main(argv=None):
try:
sms_id = sys.argv[1]
except IndexError:
print 'Za malo parametrow'
sys.exit()
try:
conn = db.connect("dbname=%s user=%s password=%s host=%s" % (db_name, db_user, db_password, db_host))
except:
print 'Blad podczas polaczenia z baza'
if not sms_id.isdigit():
print 'Niepoprawny parametr'
sys.exit()
cur = conn.cursor()
cur.execute("SELECT * FROM inbox WHERE id=%s" % sms_id)
row = cur.fetchone()
if not row:
print 'Nie znaleziono wiadomosci sms o podanym id'
sys.exit()
rcp_num = row[3]
command = row[8].strip().lower()
if command == 'jezyk':
response = "Python"
elif command == 'system':
response = "Linux"
else:
response = "Nieznana komenda"
os.system("gammu-smsd-inject TEXT %s -text '%s'" % (rcp_num, response))
if __name__ == '__main__':
main()
Finał - uruchomienie demona
Wreszcie przechodzimy do puenty - uruchomienia demona. Wystarczy krótkie:
gammu-smsd --daemon
I w ten oto sposób, telefon jest gotowy do odbierania i wysyłania wiadomości.
7 komentarzy
Jeśli chcesz robić coś sensowniejszego niż prowizorki to zapoznaj się z http://www.kannel.org/
Nieźle, tylko napisz jeszcze która sieć i w jakiej taryfie ma dość dużą, darmową ilość smsów :)
Problem w tym, że taki telefon potrafi wysłać/odebrać koło 20 SMS-ów na minutę. To troszkę mało, żeby zastosować to do czegoś innego niż zabawa. Wysyłka 10000 SMS-ów taką metodą będzie trwała 8h. Na dodatek ten "miliard darmowych SMS-ów" jest najczęściej tylko do jednej sieci.
Polecam play. Doładowujesz za 5 zł masz 500 smsów w ciągu 5 dni do wszystkich sieci. Taniej nie znajdziesz i nie do wszystkich sieci...
Play już ukróciło swoją ofertę. Jak doładowuje za 50 zł to dostaje 2000 SMSów do wszystkich (wcześniej było 5k), niemniej jednak wciąż to pozostaje najlepsza opcja, ew. dogadanie się z jamiś operatorem.
Tego typu rozwiązania przydają się do alarmowania nas o różnych zdarzeniach w systemie: http://thecamels.org/2009/09/13/powiadomienia-smsowe-o-statusie-serwera/
@Grzegorz: Tak to prawda, po prostu lepiłem z tego co miałem. Zamiast telefonu lepiej użyć modemu, z którym Gammu też się komunikuje i tym samym przepustowość się zwiększa.
Nie napisałem jeszcze jednej istotnej rzeczy, a mianowicie grupa odbiorców komunikacji przez SMS-a, jest stosunkowo niewielka. Spodziewana liczba wysłanych wiadomości w ciągu miesiąca, to może około 100.
Po przemyśleniu sprawy doszedłem do wniosku, że lepiej oprzeć to o gotowe i bardziej stabilne rozwiązania oferowane przez firmy wyspecjalizowane w temacie. Oprócz kombinowania i dogadywania się z operatorem na korzystną liczbę SMS-ów do wszystkich sieci, pojawiają się niuanse związane z energią elektryczną, a właściwie z jej tymczasowym zanikiem, czy ze stabilnością połączenia z internetem.