implement bots
This commit is contained in:
0
bots/__init__.py
Normal file
0
bots/__init__.py
Normal file
3
bots/admin.py
Normal file
3
bots/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
5
bots/apps.py
Normal file
5
bots/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BotsConfig(AppConfig):
|
||||
name = 'bots'
|
19
bots/forms.py
Normal file
19
bots/forms.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from django.forms import ModelForm
|
||||
|
||||
from bots.models import TelegramBot
|
||||
|
||||
|
||||
class BotForm(ModelForm):
|
||||
prefix = 'bot'
|
||||
|
||||
def __init__(self, *args, module, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not hasattr(module, 'rpc_dispatcher'):
|
||||
self.fields.pop('rpc_name')
|
||||
if not hasattr(module, 'periodic_task'):
|
||||
self.fields.pop('periodic_interval')
|
||||
self.fields.pop('periodic_last_run')
|
||||
|
||||
class Meta:
|
||||
model = TelegramBot
|
||||
exclude = 'owner', 'config_type', 'config_id',
|
0
bots/management/__init__.py
Normal file
0
bots/management/__init__.py
Normal file
0
bots/management/commands/__init__.py
Normal file
0
bots/management/commands/__init__.py
Normal file
53
bots/management/commands/run_bots.py
Normal file
53
bots/management/commands/run_bots.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import sentry_sdk
|
||||
from django.core.cache import cache
|
||||
from django.core.management import BaseCommand
|
||||
from telegram import TelegramError
|
||||
from telegram.error import TimedOut
|
||||
|
||||
from bots.models import TelegramBot
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
dispatchers = []
|
||||
while True:
|
||||
try:
|
||||
if not dispatchers or cache.get('bots_reset'):
|
||||
logging.warning('Reloading dispatchers')
|
||||
dispatchers = []
|
||||
for bot in TelegramBot.objects.filter(active=True):
|
||||
try:
|
||||
dispatcher = bot.build_dispatcher()
|
||||
dispatcher.last_update_id = 0
|
||||
dispatchers.append(dispatcher)
|
||||
except TelegramError:
|
||||
pass
|
||||
cache.delete('bots_reset')
|
||||
|
||||
for dispatcher in dispatchers:
|
||||
try:
|
||||
updates = dispatcher.bot.get_updates(dispatcher.last_update_id)
|
||||
except TimedOut:
|
||||
continue
|
||||
except TelegramError as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
traceback.print_exc()
|
||||
updates = []
|
||||
|
||||
for update in updates:
|
||||
try:
|
||||
dispatcher.process_update(update)
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
except Exception as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
traceback.print_exc()
|
||||
dispatcher.last_update_id = update.update_id + 1
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
except Exception as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
traceback.print_exc()
|
33
bots/migrations/0001_initial.py
Normal file
33
bots/migrations/0001_initial.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-12 15:15
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TelegramBot',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=32)),
|
||||
('bot_token', models.CharField(max_length=256)),
|
||||
('config_id', models.PositiveIntegerField()),
|
||||
('config_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='telegrambot',
|
||||
unique_together={('config_type', 'config_id')},
|
||||
),
|
||||
]
|
23
bots/migrations/0002_echobotmoduleconfig.py
Normal file
23
bots/migrations/0002_echobotmoduleconfig.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-12 15:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bots', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EchoBotModuleConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('prefix', models.TextField()),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
18
bots/migrations/0003_telegrambot_active.py
Normal file
18
bots/migrations/0003_telegrambot_active.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-12 15:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bots', '0002_echobotmoduleconfig'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='telegrambot',
|
||||
name='active',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
28
bots/migrations/0004_auto_20191112_1908.py
Normal file
28
bots/migrations/0004_auto_20191112_1908.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-12 16:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bots', '0003_telegrambot_active'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='telegrambot',
|
||||
name='periodic_interval',
|
||||
field=models.DurationField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='telegrambot',
|
||||
name='periodic_last_run',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='telegrambot',
|
||||
name='rpc_name',
|
||||
field=models.CharField(blank=True, max_length=32, null=True),
|
||||
),
|
||||
]
|
23
bots/migrations/0005_channelhelperbotmoduleconfig.py
Normal file
23
bots/migrations/0005_channelhelperbotmoduleconfig.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-12 16:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bots', '0004_auto_20191112_1908'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ChannelHelperBotModuleConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('chat_id', models.CharField(max_length=32)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
0
bots/migrations/__init__.py
Normal file
0
bots/migrations/__init__.py
Normal file
57
bots/models.py
Normal file
57
bots/models.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from telegram import Bot
|
||||
from telegram.ext import Dispatcher
|
||||
|
||||
|
||||
class TelegramBot(models.Model):
|
||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
active = models.BooleanField(default=True)
|
||||
title = models.CharField(max_length=32)
|
||||
bot_token = models.CharField(max_length=256)
|
||||
|
||||
rpc_name = models.CharField(max_length=32, blank=True, null=True)
|
||||
periodic_interval = models.DurationField(blank=True, null=True)
|
||||
periodic_last_run = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
config_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
config_id = models.PositiveIntegerField()
|
||||
config = GenericForeignKey('config_type', 'config_id')
|
||||
|
||||
def get_bot(self):
|
||||
return Bot(self.bot_token)
|
||||
|
||||
def build_dispatcher(self):
|
||||
bot = self.get_bot()
|
||||
bot.get_me()
|
||||
dispatcher = Dispatcher(bot, None, workers=0, use_context=True)
|
||||
self.config.build_dispatcher(dispatcher)
|
||||
return dispatcher
|
||||
|
||||
def __str__(self):
|
||||
return f'#{self.pk} {self.title}'
|
||||
|
||||
class Meta:
|
||||
unique_together = ('config_type', 'config_id')
|
||||
|
||||
|
||||
class TelegramBotModuleConfig(models.Model):
|
||||
_bot = GenericRelation(TelegramBot, content_type_field='config_type', object_id_field='config_id')
|
||||
|
||||
MODULE_NAME = '<Not set>'
|
||||
|
||||
@property
|
||||
def bot(self):
|
||||
return self._bot.get()
|
||||
|
||||
@property
|
||||
def content_type(self):
|
||||
return ContentType.objects.get_for_model(self.__class__)
|
||||
|
||||
def build_dispatcher(self, dispatcher: Dispatcher):
|
||||
raise NotImplementedError()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
4
bots/modules/__init__.py
Normal file
4
bots/modules/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .channel_helper import ChannelHelperBotModuleConfig
|
||||
from .echo import EchoBotModuleConfig
|
||||
|
||||
BOT_MODULES = [EchoBotModuleConfig, ChannelHelperBotModuleConfig]
|
87
bots/modules/channel_helper.py
Normal file
87
bots/modules/channel_helper.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import base64
|
||||
import os
|
||||
import tempfile
|
||||
from io import BytesIO
|
||||
from uuid import uuid4
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from django.db import models
|
||||
from telegram import Update
|
||||
from telegram.ext import Dispatcher, CallbackContext, MessageHandler, Filters
|
||||
from jsonrpc import Dispatcher as RPCDispatcher
|
||||
|
||||
from bots.models import TelegramBotModuleConfig
|
||||
|
||||
|
||||
class ChannelHelperBotModuleConfig(TelegramBotModuleConfig):
|
||||
chat_id = models.CharField(max_length=32)
|
||||
|
||||
MODULE_NAME = 'Channel helper'
|
||||
|
||||
rpc_dispatcher = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.rpc_dispatcher = RPCDispatcher()
|
||||
self.rpc_dispatcher['post_photo'] = self.rpc_post_photo
|
||||
|
||||
def rpc_post_photo(self, photo, is_base64=False):
|
||||
try:
|
||||
if is_base64:
|
||||
f = BytesIO(base64.b64decode(photo))
|
||||
else:
|
||||
resp = requests.get(photo)
|
||||
resp.raise_for_status()
|
||||
f = BytesIO(resp.content)
|
||||
except:
|
||||
raise RuntimeError('Could not load image')
|
||||
im = Image.open(f) # type: Image.Image
|
||||
width, height = im.size
|
||||
if width > 2000 or height > 2000:
|
||||
im.thumbnail((2000, 2000))
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
fpath = os.path.join(d, '{}.jpg'.format(uuid4()))
|
||||
im.save(fpath)
|
||||
self.bot.get_bot().send_photo(self.chat_id, open(fpath, 'rb'))
|
||||
return True
|
||||
|
||||
def handle_message(self, update: Update, ctx: CallbackContext):
|
||||
m = update.effective_message
|
||||
bot = ctx.bot
|
||||
if hasattr(m, 'audio') and m.audio:
|
||||
a = m.audio
|
||||
r = bot.send_audio(self.chat_id, a.file_id, a.duration, a.performer, a.title)
|
||||
elif hasattr(m, 'document') and m.document:
|
||||
d = m.document
|
||||
r = bot.send_document(self.chat_id, d.file_id, d.file_name)
|
||||
elif hasattr(m, 'photo') and m.photo:
|
||||
p = m.photo
|
||||
r = bot.send_photo(self.chat_id, p[-1].file_id)
|
||||
elif hasattr(m, 'sticker') and m.sticker:
|
||||
s = m.sticker
|
||||
r = bot.send_sticker(self.chat_id, s.file_id)
|
||||
elif hasattr(m, 'video') and m.video:
|
||||
v = m.video
|
||||
r = bot.send_video(self.chat_id, v.file_id, v.duration)
|
||||
elif hasattr(m, 'voice') and m.voice:
|
||||
v = m.voice
|
||||
r = bot.send_voice(self.chat_id, v.file_id, v.duration)
|
||||
elif hasattr(m, 'video_note') and m.video_note:
|
||||
vn = m.video_note
|
||||
r = bot.send_video_note(self.chat_id, vn.file_id, vn.duration, vn.length)
|
||||
elif hasattr(m, 'contact') and m.contact:
|
||||
c = m.contact
|
||||
r = bot.send_contact(self.chat_id, c.phone_number, c.first_name, c.last_name)
|
||||
elif hasattr(m, 'location') and m.location:
|
||||
l = m.location
|
||||
r = bot.send_location(self.chat_id, l.latitude, l.longitude)
|
||||
elif hasattr(m, 'venue') and m.venue:
|
||||
v = m.venue
|
||||
r = bot.send_venue(self.chat_id, v.location.latitude, v.location.longitude, v.title, v.address, v.foursquare_id)
|
||||
elif hasattr(m, 'text') and m.text:
|
||||
r = bot.send_message(self.chat_id, m.text_html, 'html')
|
||||
|
||||
def build_dispatcher(self, dispatcher: Dispatcher):
|
||||
dispatcher.add_handler(MessageHandler(Filters.all, self.handle_message))
|
||||
return dispatcher
|
18
bots/modules/echo.py
Normal file
18
bots/modules/echo.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django.db import models
|
||||
from telegram import Update
|
||||
from telegram.ext import Dispatcher, CallbackContext, MessageHandler, Filters
|
||||
|
||||
from bots.models import TelegramBotModuleConfig
|
||||
|
||||
|
||||
class EchoBotModuleConfig(TelegramBotModuleConfig):
|
||||
prefix = models.TextField()
|
||||
|
||||
MODULE_NAME = 'Echo'
|
||||
|
||||
def message_handler(self, update: Update, ctx: CallbackContext):
|
||||
update.effective_message.reply_text(self.prefix + update.effective_message.text)
|
||||
|
||||
def build_dispatcher(self, dispatcher: Dispatcher):
|
||||
dispatcher.add_handler(MessageHandler(Filters.text, self.message_handler))
|
||||
return dispatcher
|
55
bots/templates/cabinet/bots/bot_form.html
Normal file
55
bots/templates/cabinet/bots/bot_form.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{% extends 'cabinet/_internal_base.html' %}
|
||||
{% load bootstrap4 %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'cabinet:bots:index' %}">Bot list</a></li>
|
||||
<li><span>{{ title }}</span></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="" method="post" class="card">
|
||||
{% csrf_token %}
|
||||
<header class="card-header">
|
||||
<h2 class="card-title">{% if feed %}Bot "{{ feed.title }}" configuration{% else %}New bot{% endif %}</h2>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<h4>General options</h4>
|
||||
{% bootstrap_form bot_form layout='horizontal' %}
|
||||
<hr>
|
||||
<h4>Module options</h4>
|
||||
{% bootstrap_form config_form layout='horizontal' %}
|
||||
</div>
|
||||
<footer class="card-footer text-right">
|
||||
{% if feed %}<a href="#delete-modal" class="modal-basic btn btn-danger">Delete</a>{% endif %}
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
{% if feed %}
|
||||
<div id="delete-modal" class="modal-block modal-full-color modal-block-danger mfp-hide">
|
||||
<section class="card">
|
||||
<header class="card-header">
|
||||
<h2 class="card-title">Delete bot "{{ feed.title }}"</h2>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-icon"><i class="fas fa-times-circle"></i></div>
|
||||
<div class="modal-text">
|
||||
<h4>Are you sure?</h4>
|
||||
<p>This action cannot be undone.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<div class="row">
|
||||
<form action="{% url 'cabinet:bots:delete' pk=feed.pk %}" method="post" class="col-md-12 text-right">
|
||||
{% csrf_token %}
|
||||
<button type="button" class="btn btn-default modal-dismiss">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
48
bots/templates/cabinet/bots/bot_list.html
Normal file
48
bots/templates/cabinet/bots/bot_list.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends 'cabinet/_internal_base.html' %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><span>Bot list</span></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-button-actions">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
Add new bot
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
{% for module in bot_modules %}
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'cabinet:bots:new' content_type=module.content_type.model %}">{{ module.MODULE_NAME }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="card-title">Your bots</h2>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover table-responsive-md mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Module</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for bot in object_list %}
|
||||
<tr class="clickable-row" data-href="{% url 'cabinet:bots:edit' pk=bot.pk %}">
|
||||
<td>{{ bot.title }}</td>
|
||||
<td>{{ bot.config.MODULE_NAME }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4">No bots added</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
3
bots/tests.py
Normal file
3
bots/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
12
bots/urls.py
Normal file
12
bots/urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.urls import path
|
||||
|
||||
from bots.views import BotListView, BotConfigEditView, BotConfigDeleteView, BotConfigCreateView
|
||||
|
||||
app_name = 'bots'
|
||||
urlpatterns = [
|
||||
path('', BotListView.as_view(), name='index'),
|
||||
path('<int:pk>/', BotConfigEditView.as_view(), name='edit'),
|
||||
path('new/<content_type>/', BotConfigCreateView.as_view(), name='new'),
|
||||
path('delete/<int:pk>/', BotConfigDeleteView.as_view(), name='delete'),
|
||||
]
|
||||
|
39
bots/utils.py
Normal file
39
bots/utils.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from bots.forms import BotForm
|
||||
from bots.models import TelegramBot
|
||||
from cabinet.utils import CabinetViewMixin
|
||||
from config.utils import get_config_form
|
||||
|
||||
|
||||
class BaseBotConfigView(CabinetViewMixin, TemplateView):
|
||||
template_name = 'cabinet/bots/bot_form.html'
|
||||
context_object_name = 'feed'
|
||||
|
||||
def get_queryset(self):
|
||||
return TelegramBot.objects.filter(owner=self.request.user)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
bot_form, config_form = self.get_forms()
|
||||
if bot_form.is_valid() and config_form.is_valid():
|
||||
return self.form_valid(bot_form, config_form)
|
||||
else:
|
||||
context = self.get_context_data(forms=(bot_form, config_form))
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_forms(self):
|
||||
bot = self.get_object()
|
||||
data = self.request.POST if self.request.method == 'POST' else None
|
||||
return BotForm(data=data, instance=bot, module=self.get_content_type().model_class()), \
|
||||
get_config_form(self.get_content_type().model_class())(data=data, instance=bot.config if bot else None)
|
||||
|
||||
def get_context_data(self, forms=None, **kwargs):
|
||||
ctx = super(BaseBotConfigView, self).get_context_data(**kwargs)
|
||||
ctx['bot_form'], ctx['config_form'] = self.get_forms() if forms is None else forms
|
||||
ctx['bot_module'] = self.get_content_type().model_class()
|
||||
return ctx
|
96
bots/views.py
Normal file
96
bots/views.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from braces.views import AjaxResponseMixin, JSONResponseMixin
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect, HttpRequest
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from jsonrpc import JSONRPCResponseManager
|
||||
|
||||
from bots.models import TelegramBot
|
||||
from bots.modules import BOT_MODULES
|
||||
from bots.utils import BaseBotConfigView
|
||||
from cabinet.utils import CabinetViewMixin
|
||||
from config.utils import AllowCORSMixin
|
||||
|
||||
|
||||
class BotListView(CabinetViewMixin, ListView):
|
||||
template_name = 'cabinet/bots/bot_list.html'
|
||||
title = 'Bot list'
|
||||
sidebar_section = 'bots'
|
||||
|
||||
def get_queryset(self):
|
||||
return TelegramBot.objects.filter(owner=self.request.user)
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super(BotListView, self).get_context_data(*args, **kwargs)
|
||||
ctx['bot_modules'] = BOT_MODULES
|
||||
return ctx
|
||||
|
||||
|
||||
class BotConfigEditView(BaseBotConfigView, SingleObjectMixin):
|
||||
title = 'Configure bot'
|
||||
sidebar_section = 'bots'
|
||||
|
||||
def form_valid(self, bot_form, config_form):
|
||||
bot_form.save()
|
||||
config_form.save()
|
||||
cache.set('bots_reset', '1')
|
||||
messages.success(self.request, 'Config was successfully saved')
|
||||
return HttpResponseRedirect('')
|
||||
|
||||
def get_content_type(self):
|
||||
return self.get_object().config_type
|
||||
|
||||
|
||||
class BotConfigCreateView(BaseBotConfigView):
|
||||
title = 'Create bot'
|
||||
sidebar_section = 'bots'
|
||||
|
||||
def get_object(self):
|
||||
return None
|
||||
|
||||
def form_valid(self, bots_form, config_form):
|
||||
config_form.save()
|
||||
bots_form.instance.owner = self.request.user
|
||||
bots_form.instance.config_type = self.get_content_type()
|
||||
bots_form.instance.config_id = config_form.instance.pk
|
||||
bots_form.save()
|
||||
cache.set('bots_reset', '1')
|
||||
messages.success(self.request, 'Config was successfully saved')
|
||||
return redirect('cabinet:bots:edit', pk=bots_form.instance.pk)
|
||||
|
||||
def get_content_type(self):
|
||||
return get_object_or_404(ContentType, model=self.kwargs['content_type'])
|
||||
|
||||
|
||||
class BotConfigDeleteView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||
def get_queryset(self):
|
||||
return TelegramBot.objects.filter(owner=self.request.user)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
feed = self.get_object()
|
||||
messages.success(self.request, 'Bot "{}" was successfully deleted'.format(feed.title))
|
||||
feed.delete()
|
||||
return redirect('cabinet:bots:index')
|
||||
|
||||
|
||||
class BotRPCView(JSONResponseMixin, AjaxResponseMixin, AllowCORSMixin, View):
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs):
|
||||
bot = get_object_or_404(TelegramBot, rpc_name=self.kwargs.get('endpoint'))
|
||||
if not hasattr(bot.config, 'rpc_dispatcher'):
|
||||
raise PermissionDenied()
|
||||
rpc_response = JSONRPCResponseManager.handle(request.body, bot.config.rpc_dispatcher)
|
||||
response = self.render_json_response(rpc_response.data)
|
||||
self.add_access_control_headers(response)
|
||||
return response
|
Reference in New Issue
Block a user