Как сделать SMS-оповещение о землетрясениях с GeoJSON и SMS API

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр! Слышали о землетрясениях, которые происходят каждый день по всему миру? Как правило, если вы не сейсмолог или не живёте рядом с тектоническим разломом, то вам всё равно. Но информировать людей о реальных катастрофах в их регионе очень важно, покажем пример такой системы оповещения через SMS. 

Сегодня мы построим небольшой центр уведомлений. За основу возьмем GeoJSON Earthquake API для получения данных о землетрясениях, подключим Exolve API, чтобы рассылать SMS, а для хранения данных используем PostgreSQL.

Что используем

  • GeoJSON Earthquake API — предоставляет данные о землетрясениях в формате GeoJSON. Включает координаты, магнитуду, время и другие параметры.

  • Exolve SMS API — позволяет отправлять SMS с динамическими шаблонами.

  • PostgreSQL — база данных для хранения землетрясений и логирования уведомлений.

  • RabbitMQ — очередь сообщений для асинхронной отправки SMS.

  • Python — связываем все компоненты в единую работающую систему.

Как всё будет работать

  1. Получение данных: скрипт запрашивает данные о землетрясениях через GeoJSON API каждые 10 минут.

  2. Фильтрация: геофильтры отбирают только события, которые относятся к нужному региону (например, России).

  3. Обработка событий: новые события записываются в базу данных. Уведомления отправляются в RabbitMQ.

  4. Асинхронная отправка SMS: RabbitMQ обрабатывает очередь, отправляя SMS через Exolve API.

  5. Хранение истории: PostgreSQL сохраняет данные для аналитики.

Подготовка базы данных

Сначала создаём таблицы для хранения данных о землетрясениях и уведомлениях.

CREATE TABLE earthquakes (
    id SERIAL PRIMARY KEY,
    quake_id TEXT UNIQUE NOT NULL,
    magnitude REAL NOT NULL,
    place TEXT,
    time TIMESTAMP NOT NULL,
    longitude REAL NOT NULL,
    latitude REAL NOT NULL
);

CREATE TABLE notifications (
    id SERIAL PRIMARY KEY,
    earthquake_id INTEGER REFERENCES earthquakes(id),
    phone_number TEXT NOT NULL,
    sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

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

Таблица notifications логирует каждое отправленное SMS, связывая его с конкретным землетрясением.

Подключим RabbitMQ

RabbitMQ — это очередь сообщений, которая позволяет отправлять уведомления асинхронно. Установим RabbitMQ:

sudo apt install rabbitmq-server
sudo systemctl start rabbitmq-server
sudo systemctl enable rabbitmq-server

Теперь создадим очередь для уведомлений. Очереди делают обработку сообщений надёжной, даже если часть системы временно недоступна.

Подключимся к базе данных и RabbitMQ:

import psycopg2
import pika

# Подключение к PostgreSQL
def get_db_connection():
    return psycopg2.connect(
        dbname="earthquakes",
        user="your_user",
        password="your_password",
        host="localhost"
    )

# Инициализация RabbitMQ
def init_rabbitmq():
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    channel = connection.channel()
    channel.queue_declare(queue='notifications')
    return channel

get_db_connection() создаёт подключение к PostgreSQL, а init_rabbitmq() подключается к RabbitMQ и создаёт очередь notifications, если её ещё нет.

Работа с GeoJSON API и фильтрация событий

import requests
from datetime import datetime
from shapely.geometry import Point, Polygon

GEOJSON_API_URL = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson"

# Координаты границ региона (пример для России)
RUSSIA_BOUNDS = Polygon([
    (19.0, 41.0), (19.0, 82.0), (169.0, 82.0), (169.0, 41.0), (19.0, 41.0)
])

def is_in_region(longitude, latitude):
    point = Point(longitude, latitude)
    return RUSSIA_BOUNDS.contains(point)

def fetch_earthquake_data():
    response = requests.get(GEOJSON_API_URL)
    if response.status_code != 200:
        print(f"Ошибка API: {response.status_code}")
        return []

    data = response.json()
    earthquakes = []
    for feature in data["features"]:
        props = feature["properties"]
        coords = feature["geometry"]["coordinates"]

        if props["mag"] >= 4.5 and is_in_region(coords[0], coords[1]):
            earthquakes.append({
                "id": feature["id"],
                "magnitude": props["mag"],
                "place": props["place"],
                "time": datetime.fromtimestamp(props["time"] / 1000),
                "longitude": coords[0],
                "latitude": coords[1]
            })

    return earthquakes

fetch_earthquake_data() запрашивает данные с GeoJSON API и возвращает список землетрясений, которые соответствуют нашим фильтрам. is_in_region() проверяет, находится ли событие в пределах России.

Сохраним данные и отправим уведомления в RabbitMQ

def save_earthquake(conn, quake):
    with conn.cursor() as cursor:
        cursor.execute("""
            INSERT INTO earthquakes (quake_id, magnitude, place, time, longitude, latitude)
            VALUES (%s, %s, %s, %s, %s, %s)
            ON CONFLICT (quake_id) DO NOTHING
        """, (quake['id'], quake['magnitude'], quake['place'], quake['time'], quake['longitude'], quake['latitude']))
    conn.commit()

def send_to_queue(channel, earthquake):
    message = {
        "place": earthquake["place"],
        "magnitude": earthquake["magnitude"],
        "time": earthquake["time"].strftime("%Y-%m-%d %H:%M:%S")
    }
    channel.basic_publish(exchange='', routing_key='notifications', body=json.dumps(message))
    print(f"Сообщение отправлено в очередь: {message}")

save_earthquake() записывает событие в базу данных и send_to_queue() отправляет данные о землетрясении в очередь RabbitMQ для дальнейшей обработки.

Обработчик уведомлений

import requests
import json

EXOLVE_API_URL = "https://api.exolve.ru/messaging/v1/SendSMS"
EXOLVE_API_KEY = "Bearer your_api_key_here"

def process_notifications(ch, method, properties, body):
    data = json.loads(body)
    message = (
        f"⚠ Землетрясение!\n"
        f"Местоположение: {data['place']}\n"
        f"Магнитуда: {data['magnitude']}\n"
        f"Время: {data['time']}\n"
        f"Рекомендация: Оставайтесь в безопасности."
    )

    payload = {
        "number": "AlfaName",
        "destination": "79991112233",
        "text": message
    }
    headers = {
        "Authorization": EXOLVE_API_KEY,
        "Content-Type": "application/json"
    }

    response = requests.post(EXOLVE_API_URL, json=payload, headers=headers)
    if response.status_code == 200:
        print("SMS успешно отправлено")
    else:
        print(f"Ошибка SMS: {response.status_code}")

process_notifications() забирает данные из очереди и отправляет SMS через Exolve API.

Виды сообщений

  • Предупреждение: «⚠ Землетрясение! Магнитуда 5.4. Оставайтесь в безопасности!»

  • Рекомендация: «Следуйте к безопасной зоне. Проверьте перекрытие газа и воды.»

  • Статус: «Все системы работают. Угрозы нет.»

Заключение

Создали систему, которая:

  1. Обрабатывает данные о землетрясениях.

  2. Фильтрует события по географическим границам.

  3. Отправляет уведомления с помощью RabbitMQ и Exolve API.

  4. Хранит все данные в PostgreSQL.

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


Подписывайтесь на наш Хаб, следите за новыми гайдами и получайте приз

Каждый понедельник мы случайным образом выбираем победителей среди новых подписчиков нашего Хабр-канала и дарим крутые призы от МТС Exolve: стильные рюкзаки, лонгсливы и мощные беспроводные зарядки. Победители прошлых розыгрышей и правила.

Источник: https://habr.com/ru/companies/exolve/articles/875216/


Интересные статьи

Интересные статьи

Всем привет, случалось такое, что вам надо поставить кучу агентов битрикса на крон, а потом сидеть и разбираться - сколько они отрабатывают, отрабатывают ли вообще, когда падают или зависают? Ну конеч...
Общение с клиентом в переписке сильно отличается от общения вживую или по телефону. Одно из главных отличий — не всегда можно понять и отследить, «услышал» ли нас человек или сообщение ушло впустую.&n...
Эта статья предназначена прежде всего для тех, кого задолбало курить, кто пробовал бросить но все еще курит, кто уже почти сдался но вроде как еще есть силы попробовать бросить.В общем задолбало курит...
Одни предприниматели стремятся к полной независимости своего дела, другие – встраиваются в денежный поток крупных игроков и зарабатывают миллионы на дополнениях для чужого продукта...
Это моя первая статья, в ней я попытаюсь описать полученный мною практический опыт работы со Spring Repository под капотом фреймворка. Готовых статей про эту тему я в инт...