Browse Source

Add viber notifications

devel
Dmitry Novikov 7 years ago
parent
commit
d673b995d3
  1. 6
      chatbot/admin.py
  2. 5
      chatbot/apps.py
  3. 112
      chatbot/locale/ru/LC_MESSAGES/django.po
  4. 64
      chatbot/migrations/0001_initial.py
  5. 27
      chatbot/migrations/0002_auto_20180808_1236.py
  6. 0
      chatbot/migrations/__init__.py
  7. 73
      chatbot/models.py
  8. 149
      chatbot/telebot.py
  9. 3
      chatbot/views.py
  10. 2
      devapp/locale/ru/LC_MESSAGES/django.po
  11. 16
      devapp/views.py
  12. 20
      djing/lib/decorators.py
  13. 3
      djing/local_settings.py.template
  14. 7
      djing/settings.py
  15. 1
      djing/urls.py
  16. 1
      messenger/__init__.py
  17. 7
      messenger/admin.py
  18. 5
      messenger/apps.py
  19. 28
      messenger/forms.py
  20. 190
      messenger/locale/ru/LC_MESSAGES/django.po
  21. 88
      messenger/migrations/0001_initial.py
  22. 0
      messenger/migrations/__init__.py
  23. 129
      messenger/models.py
  24. 56
      messenger/tasks.py
  25. 15
      messenger/templates/messenger/add_messenger.html
  26. 57
      messenger/templates/messenger/messenger_list.html
  27. 49
      messenger/templates/messenger/vibermessenger_form.html
  28. 3
      messenger/tests.py
  29. 17
      messenger/urls.py
  30. 161
      messenger/views.py
  31. 34
      msg_app/models.py
  32. 3
      msg_app/urls.py
  33. 22
      msg_app/views.py
  34. 7
      requirements.txt
  35. 4
      static/js/my.js
  36. 15
      systemd_units/djing_telebot.service
  37. 55
      taskapp/handle.py
  38. 3
      taskapp/urls.py
  39. 21
      taskapp/views.py
  40. 29
      telebot.py
  41. 9
      templates/base.html

6
chatbot/admin.py

@ -1,6 +0,0 @@
from django.contrib import admin
from . import models
admin.site.register(models.MessageHistory)
admin.site.register(models.TelegramBot)
admin.site.register(models.MessageQueue)

5
chatbot/apps.py

@ -1,5 +0,0 @@
from django.apps import AppConfig
class ChatbotConfig(AppConfig):
name = 'chatbot'

112
chatbot/locale/ru/LC_MESSAGES/django.po

@ -1,112 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Dmitry Novikov <nerosketch@gmail.com>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-09 14:57+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov <nerosketch@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: models.py:13
msgid "Employee"
msgstr "Сотрудник"
#: models.py:14
msgid "Telegram chat id"
msgstr "Номер чата из telegram"
#: models.py:21
msgid "Telegram bot"
msgstr "Telegram бот"
#: models.py:22
msgid "Telegram bots"
msgstr "Telegram боты"
#: models.py:36
msgid "Message history"
msgstr "История собщений"
#: models.py:37
msgid "Message histories"
msgstr "Истории собщений"
#: models.py:54
msgid "Target employee"
msgstr "Сотрудник"
#: models.py:55
msgid "Message"
msgstr "Сообщение"
#: models.py:60
msgid "Status of message"
msgstr "Статус сообщения"
#: models.py:62
msgid "App tag"
msgstr "Тэг приложения"
#: models.py:71 models.py:72
msgid "Message queue"
msgstr "Очередь оповещений"
#: telebot.py:64
msgid "Let's get acquainted, what is your name? Write your login from billing."
msgstr "Давай знакомиться, как тебя зовут? Напиши свой логин из биллинга."
#: telebot.py:85
msgid "I do not know the answer to this yet."
msgstr "Я пока не знаю ответа на это"
#: telebot.py:106
msgid ""
"You are not found in the database, check that it correctly pointed out its "
"LOGIN. Try again"
msgstr ""
"Ты не найден в базе, проверь что правильно указал именно свой ЛОГИН. "
"Попробуй ещё"
#: telebot.py:110
#, python-format
msgid ""
"Yes, it's nice to meet %(username)s, I will notify you about events in "
"billing. Successful work ;)"
msgstr ""
"Да, приятно познакомиться %(username)s, я буду оповещать тебя о событиях в "
"биллинге. Удачной работы ;)"
#: telebot.py:122
msgid "Let's ping, write ip. It will be necessary to wait 10 seconds"
msgstr "Давай пинганём, напиши ip. Нужно будет подождать 10 сек"
#: telebot.py:129
msgid "It's not like ip address, try again"
msgstr "Это не похоже на ip адрес, попробуй ещё"
#: telebot.py:132
#, python-format
msgid "You're '%s', right?"
msgstr "Ты ведь %s ?"
#: telebot.py:140
msgid "Telegram bot token not found"
msgstr "Токен для бота Telegram не найден"
#: telebot.py:145
#, python-format
msgid "Recipient '%s' does not subscribed on notifications"
msgstr "%s не подписан на оповещения"

64
chatbot/migrations/0001_initial.py

@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='MessageHistory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=255)),
('date_sent', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Message history',
'verbose_name_plural': 'Message histories',
'db_table': 'chat_message_history',
},
),
migrations.CreateModel(
name='MessageQueue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=255, verbose_name='Message')),
('status', models.CharField(choices=[('n', 'New'), ('r', 'Read')], default='n', max_length=1,
verbose_name='Status of message')),
('tag', models.CharField(default='none', max_length=6, verbose_name='App tag')),
('target_employee',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='Target employee')),
],
options={
'verbose_name': 'Message queue',
'verbose_name_plural': 'Message queue',
'db_table': 'chat_message_queue',
},
),
migrations.CreateModel(
name='TelegramBot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('chat_id', models.PositiveIntegerField(default=0, verbose_name='Telegram chat id')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='Employee')),
],
options={
'verbose_name': 'Telegram bot',
'verbose_name_plural': 'Telegram bots',
'db_table': 'chat_telegram_bot',
},
),
]

27
chatbot/migrations/0002_auto_20180808_1236.py

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-08-08 12:36
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('chatbot', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='messagehistory',
options={'ordering': ('-date_sent',), 'verbose_name': 'Message history', 'verbose_name_plural': 'Message histories'},
),
migrations.AlterModelOptions(
name='messagequeue',
options={'ordering': ('target_employee__username',), 'verbose_name': 'Message queue', 'verbose_name_plural': 'Message queue'},
),
migrations.AlterModelOptions(
name='telegrambot',
options={'ordering': ('chat_id',), 'verbose_name': 'Telegram bot', 'verbose_name_plural': 'Telegram bots'},
),
]

