Понадобился нам как-то смс-чат для небольшой группы пользователей. Основными требованиями были надежность и простота реализации. В наличии был средненький офисный компьютер с Windows ХР на борту, USB-модем Huawei E1550, сим-карта с положительным балансом и среднестатистический эникейщик.
Хотелось нам следующего: инженеры из числа оперативного персонала, заступая на дежурство, подключаются к группе чата и могут обмениваться между собой короткими текстовыми сообщениями. Это полезно при решении проблем, касающихся нескольких отделов (релейщиков и энергетиков, например). Когда присутствие в группе не требуется – можно выйти и сообщения не получать.
Теоретически все было просто. Берем GSM-модем, пишем программу, которая проверяет на нем входящие сообщения, выдергивает из них нужную информацию, производит обработку и передает по списку подключенных абонентов.
На практике все оказалось иначе. Активное гугление привело нас на несколько проектов, в том числе и на Хабр. Но все оказались довольно сложными. Ибо Delphi из нас никто особо не понимает, а проекты для Линукса мы не рассматривали в принципе – у нас все машины виндовые.
Последней находкой стала программа от питерской компании Headwind Solutions. Называется она «Персональный СМС Сервер». Программа платная, но есть 30 дней демо-режима. Мы начали ковырять ее.
Программа оказалась очень удобной и надежной. Основная фишка ее в том, что она избавляет от необходимости работать с модемом напрямую. Она сама работает с GSM-модемом, проверяет входящие сообщения и отправляет исходящие. Если поступает новое вызывает скрипт и передает ему два параметра: номер отправителя и текст сообщения. Все остальное решается скриптом. Уже из скрипта можно вызывать процедуру отправки сообщения в программу. Сообщений может быть много, программа сама поставит их в очередь на отправку и грамотно передаст. Скрипт написан на VBS.
Программа, кстати, может работать как служба Windows. Мы сначала проигнорировали эту возможность и совершенно напрасно служба работает стабильнее.
На самой программе подробно останавливаться не будем. Она имеет много настраиваемых параметров, несложна и хорошо описана в документации на сайте разработчика.
Подробнее остановимся на скрипте чата. Помимо самого скрипта, в том же каталоге понадобится создать три файла: hlr.txt, vlr.txt и vlr_back.txt.
Первый файл заполняем номерами абонентов, имеющих доступ к чату. Формат такой: мобильный номер без плюса <пробел> идентификатор абонента.
79111234567 Иванов (энергетик)
79111234568 Петров (WDM)
79111234569 Сидоров (RRL)
Второй и третий файлы оставляем пустыми. Во втором хранятся записи о вошедших в группу абонентах, а третий файл используется скриптом для добавления/удаления абонента из группы.
Скрипт поддерживает три команды. Начинаются они со знака # (решетка). Чтобы войти в группу чата нужно отправить смс на номер сим-карты модема с текстом #1, чтобы выйти #0. Для запроса текущего списка пользователей нужно отправить #?
Запросы от абонентов, не вошедших в группу, игнорируются.
Входящие сообщения обрабатываются следующим образом. В начало исходного сообщения ставится идентификатор абонента (у нас это фамилия сотрудника и его отдел), а затем это сообщение передается всем абонентам из файла vlr.txt. Отправитель получает квитанцию об отправке сообщения. В ней дублируется его сообщение и количество абонентов, которому оно отправлено. Передача квитанции была добавлена в качестве контрольной меры. Если квитанция получена, значит, чат исправен, деньги на сим-карте есть и всем участникам чата сообщение улетело.
Привожу наши настройки программы. Параметры подбирались несколько лет методом проб и ошибок:
И собственно скрипт:
' Получаем значения переменных, которые передаются скрипту при его вызове
Number = WScript.Arguments(0)
Message = WScript.Arguments(1)
Message = Trim(Message)
' Обнуляем флаги, устанавливаем значение переменных
FlagVlr = 0
FlagHlr = 0
FlagSymbol=0
Users = ""
Count = 0
' Устанавливаем соответствие между переменными и именами файлов
vlr = "vlr.txt"
hlr = "hlr.txt"
vlr_back = "vlr_back.txt"
' Ищем номер абонента в списке HLR
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set filehlr = objFSO.OpenTextFile(hlr, 1)
Do Until filehlr.AtEndOfStream
sLine = filehlr.ReadLine()
nSpace = InStr(sLine, " ")
If nSpace > 0 Then
Number_hlr = Left(sLine, nSpace - 1)
Name_hlr = Trim(Right(sLine, Len(sLine) - nSpace))
End If
If (Number_hlr = Number) Then
FlagHlr = 1
Name = Name_hlr
End If
If (Len(Users) = 2) Then
Users = ""
End If
Loop
filehlr.Close
' Ищем номер абонента в списке VLR
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set filevlr = objFSO.OpenTextFile(vlr, 1)
Do Until filevlr.AtEndOfStream
sLine = filevlr.ReadLine()
nSpace = InStr(sLine, " ")
If nSpace > 0 Then
Users = Users & ", "
Number_vlr = Left(sLine, nSpace - 1)
Name_vlr = Trim(Right(sLine, Len(sLine) - nSpace))
End If
Count = Count + 1
If (Number_vlr = Number) Then
FlagVlr = 1
Name = Name_vlr
End If
If (Len(Users) = 2) Then
Users = ""
End If
Users = Users & Name_vlr
Loop
filevlr.Close
' Устанавливаем флаги в зависимости от наличия записи номера в файлах HLR и VLR
If (FlagHlr = 0) Then
Flaguser = 0
End If
If (FlagHlr = 1) And (FlagVlr = 0) Then
Flaguser = 1
End If
If (FlagHlr = 1) And (FlagVlr = 1) Then
Flaguser = 2
End If
' Устанавливаем значение текстовых сообщений
MessageAlreadyEnter = "Вы уже вошли в чат. Сейчас в группе: " & Users & "."
MessageNowInGroup = "Сейчас в группе: " & Users & "."
MessageEmpty = "Вы отправили пустое сообщение."
' Устанавливаем значение текстовых сообщений при пустой группе
If (Count = 0) Then
MessageNowInGroup = "В группе нет собеседников."
End If
If (Count = 1) Then
MessageAlreadyEnter = "Вы уже вошли в чат. В группе нет собеседников."
End If
' Обработка специальных сообщений
dlina = Len(Message)
symbol = Trim(Message)
symbol = Left(symbol, 1)
If (symbol = "#") Then
FlagSymbol = 1
End If
' Обработка запроса на вход в группу
If (Message = "#1") And (Flaguser = 1) Then
FlagVlr = 0
FlagSymbol = 0
Set filevlr = objFSO.OpenTextFile(vlr, 1)
Set filevlrback = objFSO.OpenTextFile(vlr_back, 2)
rec = Number + " " + Name
filevlrback.WriteLine(rec)
Do Until filevlr.AtEndOfStream
sw = filevlr.ReadLine()
filevlrback.WriteLine(sw)
Loop
filevlr.Close
filevlrback.Close
Set filevlr = objFSO.OpenTextFile(vlr, 2)
Set filevlrback = objFSO.OpenTextFile(vlr_back, 1)
Do Until filevlrback.AtEndOfStream
sw = filevlrback.ReadLine()
filevlr.WriteLine(sw)
Loop
filevlr.Close
filevlrback.Close
' ======= Отправляем сообщение об успешном входе в группу =======
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = "Вы успешно подключились к разговору. " + MessageNowInGroup
objMsg.Send()
End If
' Обработка запроса на вход в группу, если абонент уже находится в ней
If (Message = "#1") And (Flaguser = 2) Then
FlagVlr = 0
FlagSymbol = 0
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = "Вы уже подключены к разговору. " + MessageNowInGroup
objMsg.Send()
End If
' Обработка повторного запроса на выход из группы
If (Message = "#0") And (Flaguser = 1) Then
FlagSymbol = 0
End If
' Обработка запроса на выход из группы
If (Message = "#0") And (FlagVlr = 1) Then
FlagVlr = 0
FlagSymbol = 0
Set filevlr = objFSO.OpenTextFile(vlr, 1)
Set filevlrback = objFSO.OpenTextFile(vlr_back, 2)
Do Until filevlr.AtEndOfStream
sLine = filevlr.ReadLine()
nSpace = InStr(sLine, " ")
If nSpace > 0 Then
Number_vlr = Left(sLine, nSpace - 1)
Name_vlr = Trim(Right(sLine, Len(sLine) - nSpace))
rec = Number_vlr + " " + Name_vlr
End If
If (Number_vlr <> Number) Then
filevlrback.WriteLine(rec)
End If
Loop
filevlr.Close
filevlrback.Close
Set filevlr = objFSO.OpenTextFile(vlr, 2)
Set filevlrback = objFSO.OpenTextFile(vlr_back, 1)
Do Until filevlrback.AtEndOfStream
rec = filevlrback.ReadLine()
filevlr.WriteLine(rec)
Loop
filevlr.Close
filevlrback.Close
' ======= Отправляем сообщение об успешном выходе из группы =======
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = "Вы успешно покинули группу."
objMsg.Send()
End If
' Обработка запроса на получение списка собеседников
If (Message = "#?") And (FlagVlr = 1) Then
FlagVlr = 0
FlagSymbol = 0
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = MessageNowInGroup
objMsg.Send()
End If
' Обработка неверной или несуществующей команды
If (FlagSymbol = 1) And (FlagHlr = 1) Then
FlagVlr = 0
FlagSymbol = 0
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = "Команда не распознана. Команды: #1 - вход, #0 - выход, #? - список собеседников."
objMsg.Send()
End If
' Обработка пустого сообщения
If (Message = "") And (FlagVlr = 1) Then
FlagVlr = 0
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = MessageEmpty
objMsg.Send()
End If
' Обработка непустого сообщения
If (FlagVlr = 1) Then
Message = Trim(Message)
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set filevlr = objFSO.OpenTextFile(vlr, 1)
Set objSMSDriver = CreateObject("HeadwindGSM.SMSDriver")
objSMSDriver.Connect()
Do Until filevlr.AtEndOfStream
sLine = filevlr.ReadLine
nSpace = InStr(sLine, " ")
If nSpace > 0 Then
Number_buffer = Left(sLine, nSpace - 1)
Name_buffer = Trim(Right(sLine, Len(sLine) - nSpace))
If (Number_buffer <> Number) Then
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number_buffer
objMsg.Body = Name & ": " & Message
objMsg.Send()
End If
End If
Loop
filevlr.Close
Count_receipt = Count - 1
' ======= Отправляем квитанцию =======
Set objMsg = CreateObject("HeadwindGSM.SMSMessage")
objMsg.To = Number
objMsg.Body = "Message send to " & Count_receipt & " users. Text: '" & Message & "'"
objMsg.Send()
' ======= /Отправляем квитанцию =======
End If
' ======= Конец скрипта =======
WScript.Quit
Таким макаром мы получили простенький СМС-чат, который сейчас трудится на благо отечественных инженеров. Надеемся, что пригодится и вам.
Комментарии (9)
Beltoev
29.05.2015 11:14Почему не рассмотрели вариант с виртуальным номером для приёма смс и каким-нибудь смс-шлюзом для последующей рассылки?
Просто ваше решение требует постоянно подключенного к сети компьютера, стороннюю программу и usb-модем с сим-картой. В случае с виртуальным номером хватит самого дешевого хостинга для размещения скрипта обработки полученных смс и рассылки, виртуального номера (возможно есть сервисы, где можно-таки достать бесплатно или дешевле, но из дешевых видел только за 25 зелёных в месяц) и смс-шлюза (тут выбор побольше, смс от 7-15 копеек можно достать). Плюс после каждой рассылки можно автоматически запрашивать баланс и, если он равен некоторому минимальному значению, слать ответственному лицу смс-предупреждение, что пора бы пополнить баланс.
Из минусов вижу только абон. плату за номер, но, по-моему, на электроэнергию и стоимость смс у оператора (навряд ли будет дешевле смс-шлюза) у вас сейчас то же самое уходитzabtech Автор
29.05.2015 11:56Мы специально не рассматривали услуги третьих лиц в плане передачи и приёма сообщений.
Постоянно включенный компьютер выполняет еще ряд функций, так что про экономию электроэнергии речи не идёт.Beltoev
30.05.2015 01:04Хорошо, тогда другой сценарий решения задачи (я не придираюсь, просто показываю, что задача успешно решается и другими путями):
Если нужны только смс, без участия онлайн-сервисов, то:
1. Покупаем телефон под андроидом (подойдет самый дешевый)
2. Ставим на него Tasker
3. В Tasker-е пишем скрипт на прием и обработку сообщений, последующую рассылку и логирование (по аналогии с вашим VBA, но там синтаксис проще)
4. Профит
В итоге имеем портативный «сервер» смс-чатов (можно также постоянно подключенным к сети в офисе держать, это уже кому как удобнее).
По затратам: 1 000 — 2 000 р. на телефон + 100 р. на Tasker против 5 490 р. на программу смс-сервера + затраты на модем
TimsTims
29.05.2015 18:25Если есть gsm модем, то за те же деньги приобретается мобильный интернет.
А там хоть группу в вайбере/вотсаппе гораздо дешевле получаетсяBeltoev
29.05.2015 21:19Зачем тогда вообще gsm-модем, если будет группа в одном из мессенджеров? Он как бы лишним получается
zabtech Автор
29.05.2015 21:21Не у всех инженеров телефоны поддерживают эти программы. Это раз. Во-вторых, элементарно не везде есть интернет. Часто приходится бывать в поселках, где базовая станция работает через космос на половине потока Е1. Говорить о передаче данных в таком случае довольно сложно. В третьих, смс-канал самое надежный тип обмена короткими сообщениями. И ложится он только когда падает сеть сотового оператора.
dMetrius
Зачем же постить к топику картинки с такими грубыми грамматическими ошибками?
zabtech Автор
Взято из реальной жизни.