Commit ca104303 by dima

Init here

parents
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class GoipConfig(AppConfig):
name = 'goip'
import requests
import time
import re
USERNAME = 'root'
PASSWORD = 'root'
GOIP_SMS_LIST_URL = 'http://172.16.0.252/default/en_US/status.xml'
RESET_SMS_LIMIT_URL = 'http://172.16.0.252/default/en_US/status.html'
HEADERS = {
'authorization': "Basic YWRtaW46YWRtaW4=",
'cache-control': "no-cache",
'postman-token': "805c647d-a888-b121-fa7b-237a9454002a"
}
class GOIP(object):
"""Класс для отправки СМС с GOIP"""
_cmp = re.compile(r'messageid=(\d+)')
@classmethod
def send_sms(cls, content, number):
"""
Функция отправки СМС с goip
content: текст СМС сообщения
number: телефонный номер
"""
headers = {'cache-control': 'no-cache'}
send_url = 'http://10.12.126.244/goip/en/resend.php'
login_url = 'http://10.12.126.244/goip/en/dosend.php'
querystring = {
'USERNAME': USERNAME,
'PASSWORD': PASSWORD,
'smsprovider': '1',
'smsnum': number,
'method': '2',
'Memo': content
}
response = requests.post(login_url, headers=headers, params=querystring)
if response.status_code == 200:
message_id = cls._cmp.findall(response.text)[0]
querystring = {
'messageid': message_id,
'USERNAME': USERNAME,
'PASSWORD': PASSWORD
}
requests.get(send_url, headers=headers, params=querystring)
return message_id
@classmethod
def get_sms_status(cls, sms_id):
"""
Функция получения статуса
sms_id: id СМС-ки
"""
login_url = 'http://10.12.126.244/goip/en/dologin.php'
status_url = 'http://10.12.126.244/goip/en/sendinfo.php'
post_data = {'username': USERNAME, 'password': PASSWORD}
with requests.Session() as session:
session.post(login_url, data=post_data)
response = session.get(status_url, params={'id': sms_id})
return response.text.find('<td align="center">Done</td>') != -1
def get_goips():
"""
Возвращает объект генератор, содержащий канал и количество СМС оставшихся на канале
:return: generator
"""
milliseconds = round(time.time() * 1000)
querystring = {"type": "", "ajaxcachebust": str(milliseconds)}
response = requests.get(GOIP_SMS_LIST_URL, headers=HEADERS, params=querystring)
regexp = re.compile(r'<l(?P<goip>\d+)_sms_count>(?P<value>.*?)</l\d+_sms_count>')
for i in regexp.finditer(response.text):
res = i.groupdict()
if res['value'].isdigit():
yield int(res['goip']), int(res['value'])
def reset_sms_count(channel):
"""
Посылает запрос на сброс коннекта обределенного канала GOIP
:param channel: номер канала, целое число (int)
:return: response object
"""
querystring = {'type': 'list', 'reset': 'SMS', 'line%s' % channel: '1'}
return requests.get(RESET_SMS_LIMIT_URL, headers=HEADERS, params=querystring)
def format_number(number):
"""
Форматирует телефонный номер
:param number: строка (str)
:return: str
"""
return number if number.startswith('+') else '+%s' % number
def generate_sms(numbers, text):
# если первый символ строки является английской буквой
max_str_len = 157 if 'a' <= text[0].lower() <= 'z' else 67
# разбиваем текст сообщения на n частей
sms_count, y = divmod(len(text), max_str_len)
if y > 0:
sms_count += 1
# формируем список из sms_count сообщений длинною n символов
sms_list = [text[i * max_str_len: i * max_str_len + max_str_len] for i in range(sms_count)]
for number in numbers:
number = format_number(number)
for sms_text in sms_list:
yield number, sms_text
\ No newline at end of file
# Generated by Django 2.0.5 on 2018-05-02 07:59
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='SmsHistory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sms_id', models.CharField(max_length=255)),
('number', models.CharField(max_length=15)),
('text', models.TextField()),
('status', models.CharField(choices=[('sent', 'sent'), ('error', 'error'), ('pending', 'pending')], max_length=20)),
('timestamp', models.DateTimeField(auto_now_add=True)),
],
options={
'ordering': ['sms_id'],
},
),
]
# Generated by Django 2.0.5 on 2018-05-02 08:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('goip', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='smshistory',
name='status',
field=models.CharField(choices=[('sent', 'sent'), ('error', 'error'), ('pending', 'pending')], default='pending', max_length=20),
),
]
# Generated by Django 2.0.5 on 2018-05-04 08:01
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('goip', '0002_auto_20180502_1117'),
]
operations = [
migrations.CreateModel(
name='SmsGroup',
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('timestamp', models.DateTimeField(auto_now_add=True)),
],
),
migrations.AddField(
model_name='smshistory',
name='group_id',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sms', to='goip.SmsGroup'),
),
]
# Generated by Django 2.0.5 on 2018-05-04 11:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('goip', '0003_auto_20180504_1101'),
]
operations = [
migrations.CreateModel(
name='GoipNames',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('channel_name', models.CharField(max_length=255)),
('channel_id', models.IntegerField(null=True)),
('count', models.IntegerField(null=True)),
('is_active', models.BooleanField(default=False)),
],
),
]
from django.db import models
import uuid
SMS_STATUS = (
('sent', 'sent'),
('error', 'error'),
('pending', 'pending'),
)
class SmsGroup(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
timestamp = models.DateTimeField(auto_now_add=True)
class SmsHistory(models.Model):
group_id = models.ForeignKey(SmsGroup, null=True, related_name='sms', on_delete=models.CASCADE)
sms_id = models.CharField(max_length=255)
number = models.CharField(max_length=15)
text = models.TextField()
status = models.CharField(max_length=20, choices=SMS_STATUS, default='pending')
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['sms_id']
class GoipNames(models.Model):
channel_name = models.CharField(max_length=255)
channel_id = models.IntegerField(null=True)
count = models.IntegerField(null=True)
is_active = models.BooleanField(default=False)
from goip.helpers import get_goips, reset_sms_count, generate_sms
from .models import SmsGroup, SmsHistory, GoipNames
from sms_service.celery import app
from .helpers import GOIP
import uuid
DELAY = 60
class UpdateSMStatusError(Exception):
pass
@app.task
def list_send_sms(group_id, numbers, text):
group = SmsGroup.objects.get(uuid=group_id)
for number, sms_text in generate_sms(numbers, text):
sms_id = str(uuid.uuid4())
# sms_id = GOIP.send_sms(sms_text, number)
group.sms.create(sms_id=sms_id, number=number, text=sms_text)
update_sms_status.apply_async((sms_id,), countdown=DELAY)
@app.task(bind=True)
def update_sms_status(self, sms_id):
status = GOIP.get_sms_status(sms_id)
if not status:
if self.request.retries == 3:
SmsHistory.objects.filter(sms_id=sms_id).update(status='error')
else:
self.retry(exc=UpdateSMStatusError, max_retries=3, countdown=DELAY)
else:
SmsHistory.objects.filter(sms_id=sms_id).update(status='sent')
@app.task
def reset_sms_count_limit():
for i in GoipNames.objects.filter(is_active=True):
reset_sms_count(i.channel_id)
@app.task
def update_sms_count_limit():
"""
Таск обнавляет количество лимитов в базе,
:return: None
"""
for goip_id, goip_value in get_goips():
if GoipNames.objects.filter(channel_id=int(goip_id), is_active=True).exists():
record = GoipNames.objects.get(channel_id=int(goip_id), is_active=True)
record.count = goip_value
record.save()
from django.test import TestCase
# Create your tests here.
from django.urls import path, re_path
from goip.views import SendSms, StatusSms
urlpatterns = [
path('send/sms/', SendSms.as_view()),
re_path(r'status/sms/(?P<group_id>.*?)/', StatusSms.as_view()),
]
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
from .tasks import list_send_sms
from .models import SmsGroup
class SendSms(APIView):
parser_classes = (JSONParser,)
def post(self, request):
text = request.data['content']
numbers = request.data['numbers']
group_id = SmsGroup.objects.create()
list_send_sms.delay(group_id, numbers, text)
return Response({'group_id': str(group_id)})
class StatusSms(APIView):
def get(self, _, group_id):
rec = SmsGroup.objects.filter(uuid=group_id)
if rec.exists():
res = [(i.number, i.status) for i in rec[0].sms.all()]
return Response({'status': 'success', 'res': res})
return Response({'status': 'error', 'message': 'Unknown group ID: {}'.format(group_id)})
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sms_service.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
# Indicate Celery to use the default Django settings module
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sms_server.settings')
app = Celery('sms_server')
app.config_from_object('django.conf:settings')
# This line will tell Celery to autodiscover all your tasks.py that are in your app folders
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
"""
Django settings for sms_service project.
Generated by 'django-admin startproject' using Django 2.0.5.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '1-00&!$q9u&5jygaqnm7mo2%dw+s@@j0layvu_jmrml+buf+xg'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'goip',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'sms_service.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'sms_service.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
if not DEBUG:
BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
else:
BROKER_URL = 'django://'
# Safe serializer settings
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Minsk'
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
)
}
"""sms_service URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('goip.urls')),
]
"""
WSGI config for sms_service project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sms_service.settings")
application = get_wsgi_application()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment