Бот-автоответчик с ChatGPT для Бизнес-аккаунта в Telegram на Aiogram 3
Автор: Иван Ашихмин
Не так давно в Telegram вышло большое обновление - "Telegram для бизнеса". В данный момент оно доступно для Premium-пользователей, а в будущем, вероятно, станет отдельным режимом.
"Telegram для бизнеса" предоставляет собой новый способ взаимодействия с клиентами через Telegram, вводя для этого новые функции:
- Адрес - Позволяет указать адрес и геопозицию в профиле.
- Часы работы - Позволяет указать график работы бизнеса.
- Быстрые ответы - Позволяет создать набор "шаблонных" ответов.
- Приветствия - Позволяет установить автоматическое приветствие для новых клиентов.
- "Нет на месте" - Позволяет отправлять автоматические ответы, в нерабочее время.
- Ссылки на чат - Позволяет кастомизировать ссылки на чат с вами.
- Вид нового чата - Позволяет кастомизировать вид чата для клиента, который открыл чат с вами, но ещё не написал сообщение.
Автор: Иван Ашихмин
Не так давно в Telegram вышло большое обновление - "Telegram для бизнеса". В данный момент оно доступно для Premium-пользователей, а в будущем, вероятно, станет отдельным режимом.
"Telegram для бизнеса" предоставляет собой новый способ взаимодействия с клиентами через Telegram, вводя для этого новые функции:
- Адрес - Позволяет указать адрес и геопозицию в профиле.
- Часы работы - Позволяет указать график работы бизнеса.
- Быстрые ответы - Позволяет создать набор "шаблонных" ответов.
- Приветствия - Позволяет установить автоматическое приветствие для новых клиентов.
- "Нет на месте" - Позволяет отправлять автоматические ответы, в нерабочее время.
- Ссылки на чат - Позволяет кастомизировать ссылки на чат с вами.
- Вид нового чата - Позволяет кастомизировать вид чата для клиента, который открыл чат с вами, но ещё не написал сообщение.
🔥4👍1
- Чат-боты - Позволяет подключить к учётной записи бота для взаимодействия с клиентами в личных чатах.
Из всего этого набора нас интересует только два пункта: Чат-боты и Часы работы.
Что мы с вами сделаем?
В этом посте мы создадим Telegram-бота, который будет принимать личные сообщения только в нерабочее время и для ответа использовать ChatGPT от OpenAI.
Поскольку OpenAI недоступен на территории РФ, вместо него будем использовать сервис NeuroAPI. Он предоставляет доступ к OpenAI из России и СНГ по более низким ценам.
Как это можно использовать?
Описанный в посте бот можно будет использовать как частному лицу, сделав личного ассистента на время отсутствия в сети, так и бизнесу для взаимодействия с клиентами в нерабочее время.
Главная сложность будет заключаться в составлении грамотного "системного промта", покрывающего ваши потребности.
Подключение бота в профиле.
Для проекта вам нужен бот, как его создать рассказано в посте "AIOgram3 1.5. Регистрация бота"
После создания бота и получения токена, в интерфейсе
Выберите нужного бота.
Затем в открывшемся меню выберите пункт "Bot Settings".
В следующем меню выберите пункт "Business Mode".
Включите бизнес режим.
После того, как включили бизнес режим для бота, откройте настройки Telegram и выберите пункт "Telegram для бизнеса", а в нём пункт "Чат-боты".
В открывшемся окне в первое поле пропишите ссылку на бота
Готово.
Подготовка проекта.
Создайте новый проект в удобной для вас IDE и активируйте виртуальное окружение.
Если вы пользуетесь PyCharm, то виртуальное окружение создаст IDE для нового проекта.
Если вы пользуетесь VSCode, то его придётся создать вручную, выполнив следующие команды:
В проекте используются следующие библиотеки:
-
-
-
-
-
-
Установите их, выполнив команду:
Создайте файл
Далее создайте файл
Необходимы следующие переменные:
-
-
-
-
-
-
Пример:
Также создайте файл
Файл конфигурации.
Из всего этого набора нас интересует только два пункта: Чат-боты и Часы работы.
Что мы с вами сделаем?
В этом посте мы создадим Telegram-бота, который будет принимать личные сообщения только в нерабочее время и для ответа использовать ChatGPT от OpenAI.
Поскольку OpenAI недоступен на территории РФ, вместо него будем использовать сервис NeuroAPI. Он предоставляет доступ к OpenAI из России и СНГ по более низким ценам.
Как это можно использовать?
Описанный в посте бот можно будет использовать как частному лицу, сделав личного ассистента на время отсутствия в сети, так и бизнесу для взаимодействия с клиентами в нерабочее время.
Главная сложность будет заключаться в составлении грамотного "системного промта", покрывающего ваши потребности.
Подключение бота в профиле.
Для проекта вам нужен бот, как его создать рассказано в посте "AIOgram3 1.5. Регистрация бота"
После создания бота и получения токена, в интерфейсе
BotFather, выполните команду /mybots для вывода списка всех ботов.Выберите нужного бота.
Затем в открывшемся меню выберите пункт "Bot Settings".
В следующем меню выберите пункт "Business Mode".
Включите бизнес режим.
После того, как включили бизнес режим для бота, откройте настройки Telegram и выберите пункт "Telegram для бизнеса", а в нём пункт "Чат-боты".
В открывшемся окне в первое поле пропишите ссылку на бота
t.iss.one/mybot или его имя @mybot.Готово.
Подготовка проекта.
Создайте новый проект в удобной для вас IDE и активируйте виртуальное окружение.
Если вы пользуетесь PyCharm, то виртуальное окружение создаст IDE для нового проекта.
Если вы пользуетесь VSCode, то его придётся создать вручную, выполнив следующие команды:
python -m venv .venv
# для Windows
venv\Scripts\activate.ps1 или venv\Scripts\activate.bat
# для *NIX-систем
source venv/bin/activate
В проекте используются следующие библиотеки:
-
aiogram - Фреймворк для бота.-
pydantic-settings - Библиотека для создания классов конфигураций.-
openai - Официальная библиотека OpenAI для Python.-
pytz - Библиотека для работы с часовыми поясами.-
httpx - Современная библиотека для создания синхронных/асинхронных запросов.-
redis - Библиотека для подключения к Redis.Установите их, выполнив команду:
pip install -U aiogram pydantic-settings openai pytz httpx redis
Создайте файл
requirements.txt и внесите в него установленные библиотеки:aiogram3.6.0
pydantic-settings2.2.1
openai1.29.0
pytz2024.1
httpx0.27.0
redis5.0.4
Далее создайте файл
.env для хранения переменных окружения.Необходимы следующие переменные:
-
token - Токен бота, полученный от BotFather.-
admin_id - Telegram-id администратора.-
openai_key - API-ключ полученный на сайте NeuroAPI или OpenAI.-
openai_base_url - Адрес прокси-сервера для OpenAI.-
redis_host - Хост для подключения к Redis. В нашем случае используется Docker compose, поэтому прописываем имя сервиса - redis.-
delay - Задержка между ответами в минутах. Об этом ниже.Пример:
token=12345:abcd
admin_id=123456789
openai_key=sk-abcd
openai_base_url=https://lk.neuroapi.host/v1
redis_host=redis
delay=10
Также создайте файл
main.py и пакет (Python package) app.Файл конфигурации.
🔥4👍1
В пакете
В нём будем получать данные из
Создайте класс
В теле класса пропишите шесть полей с указанием типа данных:
После полей, внутри класса напишите внутренний класс
Под классом создадим переменную
Далее создайте переменную
Будьте внимательны во время импорта класса! Нам нужен асинхронный Redis.
Последней будет переменная
Про
Полный код файла:
Хранилище строк.
Для хранения текстовых строк в одном месте в пакете
Этого можно и не делать. Кроме того, вариант с функциями можно заменить на получение текста из файла или иной способ.
Создайте три простые функции, которые ничего не принимают и возвращают текстровую строку:
-
-
-
Код:
Проверка рабочего времени.
В Telegram часы работы указываются по дням с понедельника по воскресенье. В коде же это выглядит как список объектов класса
В объекте класса
Необходимо получить текущее количество минут, прошедших с понедельника, и пройтись по списку, проверяя, входит ли текущее число в один из диапазонов.
Если входит, то бот будет игнорировать сообщения.
Если не входит, бот будет отвечать на сообщения.
app создайте файл settings.py.В нём будем получать данные из
.env-файла и определим инстанс бота и Redis.Создайте класс
Secrets, унаследованный от BaseSettings. Этот класс будет получать из .env-файла данные и преобразовывать их в Python-объекты. Для этого используется библиотека pydantic-settings.В теле класса пропишите шесть полей с указанием типа данных:
token: str
admin_id: int
openai_key: str
openai_base_url: str
redis_host: str
delay: int
После полей, внутри класса напишите внутренний класс
Config, в котором укажите из какого файла брать данные и его кодировку:class Config:
env_file = ".env"
env_file_encoding = "utf-8"
Под классом создадим переменную
secrets и объявим её экземпляром класса Secrets.Далее создайте переменную
redis_conn, это будет экземпляр класса Redis, в который передаём адрес хоста. Будьте внимательны во время импорта класса! Нам нужен асинхронный Redis.
redis_conn = Redis(host=secrets.redis_host)
Последней будет переменная
bot. Объявите её экземпляром класса Bot, передав в него токен и режим форматирования сообщений.bot = Bot(token=secrets.token, parse_mode="Markdown")
Про
parse_mode: Поскольку в ответе ChatGPT может находиться блок кода или другое форматирование, для корректного отображения его необходимо "распарсить". Передав параметр parse_mode="Markdown", мы сообщаем боту, что все сообщения будут с Markdown-форматированием.Полный код файла:
from aiogram import Bot
from pydantic_settings import BaseSettings
from redis.asyncio import Redis
class Secrets(BaseSettings):
token: str
admin_id: int
openai_key: str
openai_base_url: str
redis_host: str
delay: int
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
secrets = Secrets()
redis_conn = Redis(host=secrets.redis_host)
bot = Bot(token=secrets.token, parse_mode="Markdown")
Хранилище строк.
Для хранения текстовых строк в одном месте в пакете
app создайте файл views.py.Этого можно и не делать. Кроме того, вариант с функциями можно заменить на получение текста из файла или иной способ.
Создайте три простые функции, которые ничего не принимают и возвращают текстровую строку:
-
start_bot_message - Сообщение о запуске бота для администратора.-
stop_bot_message - Сообщение об остановке бота для администратора.-
system_prompt - Системный промт, описывающий поведение ChatGPT.Код:
def start_bot_message():
return "Бот запущен"
def stop_bot_message():
return "Бот остановлен"
def system_prompt():
return """Ты бот помощник и ты должен помогать людям."""
Проверка рабочего времени.
В Telegram часы работы указываются по дням с понедельника по воскресенье. В коде же это выглядит как список объектов класса
BusinessOpeningHoursInterval.В объекте класса
BusinessOpeningHoursInterval есть два поля: opening_minute и closing_minute, представленные в виде количества минут прошедших с 00:00 ближайшего понедельника, с учётом указанной временной зоны.Необходимо получить текущее количество минут, прошедших с понедельника, и пройтись по списку, проверяя, входит ли текущее число в один из диапазонов.
Если входит, то бот будет игнорировать сообщения.
Если не входит, бот будет отвечать на сообщения.
🔥3👍2
В пакете
Создайте функцию
Класс
-
-
Далее создайте четыре переменные:
-
-
-
-
Далее создайте цикл, в котором будем итерироваться по списку интервалов и проверять, входит ли текущее время в этот список.
Полный код:
Проверка входящих сообщений.
При получении входящего сообщения необходимо проверить актуальный режим работы и либо передать сообщение дальше в обработчик, либо "сбросить" его, тем самым никак не реагируя.
Для этого будем использовать миддлвари (middleware) - это так называзываемые "посредники", срабатывающие до передачи сообщения в обработчик и в зависимости от логики выполняющие различные действия, например, запись в БД, проверку аутентификации и многое другое.
В пакете
В этом файле создайте класс
В нём нам нужно переопределить
Далее нам необходимо получить из текущего чата объект класса
Лирическое отступление.
В актуальной на момент написания поста версии
В этом посте мы применим небольшой "костыль", для решения этой проблемы.
Разработчикам
app, создайте новый пакет utils. В этом пакете создайте файл opening_hours.py.Создайте функцию
check_opening_hours, принимающую opening_hours - объект класса BusinessOpeningHours.Класс
BusinessOpeningHours содержит два поля:-
time_zone_name - Название временной зоны. Определяется в профиле Telegram при заполнении графика работы.-
opening_hours - Упомянутый выше список с объектами класса BusinessOpeningHoursInterval.Далее создайте четыре переменные:
-
tz - В ней при помощи библиотеки pytz получаем информацию об указанной временной зоне.-
now - В ней получаем текущее время с учётом временной зоны.-
monday_start - В ней высчитываем время до начала понедельника.-
minutes_since_monday - В ней высчитываем сколько прошло минут с начала недели.tz = pytz.timezone(opening_hours.time_zone_name)
now = datetime.datetime.now(tz)
monday_start = now - datetime.timedelta(
days=now.weekday(),
hours=now.hour,
minutes=now.minute,
seconds=now.second,
microseconds=now.microsecond,
)
minutes_since_monday = (now - monday_start).total_seconds() / 60
Далее создайте цикл, в котором будем итерироваться по списку интервалов и проверять, входит ли текущее время в этот список.
for day in opening_hours.opening_hours:
if day.opening_minute <= minutes_since_monday <= day.closing_minute:
return False
return True
Полный код:
import datetime
import pytz
from aiogram.types import BusinessOpeningHours
def check_opening_hours(opening_hours: BusinessOpeningHours):
tz = pytz.timezone(opening_hours.time_zone_name)
now = datetime.datetime.now(tz)
monday_start = now - datetime.timedelta(
days=now.weekday(),
hours=now.hour,
minutes=now.minute,
seconds=now.second,
microseconds=now.microsecond,
)
minutes_since_monday = (now - monday_start).total_seconds() / 60
for day in opening_hours.opening_hours:
if day.opening_minute <= minutes_since_monday <= day.closing_minute:
return False
return True
Проверка входящих сообщений.
При получении входящего сообщения необходимо проверить актуальный режим работы и либо передать сообщение дальше в обработчик, либо "сбросить" его, тем самым никак не реагируя.
Для этого будем использовать миддлвари (middleware) - это так называзываемые "посредники", срабатывающие до передачи сообщения в обработчик и в зависимости от логики выполняющие различные действия, например, запись в БД, проверку аутентификации и многое другое.
В пакете
app создайте пакет middlewares. В нём создайте файл business_middleware.py.В этом файле создайте класс
BusinessMiddleware, унаследованный от BaseMiddleware.В нём нам нужно переопределить
dunder-метод __call__, принимающий self, handler, event, data.Далее нам необходимо получить из текущего чата объект класса
BusinessOpeningHours.Лирическое отступление.
В актуальной на момент написания поста версии
aiogram 3.6.0, заявлена полная поддержка Bot API 7.3. Если обратиться к объекту чата, то там будет параметр business_opening_hours, однако вместо желаемого объекта BusinessOpeningHours там находится None.В этом посте мы применим небольшой "костыль", для решения этой проблемы.
Разработчикам
aiogram был отправлен баг-репорт. Если в будущих версиях ситуация будет исправлена, пост будет обновлён.🔥4👍2❤1
Конец лирического отступления.
Для получения актуального графика работы мы обратимся к API Telegram.
Используя асинхронный менеджер контекста и библиотеку
В переменную
В переменной
Затем в переменной
Далее в блоке
Если возвращается
Внутри условия создаём переменную
Дальше ещё одно условие
Если условия соблюдаются, передаём сообщение дальше в обработчик.
Полный код файла:
Подключение ChatGPT.
В этой функции будем отправлять запрос к ChatGPT и возвращать полученный ответ.
В пакете
Создайте асинхронную функцию
В переменной
В переменной
Для получения актуального графика работы мы обратимся к API Telegram.
Используя асинхронный менеджер контекста и библиотеку
httpx, откройте асинхронный клиент для работы.В переменную
response получаем результат GET-запроса на сервер Telegram.В переменной
chat получаем JSON-объект из переменной response.Затем в переменной
full_chat создаём экземпляр класса ChatFullInfo, распаковав в него содержимое chat по ключу result. Таким образом мы преобразуем чистые JSON-данные в Python-объекты.async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.telegram.org/bot{secrets.token}/getChat?chat_id={secrets.admin_id}"
)
chat = response.json()
full_chat = ChatFullInfo(**chat["result"])
Далее в блоке
if вызываем ранее написанную функцию check_opening_hours, передав в неё full_chat.business_opening_hours.Если возвращается
True, мы продолжаем.Внутри условия создаём переменную
context, в которую присваиваем значение ключа event_context из переменной data.Дальше ещё одно условие
if, в котором проверяем, что сообщение содержит business_connection_id, т.е. является личным и что отправитель сообщения не админ, иначе бот будет реагировать и на ваши сообщения тоже.Если условия соблюдаются, передаём сообщение дальше в обработчик.
if check_opening_hours(full_chat.business_opening_hours):
context: EventContext = data.get("event_context")
if (
context.user.id != secrets.admin_id
and context.business_connection_id
):
return await handler(event, data)
Полный код файла:
from typing import Callable, Dict, Any, Awaitable
import httpx
from aiogram import BaseMiddleware
from aiogram.dispatcher.middlewares.user_context import EventContext
from aiogram.types import TelegramObject, ChatFullInfo
from app.settings import secrets
from app.utils.opening_hours import check_opening_hours
class BusinessMiddleware(BaseMiddleware):
async def __call__(
self,
handler: CallableTelegramObject, Dict[str, Any, Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.telegram.org/bot{secrets.token}/getChat?chat_id={secrets.admin_id}"
)
chat = response.json()
full_chat = ChatFullInfo(**chat["result"])
if check_opening_hours(full_chat.business_opening_hours):
context: EventContext = data.get("event_context")
if (
context.user.id != secrets.admin_id
and context.business_connection_id
):
return await handler(event, data)
Подключение ChatGPT.
В этой функции будем отправлять запрос к ChatGPT и возвращать полученный ответ.
В пакете
utils, создайте файл openai_actions.py.Создайте асинхронную функцию
get_chat_completion, принимающую message - объект класса Message.В переменной
http_client определите объект класса httpx.AsyncClient. Это объект HTTP-клиента, используя который будет произведён запрос.В переменной
client определите объект класса AsyncOpenAI, передав в него аргументы: api_key, http_client и base_url. Это объект клиента для OpenAI.🔥2👍1
http_client = httpx.AsyncClient(
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20)
)
client = AsyncOpenAI(
api_key=secrets.openai_key,
http_client=http_client,
base_url=secrets.openai_base_url,
)
Далее в переменной
messages создайте список словарей, где первый словарь – это системный промт, а второй – сообщение от пользователя:messages = [
{"role": "system", "content": system_prompt()},
{"role": "user", "content": message.text},
]
В переменную
response создайте запрос, передав в него:-
model - Выбранная модель ChatGPT, например, gpt-3.5-turbo, gpt-4-turbo, gpt-4o или любую другую поддерживаемую OpenAI.-
messages - Список словарей с сообщениями.-
max_tokens - Ограничение на максимальное количество токенов в ответе.-
temperature - Температура в диапазоне от 0 до 1. Определяет уровень "фантазии" бота. Чем ближе число к нулю, тем более предсказуемы будут ответы и наоборот, чем ближе к единице, тем более случайными будут ответы.И возвращаем результат запроса в обработчик:
response = await client.chat.completions.create(
model="gpt-3.5-turbo", messages=messages, max_tokens=1000, temperature=0.8
)
return response.choices[0].message.content
Полный код:
import httpx
from aiogram.types import Message
from openai import AsyncOpenAI
from app.settings import secrets
from app.views import system_prompt
async def get_chat_completion(message: Message):
http_client = httpx.AsyncClient(
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20)
)
client = AsyncOpenAI(
api_key=secrets.openai_key,
http_client=http_client,
base_url=secrets.openai_base_url,
)
messages = [
{"role": "system", "content": system_prompt()},
{"role": "user", "content": message.text},
]
response = await client.chat.completions.create(
model="gpt-3.5-turbo", messages=messages, max_tokens=1000, temperature=0.8
)
return response.choices[0].message.content
Задержка обработки сообщений.
Для того, чтобы пользователи не спамили и не использовали личные сообщения как "бесплатный GPT", добавим задержку в обработке сообщений.
В вашей реализации логики она может быть не нужна.
В пакете
utils создайте файл check_delay.py, а в нём асинхронную функцию check_user_delay, принимающую user_id.Тут-то нам и понадобится
Redis для хранения пользовательских ID и времени последнего сообщения. Вы можете использовать для этого другую БД или вовсе словарь в коде, это не принципиально.В переменную
last_message_time получаем из Redis по user_id время последнего сообщения, если оно есть. Если его нет - вернётся None.В блоке
if проверяем, что last_message_time True (проще говоря, не None).Внутри блока в переменную
time_since_last_message получаем разницу между текущим временем и полученным из хранилища.Ниже проверяем, если оно меньше указанной в
.env допустимой задержки, то возвращаем False.Во всех остальных случаях возвращаем
True.Полный код:
🔥4👍1
import asyncio
from app.settings import redis_conn, secrets
async def check_user_delay(user_id: int):
last_message_time = await redis_conn.get(f"users:{user_id}")
if last_message_time:
time_since_last_message = asyncio.get_event_loop().time() - float(
last_message_time
)
if time_since_last_message < secrets.delay * 60:
return False
return True
Обработчик бизнес сообщений.
Осталось написать обработчик, в который middleware будет передавать сообщение.
В пакете
app создайте пакет handlers, а в нём файл business_handler.py.В этом файле создайте асинхронную функцию
handle_business_message, принимающую message - объект класса Message.В самом начале создайте блок
if, проверяющий задержку и наличие текста в сообщении (отправить могут картинку или видео, а это другая логика работы с ChatGPT).Если условие не выполняется, то сообщение просто игнорируется.
Если условие выполнено, переходим к обработке.
В переменной
answer вызываем функцию get_chat_completion, передав в неё message.Затем отвечаем пользователю полученным сообщением.
Сохраняем в
Redis время текущего сообщения.Полный код:
import asyncio
from aiogram.types import Message
from app.settings import redis_conn
from app.utils.check_delay import check_user_delay
from app.utils.openai_actions import get_chat_completion
async def handle_business_message(message: Message):
if await check_user_delay(message.from_user.id) and message.text:
answer = await get_chat_completion(message)
await message.reply(answer)
await redis_conn.set(
f"users:{message.from_user.id}", asyncio.get_event_loop().time()
)
Обработка уведомлений о запуске/остановке бота.
Небольшое, но удобное дополнение.
В пакете
handlers создайте файл events.py.В нём создайте две асинхронные функции:
start_bot и stop_bot.В функциях отправляем сообщение администратору.
from app.settings import bot, secrets
from app import views
async def start_bot():
await bot.send_message(secrets.admin_id, views.start_bot_message())
async def stop_bot():
await bot.send_message(secrets.admin_id, views.stop_bot_message())
Основной файл.
Логику написали. Теперь осталось соединить всё вместе.
Откройте созданный ранее файл
main.py. Он должен находиться в корне проекта рядом с файлом .env.В нём создайте асинхронную функцию
start.В переменной
dp объявите экземпляр класса Dispatcher.Далее в несколько строк зарегистрируйте middleware и обработчики:
dp = Dispatcher()
dp.update.middleware(BusinessMiddleware())
dp.startup.register(start_bot)
dp.shutdown.register(stop_bot)
dp.business_message.register(handle_business_message)
Обратите внимание на
dp.business_message.register. Регистрируется обработка business_message, а не обычного message.Далее в блоке
try вызывается очистка сообщений, отправленных, когда бот был офлайн, и запуск пуллинга, а в блоке finally выполняется остановка бота.Вне функции в блоке
if __name__ "__main__" запускаем функцию старт.Полный код:
🔥2
import asyncio
from aiogram import Dispatcher
from aiogram.methods import DeleteWebhook
from app.handlers.business_handler import handle_business_message
from app.handlers.events import start_bot, stop_bot
from app.middlewares.business_middleware import BusinessMiddleware
from app.settings import bot
async def start():
dp = Dispatcher()
dp.update.middleware(BusinessMiddleware())
dp.startup.register(start_bot)
dp.shutdown.register(stop_bot)
dp.business_message.register(handle_business_message)
try:
await bot(DeleteWebhook(drop_pending_updates=True))
await dp.start_polling(bot)
finally:
await bot.session.close()
if __name__ "__main__":
asyncio.run(start())
Запуск бота.
Для запуска бота и Redis будем использовать Docker compose.
Сперва необходимо создать образ с ботом, для этого создайте файл
Dockerfile со следующим содержимым:FROM python:3.11-slim
WORKDIR /code
COPY requirements.txt /code
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY . /code
CMD [ "python", "./main.py" ]
В нём создаётся Docker-образ, в котором устанавливаются все зависимости из файла
requirements.txt. Затем копируются файлы проекта и выполняется команда запуска бота.Затем создайте файл
docker-compose.yaml со следующим содержимым:services:
bot:
build: .
restart: always
env_file:
- .env
volumes:
- .:/code
redis:
image: redis
restart: always
volumes:
- ./redis_data:/data
В нём описываются два сервиса:
Первый
bot. Указываем, что необходимо создать образ из Dockerfile, передать в него .env-файл и подключить текущую папку внутри контейнера.Второй
redis. Указываем, что будет использоваться официальный образ redis последней версии, и подключаем папку redis_data внутри контейнера, чтобы не потерять данные.Готово.
Запустить бота можно командой:
docker compose up -d
Пост на сайте
Поддержать проект
#aiogram #redis #telegram #Telegram_для_Бизнеса #telegram_бот #Telegram_business_mode #openai #python #chatgpt #docker
❤2🔥2
Docker. Запуск бота-автоответчика по готовому образу
Автор: Иван Ашихмин
После публикации поста "Бот-автоответчик с ChatGPT для Бизнес-аккаунта в Telegram на Aiogram 3", появился запрос на готовый Docker-образ.
Не все захотят собирать бота по гайду. Некоторым нужна возможность "взять и запустить". Для этого был собран готовый образ бота.
Получить образ можно, выполнив следующую команду:
Однако для бота всё равно необходим
Запуск бота.
Автор: Иван Ашихмин
После публикации поста "Бот-автоответчик с ChatGPT для Бизнес-аккаунта в Telegram на Aiogram 3", появился запрос на готовый Docker-образ.
Не все захотят собирать бота по гайду. Некоторым нужна возможность "взять и запустить". Для этого был собран готовый образ бота.
Получить образ можно, выполнив следующую команду:
docker pull git.pressanybutton.ru/prodream/manager_bot:latest
Однако для бота всё равно необходим
Redis и набор переменных окружения.Запуск бота.
🔥5👍1
Для запуска бота и
В отличие от композ-файла из поста в этом мы не собираем образ сами, а используем готовый.
Далее создадим файл
Пропишем в нём все необходимые данные:
Затем выполняем команду для поднятия
Бот запустится и уведомит об этом.
Данный способ позволит запустить бота без необходимости написания кода. Это поможет новичкам или тем, кому достаточно указанного в посте функционала, воспользоваться ботом-автоответчиком для своих нужд.
Если же вам нужен исходный код к проекту, то он доступен подписчикам на Boosty
Пост на сайте
Поддержать проект
#aiogram #redis #telegram #Telegram_для_Бизнеса #telegram_бот #Telegram_business_mode #openai #python #chatgpt #docker #docker_compose #docker_image
Redis, как и описано в посте, лучше использовать Docker compose. Создадим файл docker-compose.yaml со следующим содержимым:services:
bot:
image: git.pressanybutton.ru/prodream/manager_bot:latest
restart: always
env_file:
- .env
volumes:
- .:/code
redis:
image: redis
restart: always
volumes:
- ./redis_data:/data
В отличие от композ-файла из поста в этом мы не собираем образ сами, а используем готовый.
Далее создадим файл
.env в той же директории. Переменные окружения можно прописать и в композ-файле, но это может быть не очень удобно.Пропишем в нём все необходимые данные:
token=adasfasfas # токен бота
admin_id=1234567 # id администратора
openai_key=sk-... # токен OpenAI или neuroapi
openai_base_url=https://neuroapi.host/v1 # Оставляем neuroapi, либо прописываем API OpenAI
redis_host=redis # оставляем без изменений, либо прописываем свой Redis хост
delay=10 # указываем необходимую задержку на ответ
system_prompt=Ты бот помощник\nТвоя задача помогать людям # прописываем нужны системный промт используя \n для переноса строки
Затем выполняем команду для поднятия
Docker compose сервиса:docker compose up -d
Бот запустится и уведомит об этом.
Данный способ позволит запустить бота без необходимости написания кода. Это поможет новичкам или тем, кому достаточно указанного в посте функционала, воспользоваться ботом-автоответчиком для своих нужд.
Если же вам нужен исходный код к проекту, то он доступен подписчикам на Boosty
Пост на сайте
Поддержать проект
#aiogram #redis #telegram #Telegram_для_Бизнеса #telegram_бот #Telegram_business_mode #openai #python #chatgpt #docker #docker_compose #docker_image
🔥5
Привет!
Добро пожаловать в вечер пятницы, который идеально подходит для просмотра увлекательного фильма! Надеюсь, что вы уже подготовили уютное место, закуску и напитки, чтобы насладиться прекрасным вечером. Погрузитесь в мир кино и позвольте себе расслабиться после долгой недели.
Фильм: Достать ножи
Год: 2019
Когда сразу после празднования 85-летия известного автора криминальных романов Харлана Тромби виновника торжества находят мёртвым, за расследование берётся обаятельный и дотошный частный детектив Бенуа Блан. Ему предстоит распутать тугую сеть уловок и корыстной лжи, которой его опутывают члены неблагополучной семьи Харлана и преданный ему персонал.
https://www.kinopoisk.ru/film/1188529/
https://www.sspoisk.ru/film/1188529/
Приятного просмотра и отличного отдыха!
Добро пожаловать в вечер пятницы, который идеально подходит для просмотра увлекательного фильма! Надеюсь, что вы уже подготовили уютное место, закуску и напитки, чтобы насладиться прекрасным вечером. Погрузитесь в мир кино и позвольте себе расслабиться после долгой недели.
Фильм: Достать ножи
Год: 2019
Когда сразу после празднования 85-летия известного автора криминальных романов Харлана Тромби виновника торжества находят мёртвым, за расследование берётся обаятельный и дотошный частный детектив Бенуа Блан. Ему предстоит распутать тугую сеть уловок и корыстной лжи, которой его опутывают члены неблагополучной семьи Харлана и преданный ему персонал.
https://www.kinopoisk.ru/film/1188529/
https://www.sspoisk.ru/film/1188529/
Приятного просмотра и отличного отдыха!
🔥3🤮2🥴1
Что выведет код с изображения ниже? №23
Anonymous Quiz
23%
True True True False
20%
True True True True
17%
False True True False
23%
False True False True
17%
False False False False
🔥3🌭1
Судя по ответам, вчерашняя задача разделила голосующих по вариантам, если не поровну, то хотя бы близко к этому. Давайте разберём верный ответ.
Код задачи:
Разбор задачи.
Задача похожа на некоторые, которые мы задавали ранее, но в этой есть хитрость (в прочем, как и в других).
Разборы прошлых похожих задач:
№15 - https://t.iss.one/press_any_button/591
№17 - https://t.iss.one/press_any_button/613
Вся суть в значении переменных
Казалось бы, значение
Всё дело в работе Python с
Для питона 0.00...001 - погрешность, которую он предпочитает игнорировать, в следствии чего, значение
Именно по этому
Что касается последнего
Всё это можно проверить, выведя значение функции
Код задачи:
x = 0.1
y = 0.10000000000000001
z = round(y, 1)
print(x is y, x == z, z == y, z is x)
Разбор задачи.
Задача похожа на некоторые, которые мы задавали ранее, но в этой есть хитрость (в прочем, как и в других).
Разборы прошлых похожих задач:
№15 - https://t.iss.one/press_any_button/591
№17 - https://t.iss.one/press_any_button/613
Вся суть в значении переменных
x и y. Казалось бы, значение
x = 0.1 и y = 0.10000000000000001 - разные числа, следовательно и храниться должны в разных ячейках памяти, но нет. Всё дело в работе Python с
float-числами (числа с плавающей запятой, вещественные числа).Для питона 0.00...001 - погрешность, которую он предпочитает игнорировать, в следствии чего, значение
y считает как 0.1 и даёт ссылку на тот же участок памяти, что и x.Именно по этому
x is y = True, как и z == y = True .Что касается последнего
z is x, то тут именно то, что и должно быть. После применения функции round(), число помещается в новую ячейку памяти.Всё это можно проверить, выведя значение функции
id() по отношению к переменной:x = 0.1
y = 0.10000000000000001
z = round(y, 1)
print(x is y, x == z, z == y, z is x)
print(x, y, z)
print(id(x), id(y), id(z))
>>> True True True False
>>> 0.1 0.1 0.1
>>> 2078346127888 2078346127888 2078346131408
🔥4👍1
Всем привет!
Близится лето и с каждым днём всё больше становится конференций, митапов и прочих мероприятий IT-мира.
Об одном из них я хотел бы рассказать вам - Yandex Pytup.
Где: Нижний Новгород и Онлайн
Когда: 01.06.2024
Страница регистрации: https://yandex.ru/pytup
Конференция по Python от Яндекс. На мероприятии будет несколько интересных докладов, например, создание RAG-приложений, про GIL и другие.
Я уже записался в онлайн, будет очень интересно посмотреть и послушать.
Хотел бы рассказывать о подобных мероприятиях, если вам будет интересно - пишите в комментарии!
P.S. Это не реклама ( нам не заплатили🙁 )
Близится лето и с каждым днём всё больше становится конференций, митапов и прочих мероприятий IT-мира.
Об одном из них я хотел бы рассказать вам - Yandex Pytup.
Где: Нижний Новгород и Онлайн
Когда: 01.06.2024
Страница регистрации: https://yandex.ru/pytup
Конференция по Python от Яндекс. На мероприятии будет несколько интересных докладов, например, создание RAG-приложений, про GIL и другие.
Я уже записался в онлайн, будет очень интересно посмотреть и послушать.
Хотел бы рассказывать о подобных мероприятиях, если вам будет интересно - пишите в комментарии!
P.S. Это не реклама ( нам не заплатили🙁 )
🔥8
Приветствую!
В длинных постах можно запутаться поэтому, собираю воедино всё, что есть на данный момент.
Оглавления:
Для удобства навигации есть посты с оглавлениями по темам:
"Сайт на Django"
"Telegram-бот на AIOgram3"
"Применение Docker"
"Полезные инструменты"
"Путь в IT."
"Код в мешке"
"Boosty эксклюзив"
Ресурсы канала:
Уютный и немного безумный чат канала.
Бот с материалами к постам
Сайт со всеми постами
Канал в Dzen
Сообщество в VK
Поддержка.
Если вам нравится канал и выходящий материал, поделитесь ссылкой с людьми, кому это тоже может быть интересно.
Также поддержать канал можно:
Подпиской или донатом на Boosty.
Донатом в нашем Telegram-боте.
Или внеся сайт в исключения вашего блокировщика рекламы.
В длинных постах можно запутаться поэтому, собираю воедино всё, что есть на данный момент.
Оглавления:
Для удобства навигации есть посты с оглавлениями по темам:
"Сайт на Django"
"Telegram-бот на AIOgram3"
"Применение Docker"
"Полезные инструменты"
"Путь в IT."
"Код в мешке"
"Boosty эксклюзив"
Ресурсы канала:
Уютный и немного безумный чат канала.
Бот с материалами к постам
Сайт со всеми постами
Канал в Dzen
Сообщество в VK
Поддержка.
Если вам нравится канал и выходящий материал, поделитесь ссылкой с людьми, кому это тоже может быть интересно.
Также поддержать канал можно:
Подпиской или донатом на Boosty.
Донатом в нашем Telegram-боте.
Или внеся сайт в исключения вашего блокировщика рекламы.
🔥3❤2👍2
Первый фриланс проект - школа паралимпийского резерва
Автор: Иван Ашихмин
Всем привет!
Вчера закончилась работа по проекту для Хабаровской краевой спортивно-адаптивной школы паралимпийского и сурдлимпийского резерва. Проект длился почти год - с июня 2023 г. по конец мая 2024 г.
Задача заключалась в создании новостного сайта школы для замены устаревшего.
О том, почему так долго и как всё проходило расскажу в этом посте.
Сайт школы: https://kski.ru
Старый сайт школы: https://old.kski.ru
С чего всё началось?
Автор: Иван Ашихмин
Всем привет!
Вчера закончилась работа по проекту для Хабаровской краевой спортивно-адаптивной школы паралимпийского и сурдлимпийского резерва. Проект длился почти год - с июня 2023 г. по конец мая 2024 г.
Задача заключалась в создании новостного сайта школы для замены устаревшего.
О том, почему так долго и как всё проходило расскажу в этом посте.
Сайт школы: https://kski.ru
Старый сайт школы: https://old.kski.ru
С чего всё началось?
🔥5
Шёл 2023-й год, я ещё учился в GeekBrains, только начал думать о канале в Telegram. В один из дней, в разговоре с одногруппником Юрием, он сообщает, что, возможно, будет заказ на новый сайт для школы. Начали обсуждать на чём его можно сделать и дошли до разработки с нуля на Django. К слову, Django я в то время знал весьма посредственно, хотя мой дипломный проект уже был готов, а у Юры другой стек, он больше по ботам в Telegram, автоматизации, парсерам и сайтам на CMS-движках.
Несколько дней спустя он мне написал с предложением заняться проектом вместе. Честно сказать, я сперва подумал, что это шутка, т.к. знал и умел тогда не-то, чтобы много. Ещё учиться и учиться, а тут, бац, "давай делать проект!". Да ещё и денег заплатят! Не то, что бы много, конечно, но "дарёному коню в зубы не смотрят". Это была возможность. Та самая возможность получить практический опыт, а не все те лабораторные задачки в GB. Я согласился.
На следующее утро я всё ещё думая, что это он просто ошибся, переспросил у него, на что получил положительный ответ. И началась работа.
Начало работы.
Было составлено первоначальное ТЗ:
- Фреймворк - Django.
- БД - MySQL.
- Менеджер БД - PHPMyAdmin.
- Веб-сервер - Nginx.
- Фронтенд должен работать на CSS-фреймворке - Bootstrap.
- В админке должен быть визуальный редактор для удобного добавления статей с возможностью добавления медиафайлов.
- На сайте должен быть поиск по материалам.
- Необходимы группы пользователей с разными уровнями прав.
- Необходимо логирование действий пользователей в админке.
- На сайте должны быть категории и материалы.
- Должна быть пагинация.
- Должна быть форма обратной связи с отправкой сообщения пользователя администратору по email.
- Должен быть раздел "Фотогалерея" с удобным управлением в админке.
Задача поставлена, также были обозначены первые сроки - до конца года, но хотят пораньше. Понял, принял и взялся за работу.
Создал первоначальный проект в Django и сделал приватный репозиторий для всего этого дела.
Было решено сразу всё делать используя Docker-контейнеры и Docker Compose, поэтому были созданы необходимые Dockerfile и docker-compose.yml. Прописаны сервисы БД, PHPMyAdmin, Nginx. Юра предоставил для разработки технический домен и VPS-сервер.
При прописывании БД, порты MySQL были прописаны наружу сервера, для того, чтобы можно было вести разработку используя БД сервера, без необходимости потом делать миграции и перенос содержимого.
Довольно быстро был выполнен начальный пласт работ - созданы необходимые модели, добавлен визуальный редактор CKeditor 4, подключен Bootstrap, созданы технические шаблоны с примерами вывода информации из БД.
В это же время Юрий занялся шаблоном сайта, разбираясь с шаблонизатором Django.
Позже были добавлены "хлебные крошки" на страницы сайта, превьюшки для статей, дополнительные кастомизации админки, теги к статьям и поиск.
Архитектура, а скорее структура менялась в процессе. Сперва было две модели - для категории статей и для статей. Затем понадобились контентные страницы (для разного рода документов школы), а затем и страницы с видео. Дабы не перегружать две модели вешая на них с указанием разных типов, для каждого были написаны свои модели. Это позволило разделить функционал и более удобно настраивать необходимые разделы.
Первые сложности.
Первая сложность возникла с реализацией галереи. Я тогда не представлял, как можно реализовать галерею с альбомами, да и сейчас бы, наверное, задумался о реализации. Выбор пал на готовую библиотеку django-photologue. Удобная библиотека, позволяющая загружать изображения не только по одному, но и в zip-архиве, создавать альбомы и управлять ими.
Пришлось немного править шаблоны библиотеки под макет сайта, но в целом трудность решена. Хотя, с этой библиотекой позже возникла ещё одна проблема, но об этом в конце поста.
Вторая большая сложность возникла при использовании библиотеки django-mptt. Она позволяет создавать древовидные категории. Сложностей было несколько:
Несколько дней спустя он мне написал с предложением заняться проектом вместе. Честно сказать, я сперва подумал, что это шутка, т.к. знал и умел тогда не-то, чтобы много. Ещё учиться и учиться, а тут, бац, "давай делать проект!". Да ещё и денег заплатят! Не то, что бы много, конечно, но "дарёному коню в зубы не смотрят". Это была возможность. Та самая возможность получить практический опыт, а не все те лабораторные задачки в GB. Я согласился.
На следующее утро я всё ещё думая, что это он просто ошибся, переспросил у него, на что получил положительный ответ. И началась работа.
Начало работы.
Было составлено первоначальное ТЗ:
- Фреймворк - Django.
- БД - MySQL.
- Менеджер БД - PHPMyAdmin.
- Веб-сервер - Nginx.
- Фронтенд должен работать на CSS-фреймворке - Bootstrap.
- В админке должен быть визуальный редактор для удобного добавления статей с возможностью добавления медиафайлов.
- На сайте должен быть поиск по материалам.
- Необходимы группы пользователей с разными уровнями прав.
- Необходимо логирование действий пользователей в админке.
- На сайте должны быть категории и материалы.
- Должна быть пагинация.
- Должна быть форма обратной связи с отправкой сообщения пользователя администратору по email.
- Должен быть раздел "Фотогалерея" с удобным управлением в админке.
Задача поставлена, также были обозначены первые сроки - до конца года, но хотят пораньше. Понял, принял и взялся за работу.
Создал первоначальный проект в Django и сделал приватный репозиторий для всего этого дела.
Было решено сразу всё делать используя Docker-контейнеры и Docker Compose, поэтому были созданы необходимые Dockerfile и docker-compose.yml. Прописаны сервисы БД, PHPMyAdmin, Nginx. Юра предоставил для разработки технический домен и VPS-сервер.
При прописывании БД, порты MySQL были прописаны наружу сервера, для того, чтобы можно было вести разработку используя БД сервера, без необходимости потом делать миграции и перенос содержимого.
Довольно быстро был выполнен начальный пласт работ - созданы необходимые модели, добавлен визуальный редактор CKeditor 4, подключен Bootstrap, созданы технические шаблоны с примерами вывода информации из БД.
В это же время Юрий занялся шаблоном сайта, разбираясь с шаблонизатором Django.
Позже были добавлены "хлебные крошки" на страницы сайта, превьюшки для статей, дополнительные кастомизации админки, теги к статьям и поиск.
Архитектура, а скорее структура менялась в процессе. Сперва было две модели - для категории статей и для статей. Затем понадобились контентные страницы (для разного рода документов школы), а затем и страницы с видео. Дабы не перегружать две модели вешая на них с указанием разных типов, для каждого были написаны свои модели. Это позволило разделить функционал и более удобно настраивать необходимые разделы.
Первые сложности.
Первая сложность возникла с реализацией галереи. Я тогда не представлял, как можно реализовать галерею с альбомами, да и сейчас бы, наверное, задумался о реализации. Выбор пал на готовую библиотеку django-photologue. Удобная библиотека, позволяющая загружать изображения не только по одному, но и в zip-архиве, создавать альбомы и управлять ими.
Пришлось немного править шаблоны библиотеки под макет сайта, но в целом трудность решена. Хотя, с этой библиотекой позже возникла ещё одна проблема, но об этом в конце поста.
Вторая большая сложность возникла при использовании библиотеки django-mptt. Она позволяет создавать древовидные категории. Сложностей было несколько:
🔥4
- Первая была связана с отображением древовидных категорий в админке. Эта сложность возникла не сразу, а после того, как мы сменили шаблон админки со стандартного на django-jazzmin. Пришлось немного править файлы шаблона.
- Вторая была связана с отображением выпадающего списка категорий на страницах сайта. Изначально была мысль делать выпадающий список, в котором будут свои выпадашки следующего уровня и это доставило много проблем, но какая-никакая, первая реализация была сделана.
В процессе Юра то и дело подкидывал задачи, например, нужно было связать фотогалерею со статьёй. Задача оказалась не трудной, достаточно было в модели статьи прописать внешний ключ на модель альбома. Это позволило при написании статьи выбирать связанную с ней галерею.
Ещё одной задачей было реализовать видеоархив. Необходимо было вставлять только ссылку на YouTube, при этом на странице выводить встраиваемый плеер, а в качестве обложки страницы брать превьюшку видео. Поскольку получать каждый раз превьюшку видео с YouTube чревато баном, для получения превьюшки была написана функция, получающая изображение и сохраняющая её на диске. Вызывается функция при сохранении новой страницы с видео в админке, там же и происходит формирование плеера.
На всё это ушёл месяц. Проект в большей степени был выполнен. И началось затишье...
Провал на пол года.
Юрий показал текущий на тот момент вариант руководству и мы начали ждать следующих распоряжений по сайту. Они пришли в феврале вместе с многостраничным документом от министерства образования. В документе описывались требования, которым должен соответствовать сайт бюджетной организации.
Изучив документ, в целом практически не нашли ничего нового. Небольшие правки шаблона, но был и один пункт - версия для слабовидящих. Работу по её внедрению взял на себя Юра. В интернете есть несколько сайтов предоставляющих скрипт для этого и почти все платно. Был найден бесплатный вариант, доработан и установлен на сайт.
Юрий занялся переносом данных со старого сайта на новый.
Legacy наше всё.
Руководство школы решило сохранить старый сайт как архив. И раз у нас куплен сервер под новый сайт, зачем платить за хостинг для старого? Давайте перенесём старый сайт на сервер.
Что может пойти не так? А вот, что - старый сайт был написан в 2011-м году. С тех пор он не обновлялся, а следовательно, базируется на очень старых технологиях:
- Apache 2.2
- PHP 5.2
- MySQL 5.7
Отойдя от ностальгических воспоминаний, я принялся за работу. Первой идеей было установить LAMP, но в нём самая старая версия PHP - 7.2. Далее были эксперименты с панелями Vesta и Hestia, но и там PHP был относительно свежий. Но, видимо не мне одному понадобился PHP 5.2, поскольку был найден docker-образ с установленным PHP и Apache. Установить MySQL версии 5.7 не составило труда, он доступен в репозитории Docker Hub.
Однако, сайт не заработал. Жаловался на базу данных, мол не может подключиться.
После ряда попыток "подружить" современные технологии со старыми, перезагрузок, правок файлов - в общем действий "методом тыка", старый сайт удалось завести.
Чтобы не мешать старый сайт и новый, для старого сайта был создан второй Docker compose файл, содержащий php, mysql и phpmyadmin.
Последний месяц работы.
Когда работа по переносу данных была закончена и и сайт был почти готов, начали доводить его до ума.
Реализовали форму обратной связи с применением celery для отправки почты администратору в фоновом режиме. Поскольку в форме присутствует возможность к сообщению прикрепить файл, был написан валидатор для прикрепляемых файлов.
Были сделаны мелкие правки для передачи нужных данных в шаблоны.
Я писал выше о проблеме реализации выпадающих списков в меню на страницах сайта, для её решения было решено сделать отдельную модель для меню сайта. Также с использованием mptt, но без дополнительных выпадающих списков. Теперь в меню указаны только нужные разделы, а не все существующие.
- Вторая была связана с отображением выпадающего списка категорий на страницах сайта. Изначально была мысль делать выпадающий список, в котором будут свои выпадашки следующего уровня и это доставило много проблем, но какая-никакая, первая реализация была сделана.
В процессе Юра то и дело подкидывал задачи, например, нужно было связать фотогалерею со статьёй. Задача оказалась не трудной, достаточно было в модели статьи прописать внешний ключ на модель альбома. Это позволило при написании статьи выбирать связанную с ней галерею.
Ещё одной задачей было реализовать видеоархив. Необходимо было вставлять только ссылку на YouTube, при этом на странице выводить встраиваемый плеер, а в качестве обложки страницы брать превьюшку видео. Поскольку получать каждый раз превьюшку видео с YouTube чревато баном, для получения превьюшки была написана функция, получающая изображение и сохраняющая её на диске. Вызывается функция при сохранении новой страницы с видео в админке, там же и происходит формирование плеера.
На всё это ушёл месяц. Проект в большей степени был выполнен. И началось затишье...
Провал на пол года.
Юрий показал текущий на тот момент вариант руководству и мы начали ждать следующих распоряжений по сайту. Они пришли в феврале вместе с многостраничным документом от министерства образования. В документе описывались требования, которым должен соответствовать сайт бюджетной организации.
Изучив документ, в целом практически не нашли ничего нового. Небольшие правки шаблона, но был и один пункт - версия для слабовидящих. Работу по её внедрению взял на себя Юра. В интернете есть несколько сайтов предоставляющих скрипт для этого и почти все платно. Был найден бесплатный вариант, доработан и установлен на сайт.
Юрий занялся переносом данных со старого сайта на новый.
Legacy наше всё.
Руководство школы решило сохранить старый сайт как архив. И раз у нас куплен сервер под новый сайт, зачем платить за хостинг для старого? Давайте перенесём старый сайт на сервер.
Что может пойти не так? А вот, что - старый сайт был написан в 2011-м году. С тех пор он не обновлялся, а следовательно, базируется на очень старых технологиях:
- Apache 2.2
- PHP 5.2
- MySQL 5.7
Отойдя от ностальгических воспоминаний, я принялся за работу. Первой идеей было установить LAMP, но в нём самая старая версия PHP - 7.2. Далее были эксперименты с панелями Vesta и Hestia, но и там PHP был относительно свежий. Но, видимо не мне одному понадобился PHP 5.2, поскольку был найден docker-образ с установленным PHP и Apache. Установить MySQL версии 5.7 не составило труда, он доступен в репозитории Docker Hub.
Однако, сайт не заработал. Жаловался на базу данных, мол не может подключиться.
После ряда попыток "подружить" современные технологии со старыми, перезагрузок, правок файлов - в общем действий "методом тыка", старый сайт удалось завести.
Чтобы не мешать старый сайт и новый, для старого сайта был создан второй Docker compose файл, содержащий php, mysql и phpmyadmin.
Последний месяц работы.
Когда работа по переносу данных была закончена и и сайт был почти готов, начали доводить его до ума.
Реализовали форму обратной связи с применением celery для отправки почты администратору в фоновом режиме. Поскольку в форме присутствует возможность к сообщению прикрепить файл, был написан валидатор для прикрепляемых файлов.
Были сделаны мелкие правки для передачи нужных данных в шаблоны.
Я писал выше о проблеме реализации выпадающих списков в меню на страницах сайта, для её решения было решено сделать отдельную модель для меню сайта. Также с использованием mptt, но без дополнительных выпадающих списков. Теперь в меню указаны только нужные разделы, а не все существующие.
🔥3
У нас возникла проблема с библиотекой фотогалереи, а именно - при загрузке архива с изображениями должен создаваться новый альбом со своим slug'ом на основе указанного названия альбома, но он не создавался. Причина была в том, что при создании альбома непосредственно в админке, поле slug заполняется одновременно с названием альбома силами JavaScript, а при создании альбома путём загрузки архива оно создаётся используя функцию slugify из Django. С этой функцией я уже сталкивался ранее и даже писал об этом в одном из постов по Django.
Проблема в том, что стандартная функция не преобразует кириллицу. Взять и изменить код в файлах библиотеки не так то просто, но это же Django, а значит можно подключить библиотеку как приложение. Это и было сделано. Удалил библиотеку из зависимостей и поместил директорию с файлами приложения в проект, поправил импорты и всё заработало. Далее оставалось дело за малым, в одном файлике изменить используемую библиотеку для слагификации названия альбома.
Оставался последний шаг - сменить технический домен на основной с поддержкой https.
С бесплатными сертификатами есть одна проблема, их нужно обновлять каждые три месяца. Это можно обойти написав скрипт, но мало ли, что пойдёт не так у владельцев сайта. Было решено заменить nginx на caddy. Caddy это современный вебсервер, напоминающий nginx структурой файла конфигурации, но в нём есть один важный плюс - он автоматически получает сертификат для сайта и сам его продлевает по истечении срока действия.
После того, как убедились, что всё работает как надо перенесли домен со старого хостинга на сервер. С переносом тоже были проблемы - регистратор R01 никак не хотел менять NS-сервера у домена. Очень странное поведение, но после обращения в поддержку NS-ы сменили.
Оставалось только заменить домен в конфигурационных файлах. Всё!
У нас получилось в итоге два Docker Compose файла - для нового и старого сайта. По сути у нас два сайта, две версии MySQL и два связанных с ними PHPMyAdmin. Благодаря докеру это не вызывает никаких проблем с версионностью и позволяет управлять контейнерами без вреда для остального.
Заключение.
На всё про всё ушёл год. Хотя работы по сути было на 2-3 месяца. Однако, работа над этим проектом позволила заняться реальным, а не PET-проектом. Дала толчок к существованию канала.
Не бойтесь браться за то, что кажется сложным. Пусть это проект для кого-то или личный. Всё это опыт, навыки и повод гордиться собой.
Пост на сайте
Поддержать проект
#Django #Проект #Celery #Фриланс #КСКИ #Заказ
Проблема в том, что стандартная функция не преобразует кириллицу. Взять и изменить код в файлах библиотеки не так то просто, но это же Django, а значит можно подключить библиотеку как приложение. Это и было сделано. Удалил библиотеку из зависимостей и поместил директорию с файлами приложения в проект, поправил импорты и всё заработало. Далее оставалось дело за малым, в одном файлике изменить используемую библиотеку для слагификации названия альбома.
Оставался последний шаг - сменить технический домен на основной с поддержкой https.
С бесплатными сертификатами есть одна проблема, их нужно обновлять каждые три месяца. Это можно обойти написав скрипт, но мало ли, что пойдёт не так у владельцев сайта. Было решено заменить nginx на caddy. Caddy это современный вебсервер, напоминающий nginx структурой файла конфигурации, но в нём есть один важный плюс - он автоматически получает сертификат для сайта и сам его продлевает по истечении срока действия.
После того, как убедились, что всё работает как надо перенесли домен со старого хостинга на сервер. С переносом тоже были проблемы - регистратор R01 никак не хотел менять NS-сервера у домена. Очень странное поведение, но после обращения в поддержку NS-ы сменили.
Оставалось только заменить домен в конфигурационных файлах. Всё!
У нас получилось в итоге два Docker Compose файла - для нового и старого сайта. По сути у нас два сайта, две версии MySQL и два связанных с ними PHPMyAdmin. Благодаря докеру это не вызывает никаких проблем с версионностью и позволяет управлять контейнерами без вреда для остального.
Заключение.
На всё про всё ушёл год. Хотя работы по сути было на 2-3 месяца. Однако, работа над этим проектом позволила заняться реальным, а не PET-проектом. Дала толчок к существованию канала.
Не бойтесь браться за то, что кажется сложным. Пусть это проект для кого-то или личный. Всё это опыт, навыки и повод гордиться собой.
Пост на сайте
Поддержать проект
#Django #Проект #Celery #Фриланс #КСКИ #Заказ
🔥8👍1