0
chatbot/migrations/__init__.py

73
chatbot/models.py

@ -1,73 +0,0 @@
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.conf import settings
AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL')
class ChatException(Exception):
pass
class TelegramBot(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_('Employee'))
chat_id = models.PositiveIntegerField(_('Telegram chat id'), default=0)
def __str__(self):
return "%s - %d" % (self.user.get_full_name(), self.chat_id)
class Meta:
db_table = 'chat_telegram_bot'
verbose_name = _('Telegram bot')
verbose_name_plural = _('Telegram bots')
ordering = ('chat_id',)
class MessageHistory(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE)
message = models.CharField(max_length=255)
date_sent = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message
class Meta:
db_table = 'chat_message_history'
verbose_name = _('Message history')
verbose_name_plural = _('Message histories')
ordering = ('-date_sent',)
class MessageQueueManager(models.Manager):
def pop(self, user, tag='none'):
msgs = self.filter(target_employee=user, status='n', tag=tag)[:1].only('message').values('id', 'message')
if len(msgs) > 0:
self.filter(id=msgs[0]['id']).delete()
return msgs[0]['message']
def push(self, msg, user, tag='none'):
msg = self.create(target_employee=user, message=msg, tag=tag)
return msg
class MessageQueue(models.Model):
target_employee = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_('Target employee'))
message = models.CharField(_('Message'), max_length=255)
STATUSES = (
('n', 'New'),
('r', 'Read')
)
status = models.CharField(_('Status of message'), max_length=1, choices=STATUSES, default='n')
# tag: each application puts its own to separate messages between these applications
tag = models.CharField(_('App tag'), max_length=6, default='none')
objects = MessageQueueManager()
def __str__(self):
return self.message
class Meta:
db_table = 'chat_message_queue'
verbose_name = _('Message queue')
verbose_name_plural = _('Message queue')
ordering = ('target_employee__username',)

149
chatbot/telebot.py

@ -1,149 +0,0 @@
# -*- coding: utf-8 -*-
from telepot import helper, glance, Bot
from telepot.exception import TelegramError
import os
import socket
import collections
from django.utils.translation import ugettext as _
from urllib3.exceptions import ProtocolError
from .models import TelegramBot, ChatException, MessageQueue
from chatbot.models import MessageHistory
from accounts_app.models import UserProfile
from django.conf import settings
token = getattr(settings, 'TELEGRAM_BOT_TOKEN')
class DjingTelebot(helper.ChatHandler):
_current_user = None
_dialog_fn = None
_chat_id = 0
def __init__(self, seed_tuple, **kwargs):
super(DjingTelebot, self).__init__(seed_tuple, **kwargs)
self.cmds = {
'ping': self.ping,
'iam': self.say_me
}
# отвечаем пользователю
def _sent_reply(self, text):
self.sender.sendMessage(text)
# задаём вопрос пользователю, и ожидаем ответ в fn
def _question(self, text, fn):
if not isinstance(fn, collections.Callable):
raise TypeError
self._dialog_fn = fn
if text is not None:
self._sent_reply(text)
# сохраняем сообщение в базе
def _message_log(self, msg):
if self._current_user is None:
self._question(None, self.question_name)
return False
MessageHistory.objects.create(
user=self._current_user,
message=msg
)
return True
# Начинаем диалог
# @seed - chat_id
def open(self, initial_msg, seed):
content_type, chat_type, chat_id = glance(initial_msg)
if content_type != 'text':
return True
self._chat_id = chat_id
try:
tbot = TelegramBot.objects.get(chat_id=seed)
self._current_user = tbot.user
self._message_log(initial_msg['text'])
except TelegramBot.DoesNotExist:
self._question(_("Let's get acquainted, what is your name? Write your login from billing."),
self.question_name)
return True # prevent on_message() from being called on the initial message
# получаем сообщение
def on_chat_message(self, msg):
content_type, chat_type, chat_id = glance(msg)
if content_type != 'text':
return
self._chat_id = chat_id
text = msg['text'].lower()
# выполняем комманды если они есть
if text in self.cmds.keys():
self.cmds[text]()
elif self._dialog_fn is not None:
if not callable(self._dialog_fn):
raise TypeError
self._dialog_fn(text)
self._dialog_fn = None
else:
self._sent_reply(_('I do not know the answer to this yet.'))
if not self._message_log(text):
return
# спрашиваем имя пользователя
def question_name(self, username):
try:
profile = UserProfile.objects.get(username=username)
self._current_user = profile
try:
TelegramBot.objects.get(user=profile)
except TelegramBot.DoesNotExist:
if self._chat_id == 0:
raise ChatException('telebot.py. def question_name: Chat id is empty')
TelegramBot.objects.create(
user=profile,
chat_id=self._chat_id
)
except UserProfile.DoesNotExist:
self._question(
_("You are not found in the database, check that it correctly pointed out its LOGIN. Try again"),
self.question_name)
return
self._sent_reply(
_("Yes, it's nice to meet %(username)s, I will notify you about events in billing. Successful work ;)")
% {'username': profile.get_full_name()})
# заканчивается время диалога
# ex - время ожидания (timeout=ex в pave_event_space)
def on_close(self, ex):
self._current_user = None
self._dialog_fn = None
self._chat_id = 0
def ping(self, ip=None):
if ip is None:
self._question(_("Let's ping, write ip. It will be necessary to wait 10 seconds"), self.ping)
return
try:
socket.inet_aton(ip)
ret = os.popen('`which ping` -c 10 %s' % ip).read()
self._sent_reply(ret)
except socket.error:
self._question(_("It's not like ip address, try again"), self.ping)
def say_me(self):
self._sent_reply(_("You're '%s', right?") % self._current_user.get_full_name())
# Just sending text to specified account
def send_notify(msg_text, account, tag='none'):
try:
MessageQueue.objects.push(msg=msg_text, user=account, tag=tag)
if token is None:
raise ChatException(_('Telegram bot token not found'))
tb = TelegramBot.objects.get(user=account)
tbot = Bot(token)
tbot.sendMessage(tb.chat_id, msg_text)
except TelegramBot.DoesNotExist:
raise ChatException(_("Recipient '%s' does not subscribed on notifications") % account.get_full_name())
except ProtocolError as e:
raise ChatException('ProtocolError: %s' % e)
except TelegramError as e:
raise ChatException('Telegram error: %s' % e)

3
chatbot/views.py

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

2
devapp/locale/ru/LC_MESSAGES/django.po

