Как стать автором
Обновить

Отправка SMS кириллицей с AT-модема

Время на прочтение3 мин
Количество просмотров6.6K

Оперативное информирование клиентов, когда их достаточно много для ручного обзвона, но недостаточно много для подключения массового сервиса, вроде sms.ru (на самом деле сервис хорош, но недавняя политика некоторых мобильных операторов создала определённые финансовые сложности ввиду заградительных тарифов на использование услуг sms-рассылок с/без использования имён) родило потребность в применении независимого инструмента.

Путем продолжительных скитаний по просторам сети, был изучен ряд материалов и некоторые готовые решения. Спасибо dos999 (Ссылка на пост) за отправную точку, но хотелось бы реализовать это "модном" на python3.

Была предпринята попытка адаптировать изложенную логику на основе полученных знаний, но уперся в кириллическую кодировку, т.к. готового кодера в UCS-2 python не имеет, а варианты на основе utf-16 приводили к какому-то такому результату "PÉQ@P>Q£Q$P>P9".

Но, кто ищет - то находит. Попалась мне готовая реализация на python2 от huh-muh (Ссылка на пост). Адаптировать под python3 труда не составило:

import serial
import time
import random
# процедура для отправки строки в модем и получения ответа
def str_send (ser, textline):
    print("<<" + textline)
    ser.write(bytes(textline, "utf-8"))
    out = ''
    N = 10
    while N > 0:
        time.sleep(1)
        while ser.inWaiting() > 0:
            out += str(ser.read(1))
        if ('OK' in out) or ('ERROR' in out) or ('>' in out):
            print(">>" + out)
            N = 1
        N -= 1

# функция преобразования телефонного номера в формат, пригодный для SMS
def PhoneNumberToSMS(number):
    number += 'F'
    result = '0B' + '91'
    i = 0
    while i < len(number):
        result += number[i+1] + number[i]
        i += 2
    return result

# функция, кодирующая юникодную строку в формат SMS
def TextToSMS(text):
    b = text
    result = ''
    i = 0
    while i < len(b):
        o = ord(b[i])
        result += ("%0.2X" % (o//256)) + ("%0.2X" % (o%256))
        i += 1
    return result

# вводим с консоли сообщение и переводим его в юникод

message = input('Текст сообщения:\n')
# message = message.decode('utf-8')

# если сообщение большое - режем его на кусочки для механизма конкатенации SMS
chunks = []

if len(message) > 70:
    while len(message) > 66:
        chunks.append(message[:66])
        message = message[66:]
if len(message) > 0:
    chunks.append(message)

# готовим номер группы сообщений и устанавливаем 6-й бит SMS_SUBMIT_PDU
SMS_SUBMIT_PDU = "11"
CSMS_reference_number = ""
if len(chunks) > 1:
    SMS_SUBMIT_PDU = "51"
    CSMS_reference_number = "%0.4X" % random.randrange(1,65536)

# связываемся с модемом
ser = serial.Serial("COM4", 9600, timeout=1)

# устанавливаем нужный формат передачи данных
str_send(ser, 'AT+CMGF=0\r')

# передаем кусочки сообщения
i = 1
for chunk in chunks:
    emessage = TextToSMS(chunk)
    if CSMS_reference_number != "":
        emessage = "06" + "08" + "04" + CSMS_reference_number + \
        ("%0.2X" % len(chunks)) + ("%0.2X" % i) + emessage
    sms = "00" #Накидываем тело сообщения в формате PDU
    sms += SMS_SUBMIT_PDU
    sms += "00"
    sms += PhoneNumberToSMS("7ХХХХХХХХХХ")
    sms += "00"
    sms += "08"
    sms += "AA"
    sms += "%0.2X" % (len(emessage)//2)
    sms += emessage
    str_send(ser, 'AT+CMGS=' + str(len(sms)//2-1) + '\r')
    str_send(ser, sms + '\x1A')
    i += 1

# отвязываемся от модема
ser.close()

В целом, код готов. Теперь можно реализовать массовую рассылку, скармливая скрипту файл с телом сообщения и списком адресатов в формате 79876543210, но это уже другая история.

UPD: Список для рассылки реализовал простым txt-файлом, где построчно:

  • Первая строка - сам текст сообщения,

  • Последующие строки - номера телефонов в 10-значном формате (без кода страны), т.к. рассылка планируется в пределах РФ.

Сам код для перебора номеров выглядит следующим образом:

def readlist(filename): 
  #Подключаемся к файлу с содержимым рассылки
  file = open(filename, 'r', encoding="utf-8")
  filelist = []
  for line in file:
      filelist.append(line.strip())
      #готовим SMS сообщение
  for i in range(1, len(filelist)):
      time.sleep(1)
      print('Отправка:', filelist[0], '7' + filelist[i])
			#принтую для удобства, что скрипт не подпис и всё идёт по-плану
      smsaddr = '7' + filelist[i]
      message = filelist[0]
	# и далее описанные ранее команды

При большом желании можно различные константы (вроде номер com-порта или кода страны) вывести во входные параметры, для большей гибкости, но это уже на вкус и цвет.

Теги:
Хабы:
Всего голосов 12: ↑10 и ↓2+8
Комментарии10

Публикации

Истории

Работа

Data Scientist
59 вакансий
Python разработчик
122 вакансии

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург