diff --git a/bots/migrations/0009_auto_20191126_0058.py b/bots/migrations/0009_auto_20191126_0058.py
new file mode 100644
index 0000000..c5ad099
--- /dev/null
+++ b/bots/migrations/0009_auto_20191126_0058.py
@@ -0,0 +1,63 @@
+# Generated by Django 3.0rc1 on 2019-11-25 21:58
+
+from django.db import migrations, models
+import django.db.models.deletion
+import jsonfield.encoder
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bots', '0008_auto_20191124_1922'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CyberLinaBotModuleConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('first_part', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('second_part', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('third_part', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('emoji', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('already_ran', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('welcome_reactions', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ('inline_reactions', jsonfield.fields.JSONField(default=[], dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={})),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='CyberLinaChat',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.TextField()),
+ ('chat_id', models.BigIntegerField(db_index=True)),
+ ('last_run', models.DateField(blank=True, null=True)),
+ ('config', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chats', to='bots.CyberLinaBotModuleConfig')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CyberLinaUser',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('user_id', models.BigIntegerField(db_index=True)),
+ ('name', models.TextField()),
+ ('chat', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to='bots.CyberLinaChat')),
+ ],
+ options={
+ 'unique_together': {('chat', 'user_id')},
+ },
+ ),
+ migrations.AddField(
+ model_name='cyberlinachat',
+ name='last_choice',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='bots.CyberLinaUser'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='cyberlinachat',
+ unique_together={('config', 'chat_id')},
+ ),
+ ]
diff --git a/bots/migrations/0010_auto_20191126_0112.py b/bots/migrations/0010_auto_20191126_0112.py
new file mode 100644
index 0000000..cabbf1b
--- /dev/null
+++ b/bots/migrations/0010_auto_20191126_0112.py
@@ -0,0 +1,50 @@
+# Generated by Django 3.0rc1 on 2019-11-25 22:12
+
+from django.db import migrations
+import jsonfield.encoder
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bots', '0009_auto_20191126_0058'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='already_ran',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='emoji',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='first_part',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='inline_reactions',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='second_part',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='third_part',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='welcome_reactions',
+ field=jsonfield.fields.JSONField(default={'items': []}, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ ]
diff --git a/bots/migrations/0011_auto_20191127_2117.py b/bots/migrations/0011_auto_20191127_2117.py
new file mode 100644
index 0000000..b135a2c
--- /dev/null
+++ b/bots/migrations/0011_auto_20191127_2117.py
@@ -0,0 +1,50 @@
+# Generated by Django 3.0rc1 on 2019-11-27 18:17
+
+from django.db import migrations
+import jsonfield.encoder
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bots', '0010_auto_20191126_0112'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='already_ran',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='emoji',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='first_part',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='inline_reactions',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='second_part',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='third_part',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ migrations.AlterField(
+ model_name='cyberlinabotmoduleconfig',
+ name='welcome_reactions',
+ field=jsonfield.fields.JSONField(default='{"items": []}', dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
+ ),
+ ]
diff --git a/bots/modules/__init__.py b/bots/modules/__init__.py
index df6f707..9961707 100644
--- a/bots/modules/__init__.py
+++ b/bots/modules/__init__.py
@@ -1,5 +1,6 @@
from .overlay import OverlayBotModuleConfig
from .channel_helper import ChannelHelperBotModuleConfig, QueuedItem
from .echo import EchoBotModuleConfig
+from .cyberlina import CyberLinaBotModuleConfig
-BOT_MODULES = [EchoBotModuleConfig, ChannelHelperBotModuleConfig, OverlayBotModuleConfig]
+BOT_MODULES = [EchoBotModuleConfig, ChannelHelperBotModuleConfig, OverlayBotModuleConfig, CyberLinaBotModuleConfig]
diff --git a/bots/modules/cyberlina.py b/bots/modules/cyberlina.py
new file mode 100644
index 0000000..b575c9e
--- /dev/null
+++ b/bots/modules/cyberlina.py
@@ -0,0 +1,128 @@
+import os
+from datetime import timedelta, datetime, time
+from random import choice, seed
+from uuid import uuid4
+
+import humanize
+from django.db import models
+from django.utils.timezone import localdate, now, make_aware
+from jsoneditor.forms import JSONEditor
+from jsonfield import JSONField
+from telegram import Update, Chat, User, InlineQueryResultArticle, InputTextMessageContent
+from telegram.error import BadRequest
+from telegram.ext import Dispatcher, CallbackContext, MessageHandler, Filters, CommandHandler, InlineQueryHandler
+from telegram.utils.helpers import mention_html
+
+from bots.models import TelegramBotModuleConfig
+
+
+class CyberLinaBotModuleConfig(TelegramBotModuleConfig):
+ first_part = JSONField(default='{"items": []}')
+ second_part = JSONField(default='{"items": []}')
+ third_part = JSONField(default='{"items": []}')
+ emoji = JSONField(default='{"items": []}')
+ already_ran = JSONField(default='{"items": []}')
+ welcome_reactions = JSONField(default='{"items": []}')
+ inline_reactions = JSONField(default='{"items": []}')
+
+ MODULE_NAME = 'Киберлиночка'
+ CUSTOM_WIDGETS = {
+ 'first_part': JSONEditor(),
+ 'second_part': JSONEditor(),
+ 'third_part': JSONEditor(),
+ 'emoji': JSONEditor(),
+ 'already_ran': JSONEditor(),
+ 'welcome_reactions': JSONEditor(),
+ 'inline_reactions': JSONEditor(),
+ }
+
+ def message_handler(self, update: Update, ctx: CallbackContext):
+ if not update.effective_chat or not update.effective_user:
+ return
+ CyberLinaUser.from_tg_obj(self, update.effective_chat, update.effective_user)
+
+ def goodmorning_handler(self, update: Update, ctx: CallbackContext):
+ if not all([self.first_part['items'], self.second_part['items'],
+ self.third_part['items'], self.emoji['items']]):
+ return update.effective_message.reply_text('Я не настроена :c')
+ seed(os.urandom(128))
+ self.message_handler(update, ctx)
+ chat = self.chats.get(chat_id=update.effective_chat.id)
+ if chat.last_run and (chat.last_run >= localdate() or
+ chat.last_run + timedelta(1) == localdate() and now().hour < 6):
+ humanize.i18n.activate('ru_RU')
+ time_left = make_aware(datetime.combine(chat.last_run + timedelta(1), time(6, 0))) - now()
+ return update.effective_message.reply_text(
+ choice(self.already_ran['items']).format(
+ name=chat.last_choice.name,
+ time=humanize.naturaldelta(time_left)
+ )
+ )
+ while True:
+ user = chat.users.order_by('?').first() # type: CyberLinaUser
+ if not user:
+ return update.effective_message.reply_text('Нет известных юзеров в чате')
+ try:
+ member = ctx.bot.get_chat_member(chat.chat_id, user.user_id)
+ CyberLinaUser.from_tg_obj(self, update.effective_chat, member.user)
+ break
+ except BadRequest:
+ user.delete()
+ msg = '{}, {}! {}, {} {}'.format(
+ choice(self.first_part['items']),
+ choice(self.second_part['items']),
+ mention_html(user.user_id, user.name),
+ choice(self.third_part['items']),
+ choice(self.emoji['items']),
+ )
+ update.effective_chat.send_message(msg, parse_mode='html')
+ chat.last_run = localdate()
+ chat.last_choice = user
+ chat.save()
+
+ def inline_query_handler(self, update: Update, ctx: CallbackContext):
+ if not self.inline_reactions:
+ return
+ seed(os.urandom(128))
+ results = [
+ InlineQueryResultArticle(
+ id=uuid4(),
+ title='Не нажимай >_<',
+ input_message_content=InputTextMessageContent(choice(self.inline_reactions['items']))
+ )
+ ]
+ update.inline_query.answer(results)
+
+ def build_dispatcher(self, dispatcher: Dispatcher):
+ dispatcher.add_handler(CommandHandler('goodmorning', self.goodmorning_handler))
+ dispatcher.add_handler(MessageHandler(Filters.all, self.message_handler))
+ dispatcher.add_handler(InlineQueryHandler(self.inline_query_handler))
+ return dispatcher
+
+
+class CyberLinaChat(models.Model):
+ config = models.ForeignKey(CyberLinaBotModuleConfig, on_delete=models.CASCADE, related_name='chats')
+ name = models.TextField()
+ chat_id = models.BigIntegerField(db_index=True)
+ last_run = models.DateField(null=True, blank=True)
+ last_choice = models.ForeignKey('CyberLinaUser', on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
+
+ class Meta:
+ unique_together = 'config', 'chat_id',
+
+
+class CyberLinaUser(models.Model):
+ chat = models.ForeignKey(CyberLinaChat, on_delete=models.CASCADE, related_name='users')
+ user_id = models.BigIntegerField(db_index=True)
+ name = models.TextField()
+
+ @staticmethod
+ def from_tg_obj(config: CyberLinaBotModuleConfig, chat: Chat, user: User):
+ chat_title = chat.title or user.full_name
+ chat, _ = CyberLinaChat.objects.update_or_create(config=config, chat_id=chat.id,
+ defaults={'name': chat_title})
+ CyberLinaUser.objects.update_or_create(chat=chat, user_id=user.id,
+ defaults={'name': user.full_name})
+
+ class Meta:
+ unique_together = 'chat', 'user_id',
diff --git a/bots/templates/cabinet/bots/bot_form.html b/bots/templates/cabinet/bots/bot_form.html
index 5b88831..327d15f 100644
--- a/bots/templates/cabinet/bots/bot_form.html
+++ b/bots/templates/cabinet/bots/bot_form.html
@@ -53,3 +53,8 @@
{% endif %}
{% endblock %}
+
+{% block extra_body %}
+ {{ bot_form.media }}
+ {{ config_form.media }}
+{% endblock %}
diff --git a/cabinet/templates/cabinet/_base.html b/cabinet/templates/cabinet/_base.html
index 53889da..ffe3c95 100644
--- a/cabinet/templates/cabinet/_base.html
+++ b/cabinet/templates/cabinet/_base.html
@@ -15,6 +15,7 @@
+
@@ -74,5 +75,7 @@
});
{% endfor %}
+
+ {% block extra_body %}{% endblock %}