@ -324,7 +324,7 @@ msgstr "Название типа свича"
#: templates/devapp/custom_dev_page/onu.html:22 #: templates/devapp/custom_dev_page/onu.html:22
#: templates/devapp/custom_dev_page/onu_for_zte.html:22 #: templates/devapp/custom_dev_page/onu_for_zte.html:22
msgid "Attached user" msgid "Attached user"
msgstr "Прикрепленный абонент"
msgstr "Прикреплённый абонент"
#: templates/devapp/custom_dev_page/onu.html:48 #: templates/devapp/custom_dev_page/onu.html:48
#: templates/devapp/custom_dev_page/onu_for_zte.html:50 #: templates/devapp/custom_dev_page/onu_for_zte.html:50

16
devapp/views.py

@ -3,7 +3,6 @@ from ipaddress import ip_address
from abonapp.models import Abon from abonapp.models import Abon
from accounts_app.models import UserProfile from accounts_app.models import UserProfile
from chatbot.models import ChatException
from devapp.base_intr import DeviceImplementationError from devapp.base_intr import DeviceImplementationError
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -27,8 +26,8 @@ from djing.lib.tln import ZteOltConsoleError, OnuZteRegisterError, \
from djing.tasks import multicast_email_notify from djing.tasks import multicast_email_notify
from easysnmp import EasySNMPTimeoutError, EasySNMPError from easysnmp import EasySNMPTimeoutError, EasySNMPError
from group_app.models import Group from group_app.models import Group
from guardian.decorators import \
permission_required_or_403 as permission_required
from messenger.tasks import multicast_viber_notify
from guardian.decorators import permission_required_or_403 as permission_required
from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_user
from .forms import DeviceForm, PortForm, DeviceExtraDataForm from .forms import DeviceForm, PortForm, DeviceExtraDataForm
from .models import Device, Port, DeviceDBException, DeviceMonitoringException from .models import Device, Port, DeviceDBException, DeviceMonitoringException
@ -709,19 +708,20 @@ class OnDeviceMonitoringEvent(global_base_views.SecureApiView):
recipients = UserProfile.objects.get_profiles_by_group( recipients = UserProfile.objects.get_profiles_by_group(
device_down.group.pk) device_down.group.pk)
multicast_email_notify.delay(msg_text=gettext(notify_text) % {
user_ids = tuple(recipient.pk for recipient in recipients.only('pk').iterator() if recipient.flags.notify_mon)
text = gettext(notify_text) % {
'device_name': "%s(%s) %s" % ( 'device_name': "%s(%s) %s" % (
device_down.ip_address, device_down.ip_address,
device_down.mac_addr, device_down.mac_addr,
device_down.comment device_down.comment
) )
}, account_ids=(
recipient.pk for recipient in recipients.only('pk').iterator() if recipient.flags.notify_mon
))
}
multicast_email_notify.delay(msg_text=text, account_ids=user_ids)
multicast_viber_notify.delay(None, account_id_list=user_ids, message_text=text)
return { return {
'text': 'notification successfully sent' 'text': 'notification successfully sent'
} }
except ChatException as e:
except ValueError as e:
return { return {
'text': str(e) 'text': str(e)
} }

20
djing/lib/decorators.py

@ -1,29 +1,11 @@
from functools import wraps from functools import wraps
from django.conf import settings from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponseForbidden, JsonResponse
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from djing.lib import check_sign from djing.lib import check_sign
def require_ssl(view):
"""
Decorator that requires an SSL connection. If the current connection is not SSL, we redirect to the SSL version of
the page.
from: https://gist.github.com/ckinsey/9709984
"""
@wraps(view)
def wrapper(request, *args, **kwargs):
debug = getattr(settings, 'DEBUG', False)
if not debug and not request.is_secure():
target_url = "https://%s%s" % (request.META['HTTP_HOST'], request.path_info)
return HttpResponseRedirect(target_url)
return view(request, *args, **kwargs)
return wrapper
# Allow to view only admins # Allow to view only admins
def only_admins(fn): def only_admins(fn):
@wraps(fn) @wraps(fn)

3
djing/local_settings.py.template

@ -69,3 +69,6 @@ EMAIL_HOST = 'smtp.mailserver.com'
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST_PASSWORD = 'password' EMAIL_HOST_PASSWORD = 'password'
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
# public url for Viber Bot
VIBER_BOT_PUBLIC_URL = 'https://your_domain.name'

7
djing/settings.py

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django_cleanup.apps.CleanupConfig',
'ip_pool', 'ip_pool',
'accounts_app', 'accounts_app',
'gw_app', 'gw_app',
@ -52,7 +53,7 @@ INSTALLED_APPS = [
'statistics', 'statistics',
'taskapp', 'taskapp',
'clientsideapp', 'clientsideapp',
'chatbot',
'messenger',
'msg_app', 'msg_app',
'dialing_app', 'dialing_app',
'group_app', 'group_app',
@ -234,3 +235,7 @@ REDIS_PORT = '6379'
BROKER_URL = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0' BROKER_URL = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0'
BROKER_TRANSPORT_OPTIONS = {'visibility_timeout': 3600} BROKER_TRANSPORT_OPTIONS = {'visibility_timeout': 3600}
CELERY_RESULT_BACKEND = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0' CELERY_RESULT_BACKEND = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0'
# public url for Viber Bot
VIBER_BOT_PUBLIC_URL = local_settings.VIBER_BOT_PUBLIC_URL

1
djing/urls.py

@ -18,6 +18,7 @@ urlpatterns = [
path('dialing/', include('dialing_app.urls', namespace='dialapp')), path('dialing/', include('dialing_app.urls', namespace='dialapp')),
path('groups/', include('group_app.urls', namespace='group_app')), path('groups/', include('group_app.urls', namespace='group_app')),
path('ip_pool/', include('ip_pool.urls', namespace='ip_pool')), path('ip_pool/', include('ip_pool.urls', namespace='ip_pool')),
path('messenger/', include('messenger.urls', namespace='messenger')),
path('gw/', include('gw_app.urls', namespace='gw_app')) path('gw/', include('gw_app.urls', namespace='gw_app'))
# Switch language # Switch language

1
messenger/__init__.py

@ -0,0 +1 @@
default_app_config = 'messenger.apps.messengerConfig'

7
messenger/admin.py

@ -0,0 +1,7 @@
from django.contrib import admin
from messenger import models
admin.site.register(models.Messenger)
admin.site.register(models.ViberMessenger)
admin.site.register(models.ViberSubscriber)
admin.site.register(models.ViberMessage)

5
messenger/apps.py

@ -0,0 +1,5 @@
from django.apps import AppConfig
class messengerConfig(AppConfig):
name = 'messenger'

28
messenger/forms.py

@ -0,0 +1,28 @@
from django import forms
from messenger import models
class MessengerForm(forms.ModelForm):
class Meta:
model = models.Messenger
fields = ('bot_type',)
class MessengerViberForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs['initial']['bot_type'] = 1
super().__init__(*args, **kwargs)
inst = getattr(self, 'instance')
if inst:
self.fields['bot_type'].disabled = True
#self.fields['bot_type'].widget.attrs['disabled'] = True
class Meta:
model = models.ViberMessenger
fields = '__all__'
class MessengerViberMessageForm(forms.ModelForm):
class Meta:
model = models.ViberMessage
fields = '__all__'

190
messenger/locale/ru/LC_MESSAGES/django.po

@ -0,0 +1,190 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Dmitry Novikov nerosketch@gmail.com, 2019.
#
#, fuzzy
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-02-06 13:45+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: models.py:16 templates/messenger/messenger_list.html:20
msgid "Title"
msgstr "Название"
#: models.py:18
msgid "Viber"
msgstr "Вайбер"
#: models.py:20
msgid "Bot type"
msgstr "Тип бота"
#: models.py:21 templates/messenger/messenger_list.html:22
msgid "Slug"
msgstr "Ссыль"
#: models.py:28 models.py:92
msgid "messenger"
msgstr "Мэссенджер"
#: models.py:29 templates/messenger/messenger_list.html:7
#: templates/messenger/messenger_list.html:12
#: templates/messenger/vibermessenger_form.html:8
msgid "messengers"
msgstr "Мэссенджэры"
#: models.py:48
msgid "Bot secret token"
msgstr "Секретный токен viber"
#: models.py:49 models.py:108
msgid "Avatar"
msgstr "Аватар"
#: models.py:83
msgid "Viber messenger"
msgstr "Viber мэссенджэр"
#: models.py:84
msgid "Viber messengers"
msgstr "Viber мэссенджэры"
#: models.py:89
msgid "Message"
msgstr "Сообщение"
#: models.py:90
msgid "Date"
msgstr "Дата"
#: models.py:91
msgid "Sender"
msgstr "Отправитель"
#: models.py:93
msgid "Subscriber"
msgstr "Подписчик"
#: models.py:100
msgid "Viber message"
msgstr "Сообщение viber"
#: models.py:101
msgid "Viber messages"
msgstr "Сообщения viber"
#: models.py:106
msgid "User unique id in viber"
msgstr "Уникальный id viber"
#: models.py:107
msgid "Name"
msgstr "Имя"
#: models.py:109
msgid "System account"
msgstr "Системная учётная запись"
#: models.py:116
msgid "Viber subscriber"
msgstr "Подписчик viber"
#: models.py:117
msgid "Viber subscribers"
msgstr "Подписчики viber"
#: templates/messenger/add_messenger.html:5
msgid "Select bot type"
msgstr "Выберите тип бота"
#: templates/messenger/add_messenger.html:11
msgid "Add"
msgstr "Добавить"
#: templates/messenger/messenger_list.html:21
msgid "Type"
msgstr "Тип"
#: templates/messenger/messenger_list.html:33
msgid "Edit"
msgstr "Изменить"
#: templates/messenger/messenger_list.html:40
msgid "messengers was not found"
msgstr "Мэссенджеры не найдены"
#: templates/messenger/messenger_list.html:49
msgid "New"
msgstr "Новый"
#: templates/messenger/vibermessenger_form.html:10
msgid "Update messenger"
msgstr "Обновить мэссенджэр"
#: templates/messenger/vibermessenger_form.html:11
msgid "Change viber"
msgstr "Изменить viber"
#: templates/messenger/vibermessenger_form.html:13
msgid "Add messenger"
msgstr "Добавить мэссенджэр"
#: templates/messenger/vibermessenger_form.html:14
msgid "Add viber"
msgstr "Добавить viber"
#: templates/messenger/vibermessenger_form.html:25
msgid "Change messenger"
msgstr "Изменить мэссенджэр"
#: templates/messenger/vibermessenger_form.html:28
msgid "Add new messenger"
msgstr "Добавить мэссенджэр"
#: templates/messenger/vibermessenger_form.html:39
msgid "Save"
msgstr "Сохранить"
#: templates/messenger/vibermessenger_form.html:43
msgid "Send webhook"
msgstr "Отправить webhook"
#: views.py:38
msgid "Unexpected bot type"
msgstr "Не известный тип бота"
#: views.py:51
msgid "New viber messenger successfully created"
msgstr "Новый viber мэссенджэр успешно создан"
#: views.py:62
msgid "Viber messenger successfully updated"
msgstr "viber мэссенджэр успешно обновлён"
#: views.py:73
msgid "Viber messenger successfully deleted"
msgstr "viber мэссенджэр успешно удалён"
#: views.py:132
msgid "My telephone number"
msgstr "Мой номер телефона"
#: views.py:150
msgid ""
"Telephone not found, please specify telephone number in account in billing"
msgstr ""
"Номер телефона не найден. Укажите свой номер телефона в учётке в биллинге"
msgid "Your account is attached. Now you will be receive notifications from billing"
msgstr "Ваша учётка из биллинга привязана. Теперь вы будете получать оповещения из биллинга."

88
messenger/migrations/0001_initial.py

@ -0,0 +1,88 @@
# Generated by Django 2.1.3 on 2019-02-07 12:31
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Messenger',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=64, verbose_name='Title')),
('bot_type', models.PositiveSmallIntegerField(blank=True, choices=[(1, 'Viber')], verbose_name='Bot type')),
('slug', models.SlugField(verbose_name='Slug')),
],
options={
'verbose_name': 'messenger',
'verbose_name_plural': 'messengers',
'db_table': 'messengers',
'ordering': ('title',),
},
),
migrations.CreateModel(
name='ViberMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('msg', models.TextField(verbose_name='Message')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
('sender', models.CharField(max_length=32, verbose_name='Sender')),
],
options={
'verbose_name': 'Viber message',
'verbose_name_plural': 'Viber messages',
'db_table': 'viber_messages_notifications',
'ordering': ('-date',),
},
),
migrations.CreateModel(
name='ViberSubscriber',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uid', models.CharField(max_length=32, verbose_name='User unique id in viber')),
('name', models.CharField(blank=True, max_length=32, null=True, verbose_name='Name')),
('avatar', models.URLField(blank=True, max_length=250, null=True, verbose_name='Avatar')),
('account', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='System account')),
],
options={
'verbose_name': 'Viber subscriber',
'verbose_name_plural': 'Viber subscribers',
'db_table': 'viber_subscriber',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='ViberMessenger',
fields=[
('messenger_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='messenger.Messenger')),
('token', models.CharField(max_length=64, verbose_name='Bot secret token')),
('avatar', models.ImageField(null=True, upload_to='viber_avatar', verbose_name='Avatar')),
],
options={
'verbose_name': 'Viber messenger',
'verbose_name_plural': 'Viber messengers',
'db_table': 'viber_messenger_notifications',
'ordering': ('title',),
},
bases=('messenger.messenger',),
),
migrations.AddField(
model_name='vibermessage',
name='subscriber',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='messenger.ViberSubscriber', verbose_name='Subscriber'),
),
migrations.AddField(
model_name='vibermessage',
name='messenger',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='messenger.ViberMessenger', verbose_name='messenger'),
),
]

