41 changed files with 886 additions and 610 deletions
-
6chatbot/admin.py
-
5chatbot/apps.py
-
112chatbot/locale/ru/LC_MESSAGES/django.po
-
64chatbot/migrations/0001_initial.py
-
27chatbot/migrations/0002_auto_20180808_1236.py
-
0chatbot/migrations/__init__.py
-
73chatbot/models.py
-
149chatbot/telebot.py
-
3chatbot/views.py
-
2devapp/locale/ru/LC_MESSAGES/django.po
-
16devapp/views.py
-
20djing/lib/decorators.py
-
3djing/local_settings.py.template
-
7djing/settings.py
-
1djing/urls.py
-
1messenger/__init__.py
-
7messenger/admin.py
-
5messenger/apps.py
-
28messenger/forms.py
-
190messenger/locale/ru/LC_MESSAGES/django.po
-
88messenger/migrations/0001_initial.py
-
0messenger/migrations/__init__.py
-
129messenger/models.py
-
56messenger/tasks.py
-
15messenger/templates/messenger/add_messenger.html
-
57messenger/templates/messenger/messenger_list.html
-
49messenger/templates/messenger/vibermessenger_form.html
-
3messenger/tests.py
-
17messenger/urls.py
-
161messenger/views.py
-
34msg_app/models.py
-
3msg_app/urls.py
-
22msg_app/views.py
-
7requirements.txt
-
4static/js/my.js
-
15systemd_units/djing_telebot.service
-
55taskapp/handle.py
-
3taskapp/urls.py
-
21taskapp/views.py
-
29telebot.py
-
9templates/base.html
@ -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) |
|
||||
@ -1,5 +0,0 @@ |
|||||
from django.apps import AppConfig |
|
||||
|
|
||||
|
|
||||
class ChatbotConfig(AppConfig): |
|
||||
name = 'chatbot' |
|
||||
@ -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 не подписан на оповещения" |
|
||||
@ -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', |
|
||||
}, |
|
||||
), |
|
||||
] |
|
||||
@ -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'}, |
|
||||
), |
|
||||
] |
|
||||
@ -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',) |
|
||||
@ -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) |
|
||||
@ -1,3 +0,0 @@ |
|||||
from django.shortcuts import render |
|
||||
|
|
||||
# Create your views here. |
|
||||
@ -0,0 +1 @@ |
|||||
|
default_app_config = 'messenger.apps.messengerConfig' |
||||
@ -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) |
||||
@ -0,0 +1,5 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class messengerConfig(AppConfig): |
||||
|
name = 'messenger' |
||||
@ -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__' |
||||
@ -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 "Ваша учётка из биллинга привязана. Теперь вы будете получать оповещения из биллинга." |
||||
@ -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,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',) |
||||
@ -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) |
||||
@ -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">×</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> |
||||
@ -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 %} |
||||
@ -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 %} |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
||||
@ -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'), |
||||
|
] |
||||
@ -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) |
||||
@ -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 |
|
||||
@ -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() |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue