From ea4127e5f78a60270345ca2c2ecf010f079d010e Mon Sep 17 00:00:00 2001 From: Fynjy Date: Sun, 10 Sep 2023 16:00:59 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C=20=D0=B4=D0=BB=D1=8F=20=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + adminpanel/__init__.py | 0 adminpanel/asgi.py | 16 +++ adminpanel/settings.py | 134 +++++++++++++++++++++ adminpanel/urls.py | 11 ++ adminpanel/wsgi.py | 16 +++ adminpanelapp/__init__.py | 0 adminpanelapp/admin.py | 49 ++++++++ adminpanelapp/apps.py | 6 + adminpanelapp/models.py | 40 ++++++ adminpanelapp/templates/__init__.py | 0 adminpanelapp/templates/send_telegram_message.html | 6 + adminpanelapp/tests.py | 3 + adminpanelapp/urls.py | 7 ++ adminpanelapp/views.py | 28 +++++ bot_modules/mod_table_operate.py | 78 +++++++++++- bot_sys/config.py | 8 +- manage.py | 22 ++++ 18 files changed, 421 insertions(+), 4 deletions(-) create mode 100644 adminpanel/__init__.py create mode 100644 adminpanel/asgi.py create mode 100644 adminpanel/settings.py create mode 100644 adminpanel/urls.py create mode 100644 adminpanel/wsgi.py create mode 100644 adminpanelapp/__init__.py create mode 100644 adminpanelapp/admin.py create mode 100644 adminpanelapp/apps.py create mode 100644 adminpanelapp/models.py create mode 100644 adminpanelapp/templates/__init__.py create mode 100644 adminpanelapp/templates/send_telegram_message.html create mode 100644 adminpanelapp/tests.py create mode 100644 adminpanelapp/urls.py create mode 100644 adminpanelapp/views.py create mode 100755 manage.py diff --git a/.gitignore b/.gitignore index e3f5dc5..7f1c481 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ config_root_ids __pycache__ log.txt bot.db +migrations \ No newline at end of file diff --git a/adminpanel/__init__.py b/adminpanel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adminpanel/asgi.py b/adminpanel/asgi.py new file mode 100644 index 0000000..ea6cb00 --- /dev/null +++ b/adminpanel/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for adminpanel project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'adminpanel.settings') + +application = get_asgi_application() diff --git a/adminpanel/settings.py b/adminpanel/settings.py new file mode 100644 index 0000000..5719139 --- /dev/null +++ b/adminpanel/settings.py @@ -0,0 +1,134 @@ +""" +Django settings for adminpanel project. + +Generated by 'django-admin startproject' using Django 4.1.4. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-13x&cmg=f1gx9u(i@s(xplh+4x=+@ucyujcqf2t3hu7@i(k=fe' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'adminpanelapp' +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'adminpanel.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'adminpanel.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'bot.db'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +ASGI_APPLICATION = 'adminpanelapp.asgi.application' +ASYNC_MODE = 'django' +g_telegram_bot_api_token = '6212211018:AAEwcEN0NdjbhqDiClUk8vZkE_vfRUxsReU' + + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + diff --git a/adminpanel/urls.py b/adminpanel/urls.py new file mode 100644 index 0000000..9e1d200 --- /dev/null +++ b/adminpanel/urls.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from django.urls import path, include +from django.views.generic import RedirectView +from django.conf import settings +from django.conf.urls.static import static + +urlpatterns = [ +path('admin/', admin.site.urls), +path('', include('adminpanelapp.urls')), +path('', RedirectView.as_view(url='/admin/adminpanelapp/orders'), name='') +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/adminpanel/wsgi.py b/adminpanel/wsgi.py new file mode 100644 index 0000000..fb11c1e --- /dev/null +++ b/adminpanel/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for adminpanel project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'adminpanel.settings') + +application = get_wsgi_application() diff --git a/adminpanelapp/__init__.py b/adminpanelapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adminpanelapp/admin.py b/adminpanelapp/admin.py new file mode 100644 index 0000000..5b56fdf --- /dev/null +++ b/adminpanelapp/admin.py @@ -0,0 +1,49 @@ +import requests +from django.http import HttpResponseRedirect +from django.utils.html import format_html +from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME + +from adminpanel.settings import g_telegram_bot_api_token +from .models import Orders + + +from django.contrib import admin, messages +from django.urls import reverse + + + +class OrdersAdmin(admin.ModelAdmin): + list_display = ('orderName', 'orderCreateDateTime', 'show_orderPhoto', 'show_orderPhotoPay',) + actions =['change_value_and_redirect', ] + readonly_fields = ['orderPhoto', 'orderPhotoPay'] + + + def show_orderPhoto(self, obj): + if obj.orderPhoto and obj.orderPhoto.url: + return format_html('', obj.orderPhoto.url) + return "-" + show_orderPhoto.short_description = 'Фото заказа' + + def show_orderPhotoPay(self, obj): + if obj.orderPhotoPay and obj.orderPhotoPay.url: + return format_html('', obj.orderPhotoPay.url) + return "-" + show_orderPhotoPay.short_description = 'Фото чека' + + + def change_value_and_redirect(orders, request, queryset): + selected_objects = request.POST.getlist(ACTION_CHECKBOX_NAME) + if len(selected_objects) != 1: + messages.error(request, "Выберите только один объект") + return + + selected_user_id = int(selected_objects[0]) + obj = queryset.get(orderID=selected_user_id) + user_id = obj.userID + url = reverse('send_telegram_message', kwargs={'chat_id': user_id}) + return HttpResponseRedirect(url) + + + change_value_and_redirect.short_description = 'Отправка сообщения' + +admin.site.register(Orders, OrdersAdmin) diff --git a/adminpanelapp/apps.py b/adminpanelapp/apps.py new file mode 100644 index 0000000..f186ec5 --- /dev/null +++ b/adminpanelapp/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AdminpanelappConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'adminpanelapp' diff --git a/adminpanelapp/models.py b/adminpanelapp/models.py new file mode 100644 index 0000000..067f4cc --- /dev/null +++ b/adminpanelapp/models.py @@ -0,0 +1,40 @@ +from django.db import models + +import asyncio + + +class Orders(models.Model): + SEND_MESSAGE = ( + (True, 'Написать'), + (False, 'Не отправлять'), + (None, 'Неизвестно'), + ) + + IS_APPROVED = ( + (True, 'Заказ подтвержден'), + (False, 'Заказ не подтвержден'), + (None, 'Неизвестно'), + ) + + + orderID = models.AutoField(primary_key=True, verbose_name='id заказа') + userID = models.CharField(max_length=100, verbose_name='id пользователя в tg', null=True) + orderName = models.CharField(max_length=100, verbose_name='наименование', null=True) + orderDesc = models.TextField(verbose_name='описание', null=True) + orderPhoto = models.ImageField(upload_to='photo/', verbose_name='фото') + orderPhotoPay = models.ImageField(upload_to='photo/', verbose_name='чек') + orderAddress = models.CharField(max_length=100, verbose_name='адрес доставки', blank=True, null=True) + orderAccess = models.CharField(max_length=100, verbose_name='доступ', blank=True, null=True) + orderCreateDateTime = models.DateTimeField(auto_now_add=True, null=True, verbose_name='дата и время создания') + orderStatus = models.CharField(max_length=100, verbose_name='статус заказа', blank=True, null=True) + + + + class Meta: + # app_label = 'adminpanel' + verbose_name_plural = 'Заказы' + managed = False + db_table = 'orders' + + # def __str__(self): + # return self.name \ No newline at end of file diff --git a/adminpanelapp/templates/__init__.py b/adminpanelapp/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adminpanelapp/templates/send_telegram_message.html b/adminpanelapp/templates/send_telegram_message.html new file mode 100644 index 0000000..4644d06 --- /dev/null +++ b/adminpanelapp/templates/send_telegram_message.html @@ -0,0 +1,6 @@ +
+{% csrf_token %} + + + +
\ No newline at end of file diff --git a/adminpanelapp/tests.py b/adminpanelapp/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/adminpanelapp/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/adminpanelapp/urls.py b/adminpanelapp/urls.py new file mode 100644 index 0000000..37a940e --- /dev/null +++ b/adminpanelapp/urls.py @@ -0,0 +1,7 @@ +from django.urls import path, include +from .views import send_telegram_message + + +urlpatterns = [ + path('send_telegram_message//', send_telegram_message, name='send_telegram_message'), +] \ No newline at end of file diff --git a/adminpanelapp/views.py b/adminpanelapp/views.py new file mode 100644 index 0000000..5ef55cf --- /dev/null +++ b/adminpanelapp/views.py @@ -0,0 +1,28 @@ +from django.contrib import messages +from django.http import HttpResponseRedirect +from django.shortcuts import render, HttpResponse +import requests +from django.urls import reverse + +from adminpanelapp.models import Orders +from bot_sys.config import g_telegram_bot_api_token + + +def send_telegram_message(request, chat_id): + if request.method == 'POST': + message = request.POST.get('message') + + bot_token = g_telegram_bot_api_token + url = f'https://api.telegram.org/bot{bot_token}/sendMessage?text={message}&chat_id={chat_id}' + + response = requests.get(url) + if response.status_code == 200: + messages.success(request, "Сообщение успешно отправлено") + back_url = reverse('') + return HttpResponseRedirect(back_url) + else: + messages.error(request, "Сообщение не отправлено") + back_url = reverse('') + return HttpResponseRedirect(back_url) + else: + return render(request, 'send_telegram_message.html') \ No newline at end of file diff --git a/bot_modules/mod_table_operate.py b/bot_modules/mod_table_operate.py index acff162..8654c9b 100644 --- a/bot_modules/mod_table_operate.py +++ b/bot_modules/mod_table_operate.py @@ -1,10 +1,17 @@ # -*- coding: utf8 -*- -# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) - +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) +import os +import sqlite3 + +import requests +from aiogram.dispatcher.storage import FSMContextProxy +from asgiref.sync import sync_to_async +from adminpanelapp.models import Orders # Модуль для редактирования и просмотра таблицы в БД from bot_sys import keyboard, user_access, bd_table, bot_bd, bot_subscribes from bot_modules import access_utils, mod_simple_message +from bot_sys.config import g_telegram_bot_api_token from template import simple_message, bd_item, bd_item_select, bd_item_view, bd_item_delete, bd_item_add, bd_item_edit from aiogram.dispatcher import FSMContext @@ -259,6 +266,43 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): print('request', request, param) res, error = self.m_Bot.SQLRequest(request, commit = True, return_error = True, param = param) + async def create_order(): + + # получаю orderID + conn = sqlite3.connect('bot.db') + cursor = conn.cursor() + query = f"SELECT orderID FROM orders WHERE orderDesc = '{param[1]}'" + cursor.execute(query) + result = cursor.fetchone() + order_id = result[0] if result else None + conn.close() + + + token = g_telegram_bot_api_token + file_id = param[2] + url = f"https://api.telegram.org/bot{token}/getFile?file_id={file_id}&upload.auto_scale=true" + + response = requests.get(url) + data = response.json() + + if data['ok']: + file_path = data["result"]["file_path"] + file_url = f"https://api.telegram.org/file/bot{token}/{file_path}" + file_name = os.path.basename(file_path) + + save_path = f"media/photo/{file_name}" # Полный путь для сохранения файла + + response = requests.get(file_url) + if response.status_code == 200: + with open(save_path, 'wb') as file: + file.write(response.content) + + order = await sync_to_async(Orders.objects.get)(orderID=order_id) + with open(save_path, 'rb') as file: + await sync_to_async(order.orderPhoto.save)(file_name, file) + + await create_order() + self.OnChange() if error: self.m_Log.Error(f'Пользователь {a_UserID}. Ошибка добавления записи в таблицу {request} {param}.') @@ -353,6 +397,36 @@ class TableOperateModule(mod_simple_message.SimpleMessageModule): async def OnChange(a_ItemID, a_ItemData, a_EditUserID): await self.OnChangeField(a_Field, a_ItemID, a_ItemData, a_EditUserID) + + async def my_handler(fsm: FSMContextProxy): + order_id = fsm.get('orderID') + cheque = fsm.get('orderPhotoPay') + item = await sync_to_async(Orders.objects.get)(orderID=order_id) + + if cheque != None: + token = g_telegram_bot_api_token + url = f"https://api.telegram.org/bot{token}/getFile?file_id={cheque}" + + response = requests.get(url) + data = response.json() + + if data['ok']: + file_path = data["result"]["file_path"] + file_url = f"https://api.telegram.org/file/bot{token}/{file_path}" + file_name = os.path.basename(file_path) + + save_path = f"media/photo/{file_name}" + + response = requests.get(file_url) + + if response.status_code == 200: + with open(save_path, 'wb') as file: + file.write(response.content) + + order = await sync_to_async(Orders.objects.get)(orderID=order_id) + with open(save_path, 'rb') as file: + await sync_to_async(order.orderPhotoPay.save)(file_name, file) + await my_handler(a_ItemData) return self.OnChange() table_name = self.m_Table.GetName() diff --git a/bot_sys/config.py b/bot_sys/config.py index d41e67f..28bbba8 100644 --- a/bot_sys/config.py +++ b/bot_sys/config.py @@ -6,11 +6,11 @@ # --------------------------------------------------------- # API токен телеграмм бота. Создаётся с помощью @BotFather # Задаётся либо прямо тут в коде, либо в файле telegram_bot_api_token_file_name -g_telegram_bot_api_token = '' +g_telegram_bot_api_token = '6212211018:AAEwcEN0NdjbhqDiClUk8vZkE_vfRUxsReU' # Пользователи имеющие полный доступ, ID можно узнать например у этого бота @GetMyIDBot # Задаётся либо прямо тут в коде, либо в файле root_ids_file_name -g_root_ids = [] +g_root_ids = [1221909008] # Логирование событий в файл g_log_to_file = True @@ -57,3 +57,7 @@ def GetRootIDs(): return g_root_ids +import os +import django +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'adminpanel.settings') +django.setup() \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..b64513a --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'adminpanel.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() -- 2.11.0 From 6750d39dbe5a18e7f2d463c510e26ecd5a30ab53 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 10 Sep 2023 22:11:46 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D0=BE=D1=82=D0=B0=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot_sys/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot_sys/config.py b/bot_sys/config.py index 28bbba8..3310804 100644 --- a/bot_sys/config.py +++ b/bot_sys/config.py @@ -6,11 +6,11 @@ # --------------------------------------------------------- # API токен телеграмм бота. Создаётся с помощью @BotFather # Задаётся либо прямо тут в коде, либо в файле telegram_bot_api_token_file_name -g_telegram_bot_api_token = '6212211018:AAEwcEN0NdjbhqDiClUk8vZkE_vfRUxsReU' +g_telegram_bot_api_token = '' # Пользователи имеющие полный доступ, ID можно узнать например у этого бота @GetMyIDBot # Задаётся либо прямо тут в коде, либо в файле root_ids_file_name -g_root_ids = [1221909008] +g_root_ids = [] # Логирование событий в файл g_log_to_file = True -- 2.11.0 From 3ab3b3eaedfbc95904407eb53f0133d29de8cb9a Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 10 Sep 2023 22:16:56 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20requirements.txt=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | Bin 30 -> 772 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index e99a1ef1c0365cbcefd35ef1b5987c4bbcb06c2e..c48ca049d89088e5439b503ca842829ef2d5dbcc 100644 GIT binary patch literal 772 zcmYk4Sx&=H3`PBo#8QenF@5w0u>f{}LWc5CCYn~zE*?17PD&L;ZQiwg2fyE+%#wAs zwVh?w*oJ3mH@tJZwkytrzT2LscXS2%zk} z3Zyf%gqj?;nKP7B&(PKJ)ws80C!eBj^@$;vbE;~uREn9;mujhxq@L}A^Oht!M}??A z&W_zU++G7^x#WQC_Z@vq%=LsqprE2zlF&O16ofjVD6YFGH>>$%(b;O(lavPO3u&82l5haZ4LBH^h~(`n4<{p -- 2.11.0 From 8e8196df181a77b7aaa2f3fc481915ac6819880b Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 10 Sep 2023 22:40:50 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=84=D0=BE=D1=82=D0=BE=20=D1=85=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D1=88=D0=B5=D0=B3=D0=BE=20=D0=BA=D0=B0=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B0=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/bd_item_add.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/bd_item_add.py b/template/bd_item_add.py index 54c6960..542e14e 100644 --- a/template/bd_item_add.py +++ b/template/bd_item_add.py @@ -82,7 +82,7 @@ def FinishOrNextAddBDItemTemplate(a_Bot, a_FSM, a_AddBDItemFunc, a_ParentTableNa if a_Message.photo == None or len(a_Message.photo) == 0: await state.finish() return simple_message.WorkFuncResult(bot_messages.MakeBotMessage(error_photo_type_message), keyboard_func = a_FinishButtonFunc) - field_value = a_Message.photo[0].file_id + field_value = a_Message.photo[-1].file_id else: result = a_Message.text if a_PostProcessFunc: -- 2.11.0