0
chatbot/__init__.py → messenger/migrations/__init__.py

129
messenger/models.py

@ -0,0 +1,129 @@
from typing import Iterable
from urllib.parse import urljoin
from django.conf import settings
from django.shortcuts import resolve_url
from django.utils.translation import gettext_lazy as _
from django.db import models
from viberbot import Api, BotConfiguration
from viberbot.api.messages import TextMessage
from viberbot.api.messages.message import Message
from accounts_app.models import UserProfile
class Messenger(models.Model):
title = models.CharField(_('Title'), max_length=64)
CHAT_TYPES = (
(1, _('Viber')),
)
bot_type = models.PositiveSmallIntegerField(_('Bot type'), choices=CHAT_TYPES, blank=True)
slug = models.SlugField(_('Slug'))
def __str__(self):
return self.title
class Meta:
db_table = 'messengers'
verbose_name = _('messenger')
verbose_name_plural = _('messengers')
ordering = ('title',)
def get_absolute_url(self):
if self.bot_type == 1:
return resolve_url('messenger:update_viber_messenger', self.slug)
def get_next_url(self):
if self.bot_type == 1: # Viber
return resolve_url('messenger:update_viber_messenger', self.slug)
else:
return resolve_url('messenger:messengers_list')
class ViberMessenger(Messenger):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._viber_cache = None
token = models.CharField(_('Bot secret token'), max_length=64)
avatar = models.ImageField(_('Avatar'), upload_to='viber_avatar', null=True)
def get_viber(self):
if self._viber_cache is None:
self._viber_cache = Api(BotConfiguration(
name=str(self.slug),
avatar=self.avatar.url,
auth_token=str(self.token)
))
return self._viber_cache
def send_message(self, to: UserProfile, msg):
try:
viber = self.get_viber()
vs = to.vibersubscriber
if issubclass(msg.__class__, Message):
viber.send_messages(str(vs.uid), msg)
else:
viber.send_messages(str(vs.uid), TextMessage(text=msg))
except ViberSubscriber.DoesNotExist:
pass
def send_messages(self, receivers, msg_text: str):
"""
:param receivers: QuerySet of accounts_app.UserProfile
:param msg_text: text message
:return: nothing
"""
viber = self.get_viber()
msg = TextMessage(text=msg_text)
for vs in ViberSubscriber.objects.filter(account__in=receivers).iterator():
viber.send_messages(str(vs.uid), msg)
def send_webhook(self):
pub_url = getattr(settings, 'VIBER_BOT_PUBLIC_URL')
listen_url = resolve_url('messenger:listen_viber_bot', self.slug)
public_url = urljoin(pub_url, listen_url)
viber = self.get_viber()
viber.set_webhook(public_url, ['failed', 'subscribed', 'unsubscribed', 'conversation_started'])
def __str__(self):
return self.title
class Meta:
db_table = 'viber_messenger_notifications'
verbose_name = _('Viber messenger')
verbose_name_plural = _('Viber messengers')
ordering = ('title',)
class ViberMessage(models.Model):
msg = models.TextField(_('Message'))
date = models.DateTimeField(_('Date'), auto_now_add=True)
sender = models.CharField(_('Sender'), max_length=32)
messenger = models.ForeignKey(ViberMessenger, verbose_name=_('messenger'), on_delete=models.CASCADE)
subscriber = models.ForeignKey('ViberSubscriber', on_delete=models.SET_NULL, verbose_name=_('Subscriber'), null=True)
def __str__(self):
return self.msg
class Meta:
db_table = 'viber_messages_notifications'
verbose_name = _('Viber message')
verbose_name_plural = _('Viber messages')
ordering = ('-date',)
class ViberSubscriber(models.Model):
uid = models.CharField(_('User unique id in viber'), max_length=32)
name = models.CharField(_('Name'), max_length=32, null=True, blank=True)
avatar = models.URLField(_('Avatar'), max_length=250, null=True, blank=True)
account = models.OneToOneField(UserProfile, on_delete=models.CASCADE, verbose_name=_('System account'), blank=True, null=True)
def __str__(self):
return self.name or 'no'
class Meta:
db_table = 'viber_subscriber'
verbose_name = _('Viber subscriber')
verbose_name_plural = _('Viber subscribers')
ordering = ('name',)

56
messenger/tasks.py

@ -0,0 +1,56 @@
from typing import Optional, Iterable
from celery import shared_task
from accounts_app.models import UserProfile
from messenger.models import ViberMessenger
@shared_task
def send_viber_message(messenger_id: Optional[int], account_id: int, message_text: str) -> Optional[str]:
"""
Send text message via viber
:param messenger_id: Primary key UID for messanger.ViberMessenger
:param account_id: User id from accounts_app.UserProfile
:param message_text:
:return: Optional text for log
"""
if not message_text:
return 'ERROR: empty message text'
try:
sp = UserProfile.objects.get(pk=account_id)
if messenger_id is None:
for vm in ViberMessenger.objects.all().iterator():
vm.send_message(sp, message_text)
else:
vm = ViberMessenger.objects.get(pk=messenger_id)
vm.send_message(sp, message_text)
except ViberMessenger.DoesNotExist:
return 'ERROR: Viber messanger with id=%d not found' % messenger_id
except UserProfile.DoesNotExist:
return 'ERROR: accounts_app.UserProfile with pk=%d does not exist' % account_id
@shared_task
def multicast_viber_notify(messenger_id: Optional[int], account_id_list: Iterable[int], message_text: str):
"""
Send multiple message via Viber to several addresses
:param messenger_id: Primary key UID for messanger.ViberMessenger
:param account_id_list: list of account ids from accounts_app.UserProfile
:param message_text:
:return: Optional text for log
"""
if not message_text:
return 'ERROR: empty message text'
account_id_list = tuple(account_id_list)
recipients = UserProfile.objects.filter(pk__in=account_id_list)
if not recipients.exists():
return 'No recipients found from ids: %s' % ','.join(str(i) for i in account_id_list)
if messenger_id is None:
for vm in ViberMessenger.objects.all().iterator():
vm.send_messages(recipients, message_text)
else:
vm = ViberMessenger.objects.filter(pk=messenger_id).first()
if vm is None:
return 'ERROR ViberMessenger with pk=%d does not exist' % messenger_id
vm.send_messages(recipients, message_text)

15
messenger/templates/messenger/add_messenger.html

@ -0,0 +1,15 @@
{% load i18n bootstrap3 %}
<form role="form" action="{% url 'messenger:add_messenger' %}" method="post">{% csrf_token %}
<div class="modal-header primary">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-blackboard"></span>{% trans 'Select bot type' %}</h4>
</div>
<div class="modal-body">
{% bootstrap_form form %}
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</button>
</div>
</form>

57
messenger/templates/messenger/messenger_list.html

@ -0,0 +1,57 @@
{% extends 'base.html' %}
{% load dpagination i18n %}
{% block breadcrumb %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li class="active">{% trans 'messengers' %}</li>
</ol>
{% endblock %}
{% block page-header %}
{% trans 'messengers' %}
{% endblock %}
{% block main %}
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="col-sm-4">{% trans 'Title' %}</th>
<th class="col-sm-3">{% trans 'Type' %}</th>
<th class="col-sm-4">{% trans 'Slug' %}</th>
<th class="col-sm-1">#</th>
</tr>
</thead>
<tbody>
{% for messenger in object_list %}
<tr>
<td>{{ messenger.title }}</td>
<td>{{ messenger.get_bot_type_display }}</td>
<td>{{ messenger.slug }}</td>
<td>
<a href="{{ messenger.get_absolute_url }}" class="btn btn-sm btn-default" title="{% trans 'Edit' %}">
<span class="glyphicon glyphicon-edit"></span>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4"><a href="#">{% trans 'messengers was not found' %}</a></td>
</tr>
{% endfor %}
</tbody>
{% if perms.messenger.add_messenger %}
<tfoot>
<tr>
<td colspan="4" class="btn-group btn-group-sm">
<a href="{% url 'messenger:add_messenger' %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-plus"></span> <span class="hidden-xs">{% trans 'New' %}</span>
</a>
</td>
</tr>
</tfoot>
{% endif %}
</table>
</div>
{% endblock %}

49
messenger/templates/messenger/vibermessenger_form.html

@ -0,0 +1,49 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n bootstrap3 %}
{% block breadcrumb %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'messenger:messengers_list' %}">{% trans 'messengers' %}</a></li>
{% if object %}
<li><a href="{{ object.get_absolute_url }}">{% trans 'Update messenger' %}</a></li>
<li class="active">{% trans 'Change viber' %}</li>
{% else %}
<li><a href="{% url 'messenger:add_messenger' %}">{% trans 'Add messenger' %}</a></li>
<li class="active">{% trans 'Add viber' %}</li>
{% endif %}
</ol>
{% endblock %}
{% block main %}
{% if object %}
{% url 'messenger:update_viber_messenger' object.slug as form_url %}
{% trans 'Change messenger' as panel_title %}
{% else %}
{% url 'messenger:add_viber_messenger' as form_url %}
{% trans 'Add new messenger' as panel_title %}
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ panel_title }}</h3>
</div>
<div class="panel-body">
<form role="form" action="{{ form_url }}" method="post" enctype="multipart/form-data">{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
{% if object %}
<a href="{% url 'messenger:webhook_viber_bot' object.slug %}" class="btn btn-default btn-sm btn-modal">
<span class="glyphicon glyphicon-share"></span> {% trans 'Send webhook' %}
</a>
{% endif %}
</form>
</div>
</div>
{% endblock %}

3
messenger/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

17
messenger/urls.py

@ -0,0 +1,17 @@
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from messenger import views
app_name = 'messenger'
urlpatterns = [
path('', views.messengerListView.as_view(), name='messengers_list'),
path('new/', views.AddmessengerCreateView.as_view(), name='add_messenger'),
path('viber/new/', views.AddmessengerViberCreateView.as_view(), name='add_viber_messenger'),
path('viber/<slug:slug>/update/', views.UpdateVibermessengerUpdateView.as_view(), name='update_viber_messenger'),
path('viber/<slug:slug>/delete/', views.RemoveVibermessengerDeleteView.as_view(), name='delete_viber_messenger'),
path('viber/<slug:slug>/listen/', csrf_exempt(views.ListenViberView.as_view()), name='listen_viber_bot'),
path('viber/<slug:slug>/set_webhook/', views.SetWebhook.as_view(), name='webhook_viber_bot'),
]

161
messenger/views.py

@ -0,0 +1,161 @@
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import HttpResponseForbidden, HttpResponse, HttpResponseNotFound
from django.shortcuts import resolve_url
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _, gettext
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, FormView, View
from django.views.generic.detail import SingleObjectMixin
from viberbot.api.messages import KeyboardMessage, ContactMessage
from viberbot.api.user_profile import UserProfile as ViberUserProfile
from viberbot.api.viber_requests import ViberMessageRequest, ViberSubscribedRequest, ViberFailedRequest, \
ViberUnsubscribedRequest
from accounts_app.models import UserProfile
from djing.lib.mixins import LoginAdminPermissionMixin, LoginAdminMixin
from messenger import forms, models
from messenger.models import ViberMessage, ViberSubscriber
class messengerListView(LoginAdminPermissionMixin, ListView):
model = models.Messenger
permission_required = 'messenger.view_messenger'
class AddmessengerCreateView(LoginAdminMixin, FormView):
template_name = 'messenger/add_messenger.html'
form_class = forms.MessengerForm
def form_valid(self, form):
bot_type = form.cleaned_data.get('bot_type')
if isinstance(bot_type, int) and bot_type > 0:
if bot_type == 1:
self.success_url = resolve_url('messenger:add_viber_messenger')
return super().form_valid(form)
messages.info(self.request, _('Unexpected bot type'))
self.success_url = resolve_url('messenger:messengers_list')
return super().form_valid(form)
class AddmessengerViberCreateView(LoginAdminMixin, PermissionRequiredMixin, CreateView):
model = models.ViberMessenger
form_class = forms.MessengerViberForm
permission_required = 'messenger.add_vibermessenger'
success_url = reverse_lazy('messenger:messengers_list')
def form_valid(self, form):
r = super().form_valid(form)
messages.success(self.request, _('New viber messenger successfully created'))
return r
class UpdateVibermessengerUpdateView(LoginAdminPermissionMixin, UpdateView):
model = models.ViberMessenger
form_class = forms.MessengerViberForm
permission_required = 'messenger.change_vibermessenger'
def form_valid(self, form):
r = super().form_valid(form)
messages.success(self.request, _('Viber messenger successfully updated'))
return r
class RemoveVibermessengerDeleteView(LoginAdminPermissionMixin, DeleteView):
model = models.ViberMessenger
permission_required = 'messenger.delete_vibermessenger'
success_url = reverse_lazy('messenger:messengers_list')
def delete(self, request, *args, **kwargs):
r = super().delete(request, *args, **kwargs)
messages.success(request, _('Viber messenger successfully deleted'))
return r
@method_decorator(csrf_exempt, name='post')
class ListenViberView(SingleObjectMixin, View):
http_method_names = 'post',
model = models.ViberMessenger
def post(self, request, *args, **kwargs):
obj = self.get_object()
if not obj:
return HttpResponseNotFound()
self.object = obj
viber = obj.get_viber()
if not viber.verify_signature(request.body, request.META.get('HTTP_X_VIBER_CONTENT_SIGNATURE')):
return HttpResponseForbidden()
# this library supplies a simple way to receive a request object
vr = viber.parse_request(request.body)
if isinstance(vr, ViberMessageRequest):
in_msg = vr.message
if isinstance(in_msg, ContactMessage):
self.inbox_contact(in_msg, vr.sender)
subscriber, created = self.make_subscriber(vr.sender)
if not created:
ViberMessage.objects.create(
msg=vr.message,
sender=vr.sender.id,
messenger=obj,
subscriber=subscriber
)
elif isinstance(vr, ViberSubscribedRequest):
self.make_subscriber(vr.user)
elif isinstance(vr, ViberFailedRequest):
print("client failed receiving message. failure: {0}".format(vr))
elif isinstance(vr, ViberUnsubscribedRequest):
ViberSubscriber.objects.filter(
uid=vr.user_id
).delete()
return HttpResponse(status=200)
def make_subscriber(self, viber_user_profile: ViberUserProfile):
subscriber, created = ViberSubscriber.objects.get_or_create(
uid=viber_user_profile.id,
defaults={
'name': viber_user_profile.name,
'avatar': viber_user_profile.avatar
}
)
if created and hasattr(self, 'object'):
msg = KeyboardMessage(keyboard={
'Type': 'keyboard',
'DefaultHeight': True,
'Buttons': ({
'ActionType': 'share-phone',
'ActionBody': 'reply to me',
"Text": gettext('My telephone number'),
"TextSize": "medium"
},)
}, min_api_version=3)
viber = self.object.get_viber()
viber.send_messages(viber_user_profile.id, msg)
return subscriber, created
def inbox_contact(self, msg, sender: ViberUserProfile):
tel = msg.contact.phone_number
accs = UserProfile.objects.filter(telephone__icontains=tel)
viber = self.object.get_viber()
if accs.exists():
subs = ViberSubscriber.objects.filter(uid=sender.id)
if subs.exists():
subs.update(account=accs.first())
viber.send_messages(sender.id, gettext(
'Your account is attached. Now you will be receive notifications from billing'
))
else:
viber.send_messages(sender.id, gettext('Telephone not found, please specify telephone number in account in billing'))
class SetWebhook(LoginAdminMixin, SingleObjectMixin, View):
http_method_names = 'get',
model = models.ViberMessenger
def get(self, request, *args, **kwargs):
obj = self.get_object()
if not obj:
return HttpResponseNotFound
obj.send_webhook()
return HttpResponse(b'ok', status=200)

34
msg_app/models.py

@ -2,7 +2,6 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from accounts_app.models import UserProfile from accounts_app.models import UserProfile
from djing.tasks import send_email_notify from djing.tasks import send_email_notify
from chatbot.models import ChatException
class MessageError(Exception): class MessageError(Exception):
@ -203,24 +202,21 @@ class Conversation(models.Model):
return messages[0] return messages[0]
def new_message(self, text, attachment, author, with_status=True): def new_message(self, text, attachment, author, with_status=True):
try:
msg = Message.objects.create(
text=text, conversation=self,
attachment=attachment, author=author
)
if with_status:
for participant in self.participants.filter(is_active=True):
if participant == author:
continue
MessageStatus.objects.create(msg=msg, user=participant)
if participant.flags.notify_msg:
send_email_notify.delay(
msg_text=text,
account_id=participant.pk
)
return msg
except ChatException as e:
raise MessageError(e)
msg = Message.objects.create(
text=text, conversation=self,
attachment=attachment, author=author
)
if with_status:
for participant in self.participants.filter(is_active=True):
if participant == author:
continue
MessageStatus.objects.create(msg=msg, user=participant)
if participant.flags.notify_msg:
send_email_notify.delay(
msg_text=text,
account_id=participant.pk
)
return msg
@staticmethod @staticmethod
def remove_message(msg): def remove_message(msg):

3
msg_app/urls.py

@ -7,6 +7,5 @@ urlpatterns = [
path('', views.ConversationsListView.as_view(), name='home'), path('', views.ConversationsListView.as_view(), name='home'),
path('new/', views.new_conversation, name='new_conversation'), path('new/', views.new_conversation, name='new_conversation'),
path('<int:conv_id>/', views.to_conversation, name='to_conversation'), path('<int:conv_id>/', views.to_conversation, name='to_conversation'),
path('<int:conv_id>/<int:msg_id>/del/', views.remove_msg, name='remove_msg'),
path('check_news/', views.check_news, name='check_news')
path('<int:conv_id>/<int:msg_id>/del/', views.remove_msg, name='remove_msg')
] ]

22
msg_app/views.py

@ -1,15 +1,12 @@
from json import dumps
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib import messages from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import ListView from django.views.generic import ListView
from chatbot.models import MessageQueue
from djing.lib.decorators import only_admins from djing.lib.decorators import only_admins
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
@ -87,22 +84,3 @@ def remove_msg(request, conv_id, msg_id):
conversation_id = msg.conversation.pk conversation_id = msg.conversation.pk
msg.delete() msg.delete()
return redirect('msg_app:to_conversation', conversation_id) return redirect('msg_app:to_conversation', conversation_id)
@login_required
@only_admins
def check_news(request):
if request.user.is_authenticated:
msg = MessageQueue.objects.pop(user=request.user, tag='msgapp')
if msg is None:
r = {'auth': True, 'exist': False}
else:
r = {
'auth': True,
'exist': True,
'content': msg,
'title': "%s" % _('Message')
}
else:
r = {'auth': False}
return HttpResponse(dumps(r))

7
requirements.txt

@ -1,7 +1,6 @@
urllib3 urllib3
Django>=2 Django>=2
Pillow Pillow
telepot
# for mac address field # for mac address field
netaddr netaddr
@ -31,6 +30,12 @@ asterisk
# django-bitfield # django-bitfield
-e git://github.com/disqus/django-bitfield.git#egg=django-bitfield -e git://github.com/disqus/django-bitfield.git#egg=django-bitfield
# django_cleanup for clean unused media
-e git://github.com/un1t/django-cleanup.git#egg=django_cleanup
# viberbot
-e git://github.com/Viber/viber-bot-python.git#egg=viberbot
Celery Celery
redis==2.10.6 redis==2.10.6
celery[redis] celery[redis]

