Popularne SMSy przesyłane mogą być w dwóch formatach: tekstowym lub PDU (ang. Protocol Data Unit). Z oczywistych przyczyn pierwszy wariant jest używany rzadziej. Możliwości, jakie mamy zaś w PDU są następujące.
- 7 bity na znak - rozwiązanie pozwalające na uzyskanie max. 160 znaków na SMS,
- 8 bity na znak - opcja pośrednia, 140 znaków,
- 16 bitów na znak - czyli dla bogatych (70 znaków/SMS).
Powyższe skomplikowane obliczenia uwzględniają nagłówek PDU, więc proszę mnie nie posądzać o nieumiejętność dzielenia :-). Format wspomnianego nagłowka jest całkiem nieźle opisany w tym miejscu.
Koniec części teoretycznej. Odpalamy swój najlepszy edytor tekstowy (vim), zaprzągamy ulubiony język (python), i ... dewelopujemy na wesoło.
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 def int2hex (n): 5 s = '%X'%n 6 if int(len(s))%2 == 1: 7 s = '0'+s 8 return s 9 10 def encode_message (message): 11 pdu = "" 12 l = len (message) 13 message += '\0' 14 15 # Wyznaczamy rozmiar tekstu po upakowaniu. 16 msglen = int (len(message)*7/8) 17 18 # Inicjujemy bufor i zmienne. 19 buf = [0] * msglen 20 c = shift = 0 21 22 # Dla każdego septetu... 23 for n in range(msglen): 24 # Przeskok po każdej paczce 7 bajtów. 25 if shift == 6: 26 c += 1 27 28 # Wyznaczamy oktet z dwóch septetów. 29 shift = n % 7 30 lb = ord(message[c]) >> shift 31 hb = (ord(message[c+1]) << (7-shift) & 255) 32 buf[n] = lb+hb 33 34 c += 1 35 36 # Łączymy wiadomość z jej rozmiarem w postać heksadecymalną. 37 pdu = chr(l) + ''.join(map(chr, buf)) 38 return ''.join(["%02x" % ord(n) for n in pdu]) 39 40 def encode_SMS (dest_number, text): 41 # Zaczynamy od szablonu SMSa. 42 smspdu = "001100" 43 44 # Dodajemy długość numeru i jego format. 45 smspdu += int2hex(len(dest_number)) + '81' 46 47 # Dodajemy numer nadawcy. 48 tmp_number = dest_number 49 if len(tmp_number) % 2 == 1: 50 tmp_number += 'F' 51 i = 0 52 while i < len(tmp_number): 53 smspdu = smspdu + tmp_number[i+1] + tmp_number[i] 54 i += 2 55 56 # Dodajemy id protokołu, pole DCS i okres ważności. 57 smspdu += '0000FF' 58 59 # Dodajemy dł. wiadomości no i samą wiadomość :-). 60 smspdu += int2hex(len(text)) 61 smspdu += encode_message(text) 62 63 return smspdu
Zaletą pythona jest to, że kod w nim napisany jest prawie samo-dokumentujący się. Jeżeli deklarujesz się jako profesjonalny programista, ale jednak nie rozumiesz co się dzieje powyżej, to (nie chce mi się wymyślać żadnych epitetów, po prostu zacytuję pewnego pana):
to prawdopodobnie masz jeszcze szanse na jakąś karierę, nie wiem, w polityce chyba wiele nie trzeba, musisz tylko dobrze wyglądać w krawacie
Powyższy kod całkiem dobrze się sprawuje pod warunkiem, że wszystko uda się upakować w jednej wiadomości. W innych wypadkach mogą dziać się różne rzeczy, od bełkotu wyświetlającego się na twojej komórce do niczego - SMS nie dochodzi w ogóle i nikt nie wie dlaczego (sytuacje rodem z politechniki), bramka twierdzi, że wiadomość wysłała, a komórka milczy. Wracając jeszcze raz do dokumentacji szybko znajdujemy informacje jako by przy długich wiadomościach istotną rolę odgrywał tzw. nagłówekUDH. Dodajmy więc małą modyfikację:
1 message = '\x00\x00\x00\x00\x00\x00' + message ... ? for i, ch in enumerate (udh): ? buf[i] = ord(ch)
Pierwszą linię dodajemy dokładnie na początku funkcji encode_SMS, a pętlę zaraz po upakowaniu wiadomości (czyli zaraz za pierwszą pętlą we wcześniej opisanej funkcji encode_SMS.
Na zakończenie powiem coś o sprzęcie. Można zakupić takie urządzenie które nazywa się chyba modemem GPRS. To taki mały pierdolnik podpinany do komputera przez USB i udostępniający mu internet. Nie jestem pewien na 100%, lecz bardzo prawdopodobne, że do tego modemu można podpiąć się za pomocą np. telnetu i wysyłać komendyHayes'a. Ja, szczęśliwie, miałem możliwość obcowania z pełnoprawną bramką GSM, która mieściła w sobie do 30 kart SIM pozwalając nieźle naspamować ]:->. Dziękuję za dotrwanie do końca.