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. 4
      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. 23
      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_for_zte.html:22
msgid "Attached user"
msgstr "Прикрепленный абонент"
msgstr "Прикреплённый абонент"
#: templates/devapp/custom_dev_page/onu.html:48
#: 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 accounts_app.models import UserProfile
from chatbot.models import ChatException
from devapp.base_intr import DeviceImplementationError
from django.conf import settings
from django.contrib import messages
@ -27,8 +26,8 @@ from djing.lib.tln import ZteOltConsoleError, OnuZteRegisterError, \
from djing.tasks import multicast_email_notify
from easysnmp import EasySNMPTimeoutError, EasySNMPError
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 .forms import DeviceForm, PortForm, DeviceExtraDataForm
from .models import Device, Port, DeviceDBException, DeviceMonitoringException
@ -709,19 +708,20 @@ class OnDeviceMonitoringEvent(global_base_views.SecureApiView):
recipients = UserProfile.objects.get_profiles_by_group(
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_down.ip_address,
device_down.mac_addr,
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 {
'text': 'notification successfully sent'
}
except ChatException as e:
except ValueError as e:
return {
'text': str(e)
}

20
djing/lib/decorators.py

@ -1,29 +1,11 @@
from functools import wraps
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponseForbidden, JsonResponse
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import redirect
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
def only_admins(fn):
@wraps(fn)

3
djing/local_settings.py.template

@ -69,3 +69,6 @@ EMAIL_HOST = 'smtp.mailserver.com'
EMAIL_PORT = 587
EMAIL_HOST_PASSWORD = 'password'
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.messages',
'django.contrib.staticfiles',
'django_cleanup.apps.CleanupConfig',
'ip_pool',
'accounts_app',
'gw_app',
@ -52,7 +53,7 @@ INSTALLED_APPS = [
'statistics',
'taskapp',
'clientsideapp',
'chatbot',
'messenger',
'msg_app',
'dialing_app',
'group_app',
@ -234,3 +235,7 @@ REDIS_PORT = '6379'
BROKER_URL = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0'
BROKER_TRANSPORT_OPTIONS = {'visibility_timeout': 3600}
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('groups/', include('group_app.urls', namespace='group_app')),
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'))
# 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)

4
msg_app/models.py

@ -2,7 +2,6 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from accounts_app.models import UserProfile
from djing.tasks import send_email_notify
from chatbot.models import ChatException
class MessageError(Exception):
@ -203,7 +202,6 @@ class Conversation(models.Model):
return messages[0]
def new_message(self, text, attachment, author, with_status=True):
try:
msg = Message.objects.create(
text=text, conversation=self,
attachment=attachment, author=author
@ -219,8 +217,6 @@ class Conversation(models.Model):
account_id=participant.pk
)
return msg
except ChatException as e:
raise MessageError(e)
@staticmethod
def remove_message(msg):

3
msg_app/urls.py

@ -7,6 +7,5 @@ urlpatterns = [
path('', views.ConversationsListView.as_view(), name='home'),
path('new/', views.new_conversation, name='new_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.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import ListView
from chatbot.models import MessageQueue
from djing.lib.decorators import only_admins
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
msg.delete()
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
Django>=2
Pillow
telepot
# for mac address field
netaddr
@ -31,6 +30,12 @@ asterisk
# 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
redis==2.10.6
celery[redis]

4
static/js/my.js

@ -305,8 +305,4 @@ $(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({container:'body'});
$('.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

23
taskapp/handle.py

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
from django.template.loader import render_to_string
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):
@ -11,17 +10,19 @@ class TaskException(Exception):
def handle(task, author, recipients):
errors = []
profile_ids = []
for recipient in recipients:
if not recipient.flags.notify_task:
continue
try:
task_status = _('Task')
# If signal to myself then quietly
if author == recipient:
return
profile_ids.append(recipient.pk)
task_status = _('Task')
# If task completed or failed
elif task.state == 'F' or task.state == 'C':
if task.state == 'F' or task.state == 'C':
task_status = _('Task completed')
fulltext = render_to_string('taskapp/notification.html', {
@ -33,9 +34,7 @@ def handle(task, author, recipients):
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:
send_email_notify.delay(fulltext, recipient.pk)
except ChatException as e:
errors.append(e)
if len(errors) > 0:
raise MultipleException(errors)
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('all/', views.AllTasksListView.as_view(), name='all_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 guardian.shortcuts import assign_perm
from chatbot.models import MessageQueue
from abonapp.models import Abon
from djing import httpresponse_to_referrer
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 .handle import TaskException
from .models import Task, ExtraComment
@ -272,24 +271,6 @@ def remind(request, task_id):
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):
form_class = ExtraCommentForm
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>
{% 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>
</div>

Loading…
Cancel
Save