4
static/js/my.js

@ -305,8 +305,4 @@ $(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({container:'body'}); $('[data-toggle="tooltip"]').tooltip({container:'body'});
$('.btn_ajloader').ajloader({'dst_block': '#id_block_devices'}); $('.btn_ajloader').ajloader({'dst_block': '#id_block_devices'});
$(document).notifys({news_url: '/tasks/check_news', check_interval: 50});
$(document).notifys({news_url: '/msg/check_news', check_interval: 55});
}); });

15
systemd_units/djing_telebot.service

@ -1,15 +0,0 @@
[Unit]
Description=Djing telegram bot
[Service]
Type=simple
ExecStart=/usr/bin/python3 ./telebot.py
PIDFile=/run/djing_telebot.pid
WorkingDirectory=/var/www/djing
TimeoutSec=9
Restart=always
User=www-data
Group=www-data
[Install]
WantedBy=multi-user.target

55
taskapp/handle.py

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from djing.tasks import send_email_notify
from chatbot.models import ChatException
from djing.lib import MultipleException
from djing.tasks import send_email_notify, multicast_email_notify
from messenger.tasks import multicast_viber_notify, send_viber_message
class TaskException(Exception): class TaskException(Exception):
@ -11,31 +10,31 @@ class TaskException(Exception):
def handle(task, author, recipients): def handle(task, author, recipients):
errors = []
profile_ids = []
for recipient in recipients: for recipient in recipients:
if not recipient.flags.notify_task: if not recipient.flags.notify_task:
continue continue
try:
task_status = _('Task')
# If signal to myself then quietly
if author == recipient:
return
# If task completed or failed
elif task.state == 'F' or task.state == 'C':
task_status = _('Task completed')
fulltext = render_to_string('taskapp/notification.html', {
'task': task,
'abon': task.abon,
'task_status': task_status
})
if task.state == 'F' or task.state == 'C':
# If task completed or failed than send one message to author
send_email_notify.delay(fulltext, author.pk)
else:
send_email_notify.delay(fulltext, recipient.pk)
except ChatException as e:
errors.append(e)
if len(errors) > 0:
raise MultipleException(errors)
# If signal to myself then quietly
if author == recipient:
return
profile_ids.append(recipient.pk)
task_status = _('Task')
# If task completed or failed
if task.state == 'F' or task.state == 'C':
task_status = _('Task completed')
fulltext = render_to_string('taskapp/notification.html', {
'task': task,
'abon': task.abon,
'task_status': task_status
})
if task.state == 'F' or task.state == 'C':
# If task completed or failed than send one message to author
send_email_notify.delay(fulltext, author.pk)
send_viber_message.delay(None, author.pk, fulltext)
else:
multicast_email_notify.delay(fulltext, profile_ids)
multicast_viber_notify.delay(None, profile_ids, fulltext)

3
taskapp/urls.py

@ -21,6 +21,5 @@ urlpatterns = [
path('my/', views.MyTaskListView.as_view(), name='my_tasks'), path('my/', views.MyTaskListView.as_view(), name='my_tasks'),
path('all/', views.AllTasksListView.as_view(), name='all_tasks'), path('all/', views.AllTasksListView.as_view(), name='all_tasks'),
path('all_new/', views.AllNewTasksListView.as_view(), name='all_new_tasks'), path('all_new/', views.AllNewTasksListView.as_view(), name='all_new_tasks'),
path('empty/', views.EmptyTasksListView.as_view(), name='empty_tasks'),
path('check_news/', views.check_news, name='check_news')
path('empty/', views.EmptyTasksListView.as_view(), name='empty_tasks')
] ]

21
taskapp/views.py

@ -11,11 +11,10 @@ from django.conf import settings
from django.views.generic.edit import FormMixin, DeleteView, UpdateView from django.views.generic.edit import FormMixin, DeleteView, UpdateView
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from chatbot.models import MessageQueue
from abonapp.models import Abon from abonapp.models import Abon
from djing import httpresponse_to_referrer from djing import httpresponse_to_referrer
from djing.lib import safe_int, MultipleException, RuTimedelta from djing.lib import safe_int, MultipleException, RuTimedelta
from djing.lib.decorators import only_admins, json_view
from djing.lib.decorators import only_admins
from djing.lib.mixins import LoginAdminMixin, LoginAdminPermissionMixin from djing.lib.mixins import LoginAdminMixin, LoginAdminPermissionMixin
from .handle import TaskException from .handle import TaskException
from .models import Task, ExtraComment from .models import Task, ExtraComment
@ -272,24 +271,6 @@ def remind(request, task_id):
return redirect('taskapp:home') return redirect('taskapp:home')
@json_view
def check_news(request):
if request.user.is_authenticated and request.user.is_admin:
msg = MessageQueue.objects.pop(user=request.user, tag='taskap')
if msg is not None:
r = {
'auth': True,
'exist': True,
'content': msg,
'title': _('Task')
}
else:
r = {'auth': True, 'exist': False}
else:
r = {'auth': False}
return r
class NewCommentView(LoginAdminMixin, LoginRequiredMixin, CreateView): class NewCommentView(LoginAdminMixin, LoginRequiredMixin, CreateView):
form_class = ExtraCommentForm form_class = ExtraCommentForm
model = ExtraComment model = ExtraComment

29
telebot.py

@ -1,29 +0,0 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
from pid.decorator import pidfile
import django
from telepot import DelegatorBot
from telepot.exception import BadHTTPResponse
from telepot.delegate import per_chat_id, create_open, pave_event_space
@pidfile(pidname='djing_telebot.pid', piddir='/run')
def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
django.setup()
from chatbot.telebot import token, DjingTelebot
while True:
try:
bot = DelegatorBot(token, [
pave_event_space()(
per_chat_id(), create_open, DjingTelebot, timeout=300
),
])
bot.message_loop(run_forever='Listening ...')
except BadHTTPResponse as e:
print(e)
if __name__ == '__main__':
main()

9
templates/base.html

@ -113,6 +113,15 @@
</li> </li>
{% endif %} {% endif %}
{% if perms.gw_app.view_nasmodel %}
{% url 'messenger:messengers_list' as mesngrhome %}
<li{% if mesngrhome in request.path %} class="active"{% endif %}>
<a href="{{ mesngrhome }}">
<span class="glyphicon glyphicon-comment"></span> {% trans 'Messengers' %}
</a>
</li>
{% endif %}
</ul> </ul>
</div> </div>

Loading…
Cancel
Save