Pull to refresh

Как сделать свой сервер для приема, обработки и передачи смс

Reading time 12 min
Views 125K

Доброго времени суток, уважаемое хабросообщество!
Недавно в универе мне была поставлена задача создать сервис смс-рассылки уведомлений старостами (деканатом и прочими) своим группам.
Основные требования к сервису были следующими:
— Сервис должен быть многопользовательским (старост более 1) с возможностью одновременной обработки запросов
— Не должен быть привязан к online сервисам рассылки (дабы не утекли базы пользователей)
— Должен быть достаточно легким и мобильным
— Максимально малозатратным
— Должен использовать только открытое программное обеспечение
— Должен обеспечивать отправку уведомлений, даже если у отправляющей стороны нет доступа к Интернету и/или компьютеру. (т.е. не только отправлять, но и принимать входящие смс сообщения от старост + определять от кого конкретно они поступили и делать рассылку по их группам)

Как удалось это реализовать — смотрите под катом.

Итак, начнем.

Список ингредиентов.



Для того, чтобы приступить к организации сервиса, нам будет нужно собрать все необходимые железки.

  • Компьютером у меня будет выступать нетбук ASUS EEEPC 701
  • Мобильный телефон, используемый для отправки смс — Nokia 3110
  • USB bluetooth адаптер — для связи компьютера и телефона
  • Зарядки для мобильника и нетбука для постоянной поддержки рабочего состояния
  • Подключение к Интернету. По LAN или Wi-Fi.
  • Симкарта с тарифом МТС «Супер МТС». У них все смски по РО на любого ОПСОСа по 10 копеек.
  • Зарегистрированный хостинг и какое-нибудь доменное имя.


В принципе, на этом можно и ограничиться. Но для более комфортной работы я бы посоветовал еще:

  • Монитор
  • Беспроводную клавиатуру
  • Дополнительное охлаждения для нетбука (ибо при работе 24/7 собственного маленького кулера ему будет явно не достаточно)


Охлаждение.


Что касается охлаждения — сделать его совершенно элементарно. Возьмите старый 80 миллиметровый кулер, зачистите красный и черный проводки, идущие от него. Затем отрежьте половину от ненужного USB кабеля. Также зачистите на нем красный и черный провод. Смотайте провода от кулера и от USB вместе, заизолируйте и ваше охлаждение готово. Желательно разместить кулер под нетбуком на пластилиновых ножках, чтобы он громко не резонировал.

Собственно, вот как это выглядит:



Если кто-то не понял, вот подробный мануал.

Что касается зачистки проводов — я люблю сначала опалить их концы на керосиновой горелке, а потом ножом легко счистить остатки. Керосинку тоже покупать не стоит — ее можно сделать из обычной 60 Ваттной лампочки Ильича. Пробиваете верхушку цоколя, заливаете туда керосин, вставляете кусок старой тряпки вместо фитиля и готово. Вот как это выглядит:



Подготавливаем железо к работе.



OS

В виде операционной системы для нетбука я выбрал EasyPeasy — система для нетбуков, основанная на ядре Linux и модифицированная для слабых компьютеров. Скачать ее, установить на флешку, а затем на нетбук можно тут.

Bluetooth

После того, как операционная система заняла свое место, подключаем bluetooth-адаптер в свободный USB порт. Система должна распознать его автоматически. Включаем Nokia и спариваем телефон и компьютер.

Установка gnokii

Нужно установить gnokii. Я уже и раньше ссылался на эту статью по его установке на Линукс. Думаю, разберетесь.
После установки наберите в консоли что-то вроде echo «Привет, друг!» | gnokii --sendsms '+7номер_вашего_друга' и проверьте, отправилась ли смска.

Подготовка хостинга

Возможно, Вам подойдет какой-либо другой способ организации многопользовательской работы, но я использовал возможности Joomla ACL (уровней доступа). Как это настроить — очень подробно описано в этой статье. Я лишь хочу заметить, что на выходе у Вас должен получиться сайт, где каждый из пользователей (те, кто будет отправлять смски) будет иметь свой личный кабинет, попасть в который он сможет введя номер своего мобильного телефона (в качестве логина, в формате 79XX XXX XX XX) и пятизначный цифровой код в качестве пароля. К примеру, как на сайте vamsms.ru

Подготавливаем программное обеспечение к работе.


Внимание! Все программки я писал сам. Можете использовать их как вам хочется, только дайте обратную связь, если найдете какой-либо серьезный косяк.

Итак, наша задача состоит в том, чтобы научить наш сервер общаться с удаленным сайтом, собирать с него заявки на отправку и отправлять сообщения. Кроме того, сервер должен уметь принимать входящие сообщения (на Nokia, с которой смски отправляются), осознавать, что они отправлены старостами и также ставить в очередь на рассылку по той группе, к которой конкретная староста принадлежит. Плюс, все это нужно синхронизировать. Ну и вести логи, конечно.

Организация MySQL БД

Для начала необходимо сделать БД, относящуюся к хостингу, на котором у Вы организовали сайт. Я назвал свою БД — pXXXX_sender (под XXX скрываются цифры моего пользователя на хостинге)
Внутри этой БД нужно создать 3 таблицы. У меня они имеют следующую структуру:

starosti (идентификация и ареалы действий для старост)
— number
— group
— course
— facultet
— pass

telephones (все телефоны в базе)
— number
— groups
— course
— facultet

on_demand (тут хранятся сообщения на отправку)
— message
— group
— course
— id_this_trans
— date_log

Отлично, на этом с базами данных все.

Организация shell-скрипта на нетбуке (сервере).

Теперь нужно сделать так, чтобы нетбук каждые 10 минут проверял:
1) Поступили ли новые сообщения с сайта? Если да, то отправляю их нужным группам.
2) Поступили новые входящие смски? Если да, то от старост ли они? Если нет, то просто удаляю их, если да, то определяю, за какую группу отвечает староста и делаю рассылку сообщения по той группе.

Привожу листинг shell-скрипта:

#!/bin/bash

a=1 #заглушка для  бесконечного цикла
test_trigger=`cat cache_test_trig.txt` #исходное значение триггера при старте программы

while [ $a == 1  ] #начало бесконечного цикла
do
if ping -c1 ya.ru &> /dev/null
then


