Здравствуйте уважаемыее!
Хотелось бы рассказать от том как я делал смс оповещение для своего факультета.
Я учусь на первом курсе ФСПО ИТМО. У нас на факультете есть так называемая диспетчерская служба которая занимается оповещением студентов об изменении в расписании, в интернете это осуществляется через страничку в контакте и твиттер.
Однажды произошёл небольшой скандал по поводу того что пол-группы не явились на занятия из-за того что не успели отследить изменение в расписании. В тот день у меня появилась идея создать смс оповещение с той самой Vk странички.
Да, я знаю, что для это есть специализированные сервисы, но это слишком просто…
Лирическое отступление
Я новичок в Linux, в Phython и Sql и статья эта пишется по принципу «Учась учи».
Так что не серчайте сильно, если что не так, а лучше просто отпишите свои замечания в комментариях.
С чего начать?
Немного поразмыслив я полян что мне нужно:
- Сканировать Vk страничку на предмет новых постов
- Искать в этих самых постах упоминания о какой либо группе, например 143
- Если в каком либо посте найден номер какой либо группы, то разослать этот пост на мобильники этой группы
У меня есть домашний «сервер» на Debian который я гордо именую GrindelServer, на его базе я и собирался всё это реализовать.
Решено было начать с того что-бы просто научится отправлять смски из командной строки. Так получилось что у меня завалялось около трех 3G модемов Huawei E1150, решено было использовать один из них. В Google без труда нашёлся материал по этой теме. Вкратце нужно было перевести модем в режим «только модем» (так у них еще есть фэйковый CD привод и карт ридер), после чего настроить программу Gnokii или Gammu на работу с ним.
Я выбрал Gammu…
Gammu
Не могу сказать что с Gammu было просто, но в итоге всё получилось.
Установка в Debian стандартная:
$ sudo apt-get install gammu
У меня поставилась версия 1.28.0
Конфиг gammu: /etc/gammurc
Модем обычно определяется как /dev/ttyUSB0, ttyUSB1, ttyUSB2. Нам нужен ttyUSB0
В конфиг достаточно добавить
[gammu]
port= /dev/ttyUSB0
connection = at
Сonnectinon — это тип соединения
Всё! после этого можно проверять
$ sudo gammu --identify
Должно выдать что-то на подобии
Устройство : /dev/ttyUSB0
Manufacturer : huawei
Модель : unknown (E1550)
Firmware : 11.608.12.00.143
IMEI : 123456789101112
Номер SIM (IMSI) : 123456789098765
Здесь проблем возникнуть не должно, но если они есть, то всегда есть $ man gammu
и Google.
Первая смс из консоли
Cначала нужно задать права на использование модема, они понадобятся только для этого примера, так что не переживайте по тому поводу, что они сбрасываются после перезагрузки.
$ sudo chmod 777 /dev/ttyUSB0
Теперь, если в модем вставлена симка и на ней есть деньги, можно отправить смс
Например:
$ sudo echo "Привет Grindel" | gammu sendsms TEXT +79818050567 -unicode
Автоматизация отправки смс
Пакет gammu в данном случае нужен был только для проверки, в дальнейшем я использовал демон gammu-smsd.
Он умеет отсылать смс-ки из базы данных (подробно о нём можно почитать на сайте gammu, но обратите внимание на то, что там описывается более поздняя версия.)
Ставим:
$ sudo apt-get install gammu-smsd
Конфиг /etc/gammu-smsdrc
[gammu]
port = /dev/ttyUSB0
connection = at
[smsd]
#Сервер базы данных
service = MYSQL
host = localhost
logfile = /var/log/gammu-smsd
#Пользователь базы
user = smsd
password = password
#Адрес сервера базы данных
pc = localhost
#Имя базы
database = smsd
Теперь нужно создать базу.
В $ man gammu-smsd-tables
сказано что дамп необходимой базы находится по адресу docs/sql/mysql.sql
насколько я понял надо искать в исходниках программы, так что скачав их(версия 1.28.0) можно распаковать архив и в нем найти необходимый sql скрипт, а дальше залить его любым удобным образом, например через phpMyAdmin.(Если кто-нибудь предложит более разумный метод, буду рад!)
После всех этих действий стоит перезапустить демон
$ sudo /etc/init.d/gammu-smsd restart
ВНИМАНИЕ! После запуска gammu-smsd
, gammu --identify
станет выдавать ошибку, это нормально!
Отправка смс через mysql
Пример из $ man gammu-smsd-tables
, этот код можно исполнить в том же phpMyAdmin
INSERT INTO outbox (
DestinationNumber,
TextDecoded,
CreatorID,
Coding
) VALUES (
'НОМЕР ТЕЛЕФОНА',
'This is a SQL test message',
'Program',
'Default_No_Compression'
);
Принцип следующий
- DestinationNumber — это номер на который должна прийти смс
- TextDecoded — текст который должен прийти
- CreatorID — просто пометка, говорящая о там какой процесс создал эту запись
- Coding — кодировка, если смс на английском то указывается 'Default_No_Compression', если на русском то следует указать 'Unicode_No_Compression'.
ВНИМАНИЕ! В смс можно отправить только 160 латинских и 70 кириллических символов! Для отправки большего количества текста существуют «multipart sms». Gammu-smsd поддерживает их отправку, процесс описан в $ man gammu-smsd-tables
Написание своей программы для проверки vk странички
Честно говоря дальше я был в полной растерянности, я никогда не писал подобных программ, весь мой опыт программирования ограничивался школьным Turbo Pascal и небольшим js проектом «Bart Chalkboard Generator»(можно найти в моём профиле).
Я даже не знал какой язык выбрать. Решено было задать вопрос в Q&A.
Спасибо большое за ответы g0lden и avalak!
В результате я выбрал Python и приступил к его изучению на Codecademy. Параллельно я искал способ реализации работы с VK API на Python. И нашёл я его на Хабре, спасибо dzhioev за это! Так же я потихоньку начал разбираться с VK api. Из-за учёбы свободного времени оставолось очень мало и продвигался я очень медленно.
Познакомившись с Python более или менее(на Codecademy я дошёл до списков и словарей), я решил что уже готов приступить к написанию своего скрипта.
Начал я с регистрации странички вконтакте от имени которой будет действовать приложение, далее нужно было зарегистрировать своё приложение. Это достаточно просто и описано в документации вконтакта, так что писать об этом не буду.
Разбор скрипта
Вкратце последовательность действий скрипта такова:
- Узнать id последнего отправленного программой поста
- Узнать появлялись ли новые посты после последней отправки
- Попытаться найти в каждом из новых постов хотя бы одну из групп
- Если был обнаружен номер группы то отправить смс по номерам групп(то есть отправить определённый записи в таблицу outbox базы smsd)
Стоит пояснить про базу данных. Я добавил в базу smsd таблицы VkPosts и Groups. В VkPosts складываются id всех оправленных программой постов, так что в следующий раз можно запросить id последнего отправленного поста, соответственно самого позднего. А в groups находятся номера студентов и номера групп: группа | номер.
Теперь сам скрипт по кусочкам
Подключаем необходимые модули
import vk_auth
import json
import urllib2
from urllib import urlencode
import MySQLdb
import logging
import time
import re
Инициализируем соединение в базой, и задаём кодировку, что бы не было кракозярб
db = MySQLdb.connect(host="localhost", user="Логин пользователя бд", passwd="Пароль", db="smsd")
cursor = db.cursor()
db.set_character_set('utf8')
cursor.execute('SET NAMES utf8;')
cursor.execute('SET CHARACTER SET utf8;')
cursor.execute('SET character_set_connection=utf8;')
Сообщаем vk api логин, пароль, номер приложения и запрашиваем привилегии с помощью функции auth из модуля vk_auth, напоминаю модуль взят отсюда.
token, user_id = vk_auth.auth(login, password, "3139526", "groups,wall")
Получаем в переменную token токен приложения а в user_id айди пользователя чьи данный были введены.
Далее определяем ещё несколько переменных
cursor.execute("SELECT postID FROM VKposts ORDER BY VKposts.number DESC LIMIT 0 , 1;") #Получаем id последнего отправленного поста
lastSendedPostId = cursor.fetchall()[0][0] #Кладём его в LastSendedPostId в нормальном виде
groupId = -123456 #Указываем Id группы, ВНИМАНИЕ id группы необходимо указать со знаком минус иначе vkapi решит что это id странички пользователя
cursor.execute("SELECT DISTINCT `group` FROM `groups`") #Собираем массив номеров групп
rawGroups = cursor.fetchall()
groups = []
for rawGroup in rawGroups:
groups.append(rawGroup[0])
Основная часть программы
post = call_api("wall.get", [("owner_id", groupId), ("count", "1")], token)[1] #Получаем последний пост ос стены группы
if post['id'] > lastSendedPostId and post['date'] > actualTime: #Сравниваем Id только что полученого поста с Id последнего отправленного, да можно обойтись без это строчки, но мне так проще было понять
logging.info('I have found some new posts!')
i = 0
while post['id'] > lastSendedPostId and post['date'] > actualTime: #Выбираем в цикле все новые посты
if not len(post) > 1: #Это будет верно если посты на стене внезапно закончатся
break
post['text'] = post['text'].replace('<br>', ' ') #Убираем из текста поста ненужные нам теги перевода строк
for group in groups: #Перебираем массив с номерми групп
if len(re.findall("(^| )" + str(group) + "( |$)", post['text'])) != 0: #Если в тексте поста есть больше нуля совпадение по регулярному выражению "(^| )" + номер группы + "( |$)"
logging.info("I have found %s group in %s post" % (group, post['id']))
cursor.execute("SELECT `tel` FROM `groups`WHERE `group`=%s;" % (group)) #То получаем номера телефонов этой группы
rawTels = cursor.fetchall()
tels = []
for rawTel in rawTels:
tels.append(rawTel[0])
for tel in tels: #Перебираем полученный массив телефонов
if len(post['text']) > 70: #Если длинна текста поста больше 70 то посылаем замену с указанием ссылки на пост
cursor.execute("INSERT INTO outbox(DestinationNumber, Coding, TextDecoded, CreatorID, Class)VALUES ('%s', 'Unicode_No_Compression', 'У тебя изменилось расписание vk.com/wall%s_%s', 'Python', '-1')" % (tel, groupId, post['id']))
sendedPosts.insert(0, post['id']) #Добавляем Id поста в массив
logging.info("Send Cuted %s post to %s****" % (post['id'], tel[0:8]))
else: #Если нет то тогда посылвем полный текст поста
cursor.execute("INSERT INTO outbox(DestinationNumber, Coding, TextDecoded, CreatorID, Class)VALUES ('%s', 'Unicode_No_Compression', '%s', 'Python', '-1')" % (tel, post['text']))
sendedPosts.insert(0, post['id']) #Добавляем Id поста в массив
logging.info("Send %s post to %s****" % (post['id'], tel[0:8]))
i += 1
post = call_api("wall.get", [("owner_id", groupId), ("offset", i), ("count", "1")], token)[1] #Получаем следующий пост
Здесь есть небольшая загвоздка. Если текст поста больше 70 символов я отправляю вместо него — «У тебя изменилось расписание » + сслыка на пост, делаю я это потому что в одну кириллическую смс не влезет больше 70 символов(было сказано выше), а с multipart sms я ещё не разобрался.
logging.info это функция функция ведения логов настроенная в начале кода:
logging.basicConfig(format='%(asctime)s | %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', filename='/home/NetDisk/sms/smslog.log',level=logging.DEBUG)
Вот собственно и всё! Осталось сделать скрип исполняемым
$ sudo chmod +x путь к скрипту
И добавить его в Cron c требуемым интервалом.
Но возможности gammu и всего выше перечисленного на этом не заканчиваются, и ваши, я уверен, тоже. Так что фантазируйте.
В планах
- Сбор базы данных студентов
- Создание веб интерфейса
- И еще всякие мелочёвки и фитчи
Источники
- По поводу настройки gammu было пересмотрена куча всего, так что пожалуй это будет Google и man по gammu
- Пишем модуль для авторизации в VK API, ещё раз спасибо dzhioev!
- Документация vk api
- И ещё куча вcего по мелочи в Google.
P.S.
Мне было очень интересно всем эти заниматься, я был полностью захвачен этой идеей, я был счастлив иметь возможность все это делать и изучать, пусть не всегда было легко(иной раз хотелось всё бросить).
Спасибо, что прочли, надеюсь вам понравилось!
Удачи вам с вашими идеями!
Исходник можно взять здесь.
Автор: grindel