test_trigger_now=$(wget http://site.ru/trigger.txt --quiet -O -) # получаем значение триггера

	if [ $test_trigger_now -eq $test_trigger ]; then  #совпадает ли текущий триггер с новым?
			  echo "Nothing new :("				#если совпадает, уход в сон до след цикла
			  
###############################
######### Mobile extention
###############################
aa=1 #переменная для поддержания цикла
#count=1 #счетчик для выбора смс из памяти по 1


while [ $aa == 1  ] # получение смсок из памяти по 1
do


sms_test=$(gnokii --getsms IN 1 1) #считали 1 сообщение в цикле (1 1,2 2 и т.д.)
echo $sms_test>mobile_temp.txt #поместили ответ системы в файл
answer_test=$(grep -c Date/time: mobile_temp.txt) #ищем в ответе ключевое слово, присущее сообщению(Date/time:)

if [ "$answer_test" != "1" ]; then #если ключевое слово не найдено, память закончилась, вышла ошибка

aa=2 # прерываем цикл
echo "messages over! Stop"

elif [ "$answer_test" == "1" ]; then #если ключевое слово найдено, 

echo "Here is 1 message"
#let count=$count+1 #работа со счетчиком выборки из памяти
#Теперь в переменной sms_test находится сообщение вида '1. Inbox Message (Unread) Date/time: 07/02/2013 15:33:16 +0400 Sender: +7904xxxxxx Msg Center: Text: 51235 Мое сообщение.'

server_send=$(wget http://site.ru/serv_mobile.php?text="$sms_test" -O /dev/null) #передаем сообщение на сервер для записи/незаписи в бд

sleep 1
gnokii --deletesms IN 1 1 #очищает память телефона 1 сообщение
fi

sleep 3
done




###############################
######### Mobile extention
###############################
			  
			  
			  sleep 500
				
				else 							#если не совпадает, запуск программы смс-отправки
				echo "i need to do something!"
				let different=$test_trigger_now-$test_trigger #(стало-было), сколько нового
				#echo $different #разницу выдает правильно
				get_sender=$(wget http://site.ru/trigger.php?diff=$different --quiet -O -) # в скрипт на реме передается количество новых записей (сколько папок создавать)
				#echo $get_sender # скрипт на реме воспринимает переменную different нормально
				echo $get_sender #возвращает ОК от рема
				sleep 3 #дадим рему время очухаться
				
				#Start SMS-sending
				n=0
				while [ $n -lt $different ]    # пока n < different
				do
					let n=$n+1
#!!!!!!!!!!!SENDING!!!!!!!!!!!!!!
			
					echo "send files from send$n folder" #место для запуска gnokii					
					mess_for_gnok=$(wget http://site.ru/send$n/message.txt --quiet -O -) #скачиваем сообщение
					numb_for_gnok=$(wget http://site.ru/send$n/numbers.txt) #скачиваем номера
					cat numbers.txt | while read line
					do
					inp=$line
					echo "$mess_for_gnok" | gnokii --sendsms $inp
					echo $line
					echo $mess_for_gnok
					sleep 2
					done
					rm numbers.txt #delete temporary files
					sleep 1
#!!!!!!!!!!!SENDING!!!!!!!!!!!!!!					
				done
				terminator=$(wget http://site.ru/terminate.php?kill=$different --quiet -O -) # delete all temperal folders in root
				#echo "i kill all files"
				
				test_trigger=$test_trigger_now #задание выполнено, поднимаемся и ждем новых изменений в trigger.txt
				echo $test_trigger>cache_test_trig.txt
				
				
				sleep 10 #засыпает после всех действий
			  
	fi

else
echo "Here is no Internet. Find it!"
sleep 500
fi

done



Кроме того, в папке со скриптом нужно создать файл cache_test_trig.txt со значением 1 (и в таблице on_demand сделать первую запись какую-нибудь)

Организация php-скриптов на удаленном хостинге.

В личном кабинете пользователя в joomla должна быть форма следующего содержания:

<script type="text/javascript">

function isNotMax(e){ e = e || window.event;var target = e.target || e.srcElement;var code=e.keyCode?e.keyCode:(e.which?e.which:e.charCode)switch(code){case13:case8:case9:case46:case37:case38:case39:case40:returntrue;}return target.value.length <= target.getAttribute('maxlength');}

</script>

<p><strong>Форма смс-отправки.</strong></p>

<form action="http://site.ru/handler.php" method="POST">Кому отправляется уведомление:<br />
<select name="Group"> 
<option value="11">группа 11 </option>
</select>
<select name="Course"> 
<option value="4">курс 4 </option>
</select>

<select name="Facult"> 
<option value="1">Факультет экономический </option>
</select>

<br />

<br />

<hr style="color:#B4B4B4; border:thin groove"/>
<b>Внимание!</b> Вы можете ввести не более <b>70</b> любых символов(русский/английский, знаки препинания)<hr style="color:#B4B4B4; border:thin groove"/><br />

Текст уведомления:<br />

<textarea name="message" rows="4" cols="46" maxlength="70" onkeypress="return isNotMax(event)"></textarea> <br />

<input type="submit" value="Начать отправку!" /></form>



Очевидно, данные из формы передаются в файл, лежащий в корневой директории хостинга handler.php

Вот его листинг:

<?php
$prev_page = $_SERVER['HTTP_REFERER'];

$who_is_you = substr_count("$prev_page", "http://site.ru/index.php/"); //защита от многократных запросов и запросов извне

		if ($who_is_you == 1)
		
			{

$message=$_POST['message'];
$message=htmlspecialchars(stripslashes($message)); // обработка от спецсимволов

$course=$_POST['Course'];
$course=htmlspecialchars(stripslashes($course));   // обработка от спецсимволов

$groups=$_POST['Group'];
$groups=htmlspecialchars(stripslashes($groups));   // обработка от спецсимволов

$facult=$_POST['Facult'];
$facult=htmlspecialchars(stripslashes($facult));   // обработка от спецсимволов




//validation-----------------------------
 if (empty($message))
  {
    
    echo "
<body style='background-image: url(body_bg.gif); background-repeat: repeat-x'> 

<div align='center' style='margin-top: 14%'>
<p style='color: #666666; font-family: Verdana,Helvetica,sans-serif; font-size: 18px; line-height: 1.8em;'><i>Ваше сообщение НЕ отправлено!</i> <br>Текст уведомления не указан. <a href = '$prev_page'>Вернитесь назад и заполните поле.</a> </p>
</div>

</body>
    ";
    
  }
//validation-----------------------------
  
 else
  {

//connect_to_BD-------------------------- 
include(db_connect.php);
//connect_to_BD--------------------------
mysql_query("SET NAMES 'utf8'");
$r = mysql_query("SELECT id_this_trans FROM on_demand ORDER BY id_this_trans DESC LIMIT 0, 1"); // выводит номер последней трансакции

while($row = mysql_fetch_array($r))
	{
		
		$last_tranzaktion = $row['id_this_trans'];
		
	}

$last_tranzaktion++; // подготовили id новой транзакции
$heutige_datum = date("Y-m-d H:i:s");
mysql_query ("INSERT INTO on_demand VALUES('$message','$groups','$course','$last_tranzaktion', '$heutige_datum')"); // новое указание для отправки

file_put_contents("trigger.txt", $last_tranzaktion);

    echo "
<body style='background-image: url(body_bg.gif); background-repeat: repeat-x'> 

<div align='center' style='margin-top: 14%'>
<p style='color: #666666; font-family: Verdana,Helvetica,sans-serif; font-size: 18px; line-height: 1.8em;'><i>Ваше сообщение принято  к отправке!</i> <br> Вы можете завершить работу с сервисом, или <a href = 'http://site.ru/'>вернуться в Главное меню системы</a> </p>
</div>

</body>
    ";
    
  }
  
			}
		
		
		else


			{
				
	echo "
<html>
<head><title> 404 Not Found
</title></head>
<body><h1> 404 Not Found
</h1>
The resource requested could not be found on this server!<hr />
Powered By <a href='http://www.litespeedtech.com'>LiteSpeed Web Server</a><br />
<font face='Verdana, Arial, Helvetica' size=-1>LiteSpeed Technologies is not responsible for administration and contents of this web site!</font></body></html>
	";			
			}
?>



Далее, для того, чтобы удаленный хостинг мог отвечать на запросы shell-скрипта, необходимо, чтобы в его корневой директории были расположены следующие файлы:

файл serv_mobile.php — отвечает за обработку сообщений, принятых мобильным телефоном

<?php
#$sms_text=mysql_real_escape_string($_GET['text']); // получаем очередную смску от хома + защита от инъекций
$sms_text=$_GET['text']; // получаем очередную смску от хома + защита от инъекций

file_put_contents("temp_mobile_mess.txt", $sms_text); //записываем всю смску во временное хранилище
$get_from_file = file_get_contents("temp_mobile_mess.txt"); //записываем всю смску в переменную
 
$number = substr("$get_from_file", 72, 11); //выделяем номер отправителя (не забывать про read/unread)
$pass = substr("$get_from_file", 103, 5); 	//выделяем пароль
$message = substr("$get_from_file", 109); 	//выделяем текст сообщения


//connect_to_BD-------------------------- 
include(db_connect.php);
//connect_to_BD--------------------------
mysql_query("SET NAMES 'utf8'");

//защита от инъекций
$number = mysql_real_escape_string($number);
$pass = mysql_real_escape_string($pass);
$message = mysql_real_escape_string($message);

//защита от инъекций


$r = mysql_query("SELECT COUNT(*) FROM `starosti` WHERE pass='$pass' AND number='$number'"); //проверяет наличие связки пароль/номер в базе
while($row = mysql_fetch_array($r))
	{
		

		if($row[0] == 1) //определил, что такая связка присутствует
			{


//------------определяет заполнение поля id_this_trans---------------
					$q = mysql_query("SELECT id_this_trans FROM on_demand ORDER BY id_this_trans DESC LIMIT 0, 1"); // выводит номер последней трансакции

							while($rowq = mysql_fetch_array($q))
								{
									
									$last_tranzaktion = $rowq['id_this_trans'];
									
								}

							$last_tranzaktion++; // подготовили id новой транзакции
//------------определяет заполнение поля id_this_trans---------------
							
//------------вставляет все полученные данные в таблицу on_demand----	
					$second_r = mysql_query("SELECT * FROM `starosti` WHERE pass='$pass' AND number='$number'"); //узнаем курс и группу старосты, приславшей смску
					while($second_row = mysql_fetch_array($second_r))
						{
					
							$groups = $second_row['group'];
							$course = $second_row['course'];
							$heutige_datum = date("Y-m-d H:i:s");
							mysql_query ("INSERT INTO on_demand VALUES('$message','$groups','$course','$last_tranzaktion','$heutige_datum')"); // формирует указание для отправки
						}
//------------вставляет все полученные данные в таблицу on_demand----	

	#не забыть обновить триггер на новое значение транзакции
	file_put_contents("trigger.txt", $last_tranzaktion);
		 			  
			}

		
	}
	
?>



Скрипт trigger.php, тоже лежит в корневой директории, создает папки на хостинге, содержащие файл с сообщением и файл с номерами, которым это сообщение будет рассылаться.

Его листинг:
<?php
$inp_diff = $_GET['diff'];  // принимает различия от хома

//connect_to_BD-------------------------- 
include(db_connect.php);
//connect_to_BD--------------------------
mysql_query("SET NAMES 'utf8'");

//Create files AND Folders---------------
for($i=1; $i<=$inp_diff;$i++)
	{
		mkdir("send$i");
		file_put_contents("send$i/message.txt",'');
		file_put_contents("send$i/numbers.txt",'');
	}
//Create files AND Folders---------------

$counter=1;// счетчик для записи в папки, указывает на номер папки

// Вывод последних дифферент записей-----
$r = mysql_query("SELECT * FROM on_demand ORDER BY id_this_trans DESC LIMIT 0, $inp_diff"); // выводит последние записи

while($row = mysql_fetch_array($r))
	{
		file_put_contents("send$counter/message.txt",$row['message']); //заполняет текстом файл message.txt во всех папках
// заполняем файл numbers.txt во всех папках			
			$help_course = $row['course'];
			$help_group = $row['group'];
			$second_r = mysql_query("SELECT * FROM telephones WHERE course='$help_course' AND groups='$help_group'");
			while($second_row = mysql_fetch_array($second_r))
				{
					
					$insert_number = $second_row['number']; // помогает записать в файл 1 номер на 1 итерации
					$fp=fopen("send$counter/numbers.txt","a");
					fputs($fp,$insert_number. "\n");
					fclose($fp);
					

				}
// заполняем файл numbers.txt во всех папках			
$counter++;
		
	}
// Вывод последних дифферент записей-----

echo "ok";

?>


Последний файл в корневой директории — файл terminate.php. Удаляет созданные ранее папки с сервера, когда сообщения уже разосланы.

Его листинг:
<?php
$inp_diff = $_GET['kill'];  // принимает различия от хома

//Delete files AND Folders---------------
for($i=1; $i<=$inp_diff;$i++)
	{
		unlink("send$i/message.txt");
		unlink("send$i/numbers.txt");
	        rmdir("send$i");
	}
//Delete files AND Folders---------------

?>


Подведение итогов.


Прошу меня простить, если мой рассказ получился излишне сумбурным. Я правда старался максимально последовательно изложить тут свои мысли :) Удачной вам настройки, с радостью отвечу на вопросы в комментариях.

Кстати, вот как все это выглядит у меня:

Tags:
Hubs:
+62
Comments 83
Comments Comments 83

Articles