Код на салфетке
2.22K subscribers
746 photos
14 videos
2 files
788 links
Канал для тех, кому интересно программирование на Python и не только.

Сайт: https://pressanybutton.ru/
Чат: https://t.iss.one/+Li2vbxfWo0Q4ZDk6
Заметки автора: @writeanynotes

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Docker 5.1 Почтовый сервер на Docker Mailserver - настройка домена

В посте "Django 12. Настройка отправки почты", я описывал как в Django можно настроить отправку почты используя обычный почтовый ящик и упоминал про возможность отправлять почту от имени домена используя специальные сервисы.

Поскольку меня не устраивали эти варианты, я начал изучать вопрос поднятия своего сервера для отправки/получения почты.

Решений нашлось не мало, от сборки своего сервера с нужными компонентами, до готовых комбайнов.
Сборка своего, даже из готовых компонентов, достаточно нетривиальная задача, требующая массу времени и знаний в этой области, оставим это Dev-ops инженерам и администраторам.
Выбор пал на готовые решения, но и тут не всё так просто. Есть масса вариантов, от совсем простеньких, до комбайнов, включающих в себя массу модулей и возможностей. Тут уже стоит исходить из потребностей, требуемых возможностей и, что немаловажно из системных требований решения. Поскольку в ресурсах я ограничен двумя Гигабайтами оперативной памяти на моём сервере, я выбрал в качестве почтового сервера - Docker Mailserver.


Окружение.
Действия в данном посте подразумевают, что у вас имеется домен и доступ к управлению DNS-записями.
В следующем посте понадобится VPS-сервер и действующий SSL-сертификат.

Могу порекомендовать два хостинга:
- HSHP - достаточно дешевые сервера в Германии, отлично подойдут для тестов или, например, настроить собственный VPN.
- TimeWeb Cloud - наверное, самый надёжный хостинг из всех, что я пробовал. Также тут удобно приобретать или подключать свои домены.
(Ссылки реферальные - вам не сложно, а мне приятно, спасибо.)

Используемый сервер:
- CPU: 1x 3.3ГГц
- RAM: 2Гб
- SSD: 20Гб NVMe
- OS: Debian 12


Docker Mailserver.
Docker Mail Server, или DMS, представляет собой готовый к работе полнофункциональный, но простой почтовый сервер.

Ключевые особенности:
- Поддерживает протоколы SMTP, IMAP, POP3 и LDAP.
- Используются только файлы конфигурации, без базы данных SQL.
- Поддержка антиспам фильтра SpamAssassin и антивируса ClamAV.


Настройка домена.
Начнём с подготовки домена для правильной работы с почтой.

Нам необходимо добавить шесть записей к домену, а именно: MX, A, PTR, DKIM, DMARC, SPF.
Вернее пять, поскольку DKIM-запись будем добавлять на этапе запуска сервера.

Для почты будем использовать поддомен mail.<имя_домена>. В моём случае это - mail.pressanybutton.ru, он же будет далее по тексту.
Необходимо его создать.

Все примеры будут на основе панели управления хостинга TimeWeb



PTR.
PTR (Pointer) - обратная запись ресурсов DNS. Она связывает IP-адрес с доменным именем, позволяя определить, какому домену принадлежит IP-адрес отправителя электронной почты.

Чаще всего данную запись нельзя установить из панели управления. Если такая возможность отсутствует у вашего хостинг-провайдера, то для установки вам необходимо обратиться в службу поддержки.

Установка на TimeWeb:
Открываем страницу "Облачные серверы" и выбираем vps, на котором будет располагаться почтовый сервер.
Выбираем вкладку "Сеть".
Там внизу находим IP-адрес сервера и нажимаем на шестерёнку. На открывшейся странице указываем доменное имя для почты.

MX.
MX (Mail eXchanger) - это записи ресурсов DNS, которые определяют серверы, принимающие почту для конкретного домена. MX-запись содержит приоритет и доменное имя сервера, который будет использоваться для обработки почты. Чем меньше числовое значение приоритета, тем выше приоритет сервера.

Установить запись можно в настройках DNS вашего хостинг-провайдера или у регистратора домена.

Запись должна быть вида: 10 mail.pressanybutton.ru.
Где 10 - это приоритет, а mail.pressanybutton.ru - почтовый домен.

Установка на TimeWeb(если домен приобретён или делегирован на сервера TimeWeb):
Открываем раздел "Домены". Выбираем нужный домен и переходим на вкладку DNS.
Если имеются mx-записи, удаляем. Затем нажимаем кнопку "Добавить запись" и выбираем MX.
Выбираем службу "Другое" и прописываем приоритет и почтовый домен.

То же самое делаем для поддомена.
A.
A (Address) - запись ресурсов DNS, которая сопоставляет доменное имя с его IP-адресом. Она используется для определения местоположения сервера, на котором находится сайт или почтовый сервер.

Также прописываем и для поддомена.

Установка такая же как и для предыдущего пункта.
Отличия лишь в том, что мы выбираем вместо mx-записи - a-запись. В значении указываем IP-адрес сервера.

Для поддомена в TimeWeb, А-запись не создаём, запись будет создана автоматически.

DMARC.
DMARC (Domain-based Message Authentication, Reporting and Conformance) - стандарт, который позволяет доменам определять политики обработки сообщений, отправленных с поддельных адресов электронной почты. DMARC включает в себя три компонента: аутентификацию, отчетность и соответствие.

Необходимо создать TXT-запись.

Хост: _dmarc.pressanybutton.ru
Значение: v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:[email protected]; ruf=mailto:[email protected]

Изменить адреса почты на ваши.

Установка на TimeWeb:
В том же разделе, что и ранее выбираем "Добавить запись" - "TXT".
В пункте "Хост" выбираем "Ввести вручную" и вводим _dmarc
Значение вводим такое же, что указано выше.

SPF.
SPF (Sender Policy Framework) - механизм, который помогает определить, является ли домен, указанный в поле “от”, авторизованным для отправки электронной почты от имени этого домена. SPF-запись в DNS содержит список серверов, которые авторизованы для отправки почты от имени домена.

Необходимо отредактировать имеющуюся TXT-запись с spf-записью или добавить новую TXT-запись.

Если TXT-записи с spf нет:
Хостом выбираем основной домен pressanybutton.ru.
В поле значение вводим: v=spf1 mx ~all

Если TXT-запись с spf есть:
Смотрим, что записано в поле значение, если там нет mx, добавляем через пробел после v=spf1, также если там нет ~all, то добавляем через пробел в самый конец.
У меня имелась запись: v=spf1 include:_spf.timeweb.ru ~all
Новая стала выглядеть так: v=spf1 mx include:_spf.timeweb.ru ~all

Установка на TimeWeb:
Так же как и в предыдущем пункте, только выбираем хостом основной домен и всё.

На этом подготовка домена закончена. В следующем посте будем запускать и настраивать сервер.

Файлы к посту, можно получить в боте по коду: 284512
Пост на сайте.
Поддержать канал.
🔥1
Тут меня вчера озадачили.

Поступило предложение, организовать к новому году "Тайного Санту". Кто не знает, что это - погуглите) Вопрос: хотели бы вы в этом поучаствовать?
Anonymous Poll
34%
Да! С удовольствием)
38%
Выглядит интересно, но я только посмотреть
3%
Нет, плохая идея
25%
Вам делать совсем нечего?
Docker 5.2 Почтовый сервер на Docker Mailserver - настройка и запуск

В прошлом посте мы подготовили домен для работы с почтой, в этом мы запустим почтовый сервер.

Запускать почтовый сервер будем в Docker-контейнере. Если у вас не установлен Docker, то почитать об установке можно в посте "Docker 2.2 Установка Docker на Linux".

Стоит отметить, что в этом посте не будет всеобъемлющей настройки почтового сервера. Я расскажу как запустить "минимальную конфигурацию", чтобы можно было пользоваться.
Полный перечень конфигураций и возможностей можно найти в официальной документации: https://docker-mailserver.github.io/docker-mailserver/latest/
Напомню используемое окружение:
Действия в данном посте подразумевают, что у вас имеется VPS сервер, настроенный домен и действующий SSL-сертификат.

Могу порекомендовать два хостинга:
- HSHP - достаточно дешевые сервера в Германии, отлично подойдут для тестов или скажем настроить собственный VPN.
- TimeWeb Cloud - наверное самый надёжный хостинг из всех, что я пробовал. Так же тут удобно приобретать или подключать свои домены.
(Ссылки реферальные - вам не сложно, а мне приятно, спасибо.)

Используемый сервер:
- CPU: 1x 3.3ГГц
- RAM: 2Гб
- SSD: 20Гб NVMe
- OS: Debian 12


Файл Docker Compose и конфигурация переменных окружения

Подключаемся по SSH к серверу и переходим в удобную для работы директорию.
Далее выполняем команду mkdir dms для создания директории в которой будут находиться конфигурационные файлы почтового сервера. Название директории можете изменить по желанию.

Переходим в директорию, выполнив команду cd dms.

Необходимо скачать два файла: compose.yaml и mailserver.env.
В первом находится конфигурация Docker Compose сервиса, а во втором параметры переменных окружения, определяющих настройки почтового сервера.

Для этого выполняем две команды:
wget "https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/compose.yaml"
wget "https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.env"


Также, вы можете найти эти файлы в архиве с материалами к посту.


Редактирование файла compose.yaml.
Откроем файл выполнив команду nano compose.yaml и изменим следующие параметры:
- hostname - в этом параметре прописываем почтовый домен, в моём случае это mail.pressanybutton.ru
- volumes - в этом параметре описываются локальные директории подключаемые в контейнере. Добавим две строчки в этот параметр:
- ./docker-data/dms/rspamd/:/etc/rspamd/override.d/
- ./docker-data/dms/custom-certs/:/tmp/dms/custom-certs/:ro


Готово. Нажимаем CTRL+S для сохранения, затем CTRL+X для выхода.


Редактирование файла mailserver.env.
Откроем файл выполнив команду nano mailserver.env и изменим следующие параметры:
- POSTMASTER_ADDRESS - данный параметр определяет почту администратора. Указываем почту по вашему желанию, например, у меня это - [email protected]
- TZ - указывает на временную зону. Указываем подходящий вам часовой пояс, например, Europe/Moscow
- ENABLE_OPENDKIM, ENABLE_OPENDMARC, ENABLE_POLICYD_SPF и ENABLE_AMAVIS - эти параметры выставляем на 0, поскольку за это будет отвечать Rspamd
- ENABLE_CLAMAV - параметр, отвечающий за работу антивируса ClamAV. Включать его или нет, выбирать вам, поскольку он достаточно сильно нагружает сервер и исходить нужно из имеющихся ресурсов и потребностей.
- ENABLE_RSPAMD - параметр, отвечающий за работу Rspamd. Устанавливаем здесь 1 для включения. Он необходим нам для работы DKIM, о котором чуть дальше. Rspamd — это высокопроизводительный сервер обработки почты с открытым исходным кодом. Он предназначен для фильтрации спама и вирусов, а также для ускорения обработки почты.
- SMTP_ONLY - параметр, переключающий почтовый сервер в только SMTP-сервер. В таком режиме он сможет только отправлять почту.
- SSL_TYPE - параметр, определяющий какой тип SSL-сертификата будет использоваться. Выставляем manual.
- SSL_CERT_PATH - путь до сертификата. Указываем /tmp/dms/custom-certs/cert.crt
- SSL_KEY_PATH - путь до ключа. Указываем /tmp/dms/custom-certs/privkey.key
- ENABLE_SPAMASSASSIN - параметр, отвечающий за работу Spam Assassin. Включайте по желанию и имеющимся ресурсам.

Готово. Нажимаем CTRL+S для сохранения, затем CTRL+X для выхода.
Сертификаты.
Выполните следующую команду:
mkdir docker-data && cd docker-data && mkdir dms && cd dms && mkdir custom-certs && cd custom-certs


В итоге должны создаться три директории и мы окажемся в директории custom-certs.
В эту директорию необходимо поместить сертификат и ключ с теми же именами, что прописывали ранее в конфигурационном файле.

Первый запуск.
Для запуска возвращаемся в основную директорию dms где находится файл compose.yaml и выполняем команду docker compose up. Если всё указано верно, сервер запустится и начнёт работу, но необходимы ещё настройки.

Откройте второй терминал и подключитесь к серверу.
Нам необходимо создать почтовый ящик. Для этого выполняем команду docker exec -ti mailserver setup email add <ADDRESS> <PASSWORD>. Где mailserver - имя контейнера, если вы не меняли его в файле compose.yaml, то оно будет mailserver.

После этого необходимо добавить псевдоним главного почтового адреса, выполнив команду docker exec -ti mailserver setup alias add [email protected] [email protected]. Первой почтой указываем почту, прописанную в файле mailserver.env, вторым можете указать удобную для вас почту на другом домене или ранее созданную почту.


DKIM.
В прошлом посте, я упоминал его в списке необходимых для прописывания в домене, сейчас мы его добавим.

DKIM (DomainKeys Identified Mail) - метод аутентификации электронной почты, который добавляет цифровую подпись в сообщение. Это помогает подтвердить, что сообщение пришло от предполагаемого отправителя.

Всё в том же втором терминале выполняем следующую команду: docker exec -ti mailserver setup config dkim

После выполнения отобразится результат работы и будет указан хост и значение для TXT-записи.
Перейдите в панель управления доменом и создайте новую запись.

В моём случае хост: mail._domainkey
Значение: v=DKIM1; k=rsa; p=MIIB.........

После этого возвращаемся в терминал и из директории dms, в которой файл compose.yaml, переходим в директорию docker-data/dms/rspamd там создаём файл dkim_signing.conf выполнив команду touch dkim_signing.conf.

Затем открываем его выполнив команду nano dkim_signing.conf и вставляем следующий текст:
# documentation: https://rspamd.com/doc/modules/dkim_signing.html

enabled = true;

sign_authenticated = true;
sign_local = true;

use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true;

check_pubkey = true; # you wan't to use this in the beginning

domain {
pressanybutton.ru {
path = "/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-pressanybutton.ru.private.txt";
selector = "mail";
}
}


В разделе domain указываем ваш домен, ниже в строке путь rsa-2048-mail-pressanybutton.ru.private.txt меняем на название файла находящегося по пути dms/docker-data/dms/config/rspamd/dkim

Готово. Нажимаем CTRL+S для сохранения, затем CTRL+X для выхода.

Завершение настройки.
Переходим в первый терминал с запущенным сервером и нажимаем CTRL+C для остановки. Далее на всякий случай удаляем контейнер, если он не удалился после остановки docker rm mailserver.

И запускаем снова, в этот раз в фоне(хотя можно и не в фоне, чтобы убедиться, что проблем нет) выполнив команду docker composer up -d.
Подключение к почте и проверка.
Для подключения к почте, можно воспользоваться программой Thunderbird. Скачать можно на сайте: https://www.thunderbird.net/ru/
Выбираем подключить существующую почту, вводим имя, адрес почты и пароль. Программа найдёт конфигурацию самостоятельно и подключится к серверу.

Далее открываем сайт https://www.mail-tester.com/ , копируем указанную почту и отправляем на неё письмо, желательно с осмысленной темой и содержимым. Затем на сайте нажимаем кнопку под адресом. Спустя несколько секунд должна отобразиться оценка и будут указаны ошибки, если что-то было неправильно настроено. Но, если всё делали в точности, должно быть 10/10!

Также, можете отправить письмо себе на разные ящики и замучить тестами друзей 🙂


Настройка Django.
Изначально с этого всё и началось, что нужен был почтовый сервер для отправки почты с сайта.
Откроем файл settings.py и найдём настройки почтового сервера, а если их не было, вписываем следующие:
EMAIL_HOST = 'mail.pressanybutton.ru'
EMAIL_PORT = 465
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False
EMAIL_USE_SSL = True
SERVER_EMAIL = EMAIL_HOST_USER
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER


В поле EMAIL_HOST вписываем ваш почтовый домен.
В поле EMAIL_HOST_USER вписываем почтовый адрес.
В поле EMAIL_HOST_PASSWORD вписываем пароль от почтового адреса.

Готово! Теперь Django отправляет почту от имени нашего домена через наш почтовый сервер!

Провозился с этим постом конечно долго, в процессе было уйма ошибок и попыток понять, как работает современная почтовая система со всеми их защитами от спама.
На самом деле, в этом посте только базовая настройка, сам Docker Mailserver весьма функционален и у него много возможностей, но настройка всего требует времени и знаний. Я постарался описать так, что бы "сделал и оно работает", а настройка под конкретные ситуации, уже на пользователе.

Файлы к посту, можно получить в боте по коду: 245646
Пост на сайте.
Поддержать канал.
🔥2
Как быстро вы ловите себя на мысли, что допустили ошибку при написании кода? (до выполнения и её проявления)
Anonymous Poll
11%
Довольно быстро, сразу исправляю
8%
Есть ощущение, что что-то не так, но не ищу причину
61%
Замечаю после запуска и пересмотра кода
19%
Замечаю только при её проявлении
Django 30. Рефакторинг и допущенные ошибки

Ошибки - неотъемлемая часть программирования. Как ни старайся, всё равно допустишь ошибки, которые по началу сочтёшь мелкими и незначительными, а по факту они соберутся в "снежный ком".

Приступив к написанию одного из следующих постов, я обнаружил несколько неудобных моментов в своём коде. Этого можно было бы избежать изначально, но невнимательность сделала своё дело.

В этом посте я проведу небольшой рефакторинг, а вернее даже, реорганизацию своего кода (и чуть-чуть скреплю это "костылями" и "синей изолентой").


Директория с шаблонами.
Начнём с простого. Сейчас есть всего одно приложение blog и директория с шаблонами находится в нём, но в будущем (и как следовало бы, в прошлом) будет несколько приложений. Каждое будет отвечать за свою область и у всех будут свои шаблоны. Делать в каждом приложении свою директорию с шаблонами не очень красиво, удобнее всё собрать в одном месте.
🔥3
К слову, это даже было предусмотрено. Изначально в посте по первоначальной настройке, мы добавили в параметр TEMPLATES строку - 'DIRS': [BASE_DIR / 'templates'],.

Решение проще некуда. Достаточно просто вынести директорию templates из директории приложения в директорию уровнем выше, где находится файл manage.py.

В ней находится директория blog, отвечающая за шаблоны приложения blog. В дальнейшем будут другие директории одноимённые используемым приложениям.


Документирование.
Классов, методов и функций становится всё больше, порой даже можно забыть зачем писал что-то. По-хорошему нужна хотя бы минимальная документация для класса и методов в нём.

Задача простая и поправимая, главное не забыть писать комментарии к чему-либо сразу при написании.


Разделение на приложения.
Добрались до главной ошибки.

В Django принято разделять логические участки на отдельные приложения, например, приложение для блога, приложение для API, приложение для аутентификации и профиля пользователя и так далее.

Моя ошибка заключается в том, что я блог и API уместил в одном приложении блога. Это не критичная ошибка, всё работает, однако это создаёт проблему навигации по коду. Код разрастается, состоит из блоков для разных задач.

Для исправления этой ситуации, я создам новое приложение api_app и перенесу в него всё, что связано с API. И всё бы ничего, но у меня есть несколько моделей и они содержат данные в БД. Простого способа перенести модель из приложения в приложение с сохранением данных в новой таблице я не нашёл. Однако есть один, весьма простой, но "костыльный" способ.

Если вы ещё только в процессе разработки сайта, не "пихайте" всё в одно приложение. Продумайте "на берегу" структуру и будущие модули. Я этого не сделал, теперь буду городить "костыли". Главное вовремя найти ошибку, исправить её и выучить урок.


Создание нового приложения.
Создадим новое приложение api_app выполнив команду python manage.py startapp api_app.

Переходим сразу в settings.py и добавляем новое приложение в INSTALLED_APPS.

И создадим файл urls.py в директории приложения api_app.


Перенос моделей и файлов.
Далее необходимо перенести всё связанное с API из приложения blog в приложение api_app.

Начнём с модели. Полностью вырезаем код необходимых моделей и переносим его в файл models.py в новом приложении.
Проверяем, не осталось ли каких-либо связей в других моделях относительно перенесённых. Если остались, правим импорты.

Затем в класс Meta перенесённых моделей добавляем новый пункт app_label и указываем имя старого приложения.
Таким образом код у нас находится в новом приложении, но модель считается моделью старого приложения и таблицы БД не будут перезаписаны. Если у вас нет данных в БД, пометку делать не нужно.

Далее переносим из файла admin.py связанные классы. Правим импорты.

Переносим файлы api.py и serializers.py. Правим импорты.

Переносим URL-паттерны для API из файла urls.py в новое приложение. Правим импорты и переходим в файл urls.py, находящийся в директории проекта. Добавляем новый паттерн path('api/', include('api_app.urls', namespace='api_app')),.

Применяем миграции. Результатом должно быть отсутствие новых миграций.

Готово. Всё должно работать, как и раньше.

Завершение.
Да, модели хоть и находятся на новом месте, но относятся к старому. Возможно в будущем, углубившись в Django я найду решение с "безболезненным" переносом данных модели. Но пока оставлю так, как говорится "надо было думать раньше".

Возможно, в моём коде есть ещё ошибки которые я не замечаю, буду рад если укажете на них в комментариях.

Файлы к посту, можно получить в боте по коду: 919633
Пост на сайте.
Поддержать канал.
🔥2
Cpp FreeGPT WebUI - Бесплатный GPT на вашем сервере

FreeGPT мёртв, да здравствует FreeGPT!

Сегодня, благодаря комментариям на канале, выяснилось, что FreeGPT, о котором я писал ранее прекращает свою работу.
Добавил об этом пометку и сообщение автора в посте "Бесплатный Chat-GPT4 на вашем ПК или сервере".

Заинтересовался вопросом, нет ли альтернатив и, как оказалось альтернатива есть.

Встречайте Cpp FreeGPT WebUI.

Написан на C++. Использует GPT4Free API и WebUI от ChatGPT Clone.

Доступны вариации моделей Chat GPT 3.5 Turbo и Chat GPT 4 (насчёт четвёрки не уверен, скорее всего та же тройка).

Ссылка на GitHub проекта: https://github.com/fantasy-peak/cpp-freegpt-webui#cpp-freegpt-webui

Единственный минус данного решения - установка либо на Linux, либо в Docker-контейнере.

А остальное, вы все прекрасно знаете. Приступим к установке.
Установка на Linux.

1. Клонируем репозиторий выполнив команду:
git clone https://github.com/fantasy-peak/cpp-freegpt-webui.git


2. Проверьте установленную версию пакета g++. Необходима версия g++ >= 13.1.0 (GCC).
3. Установите xmake, выполнив следующие команды:
wget https://github.com/xmake-io/xmake/releases/download/v2.8.2/xmake-v2.8.2.xz.run
chmod 777 xmake-v2.8.2.xz.run
./xmake-v2.8.2.xz.run
source ~/.xmake/profile


4. Установите libcurl-impersonate:
# ubuntu
apt-get install libcurl4-openssl-dev

# centos7
yum install libcurl-devel.x86_64

wget https://github.com/lwthiker/curl-impersonate/releases/download/v0.5.4/libcurl-impersonate-v0.5.4.x86_64-linux-gnu.tar.gz
sudo mv libcurl-impersonate-v0.5.4.x86_64-linux-gnu.tar.gz /usr/lib64
cd /usr/lib64
sudo tar -xvf libcurl-impersonate-v0.5.4.x86_64-linux-gnu.tar.gz
export LD_LIBRARY_PATH=/usr/lib64:$LD_LIBRARY_PATH
export LIBRARY_PATH=/usr/lib64:$LIBRARY_PATH


5. Компиляция:
git clone https://github.com/fantasy-peak/cpp-freegpt-webui.git
cd cpp-freegpt-webui
xmake build -v -y
xmake install -o .
cd bin
./cpp-freegpt-webui ../cfg/cpp-free-gpt.yml


6. После всех манипуляций, чат будет доступен в браузере по ссылке: https://127.0.0.1:8858/chat.


Запуск в Docker-контейнере.

1. Скачиваем актуальную версию образа, выполнив команду:
docker pull fantasypeak/freegpt:latest


2. Запускаем контейнер:
docker run -p 8858:8858 --name freegpt -d fantasypeak/freegpt:latest


3. Чат будет доступен в браузере по ссылке: https://127.0.0.1:8858/chat.

Завершение.
Запускал в Docker-контейнере на Windows и VPS-сервере. Работает без нареканий

Пост на сайте.
Поддержать канал.
🔥2
AIOgram3 12. Добавление бота в чат

Telegram-боты могут работать не только 1 на 1 с пользователем, но и с целыми чатами.

Вот и в чате канала появилась необходимость в боте, а именно в следующем функционале:
1. Приветствие новых пользователей чата.
2. Удаление сообщений, содержащих мат (уж простите, но чат публичное место и если без мата не обойтись, то лучше промолчать).
3. Погода. Да, погода. Уж не знаю на кой ляд она сдалась, но раз просят - надо делать.

И, что самое удобное, бот будет действовать на комментарии к постам на канале.
Добавление бота в чат.
Начнём с самого начала, а именно с добавления бота в чат.

В чате с ботом открываем его профиль.
Там выбираем пункт "Добавить в группу". В открывшемся списке выбираем необходимую группу.

Бот добавлен, но он не имеет доступа к сообщениям. Он будет работать, если к нему напрямую обращаться по команде, а остальные сообщения он не будет видеть. Это не позволит ему проверять сообщения, а также не позволит работать машине состояний, если будет несколько уровней взаимодействия.

Чтобы разрешить доступ к сообщениям боту, необходимо сделать его администратором чата. Достаточно минимальных прав, либо можно на будущее выбрать необходимое. Всё зависит от требуемого функционала.
Для этого откройте управление группой и выберите пункт "Администраторы". Нажмите "Добавить администратора" и выберите в списке вашего бота.
Затем выберите необходимые права и по желанию боту можно прописать роль.

Готово. Бот добавлен в группу и может читать сообщения.


Получение идентификатора чата.
Для взаимодействия бота с чатом, разграничением функционала и корректной работы, необходимо знать ID-чата.

Есть разные способы получить идентификатор, от простого - воспользоваться другим ботом, до замороченного - вывод идентификатора в консоль при срабатывании команды.

Воспользуемся простым.

Открываем чат с ботом https://t.iss.one/userinfobot
Сразу после запуска он покажет ваш Telegram-ID, но нам необходим ID-чата.

Переходим в чат и пишем сообщение от имени чата. Затем выделяем сообщение и пересылаем его в ранее упомянутого бота.

В ответ он пришлёт идентификатор чата начинающийся со знака -. Всё верно, Идентификаторы пользователей - целое число, а идентификаторы чатов и каналов - отрицательное.

Отлично. Теперь у вас есть идентификатор вашего чата.


Доработка кода бота.
Разобравшись с формальностями, перейдём к коду. Откроем файл settings.py.

У нас есть Dataclass Secrests в котором мы храним важные для работы бота данные, а именно токен и идентификатор администратора.

Добавим новую строчку:
group_id: int = -12345


Вместо -12345 вставляем идентификатор вашего чата.


Меню для чата.
На данный момент в моём боте есть четыре команды:
- start - Начало работы
- get_file - Получение файла с материалами
- help - Помощь по доступным командам
- about - Информация о боте

Однако, в чате в первых двух командах нет необходимости. Я хочу, чтобы они были доступны только из бота и их не было видно в меню чата.

Для этого откроем файл commands.py.
В теле функции set_commands находится список commands, определяющий меню бота. Его мы не трогаем.
Ниже до команды await bot.set_my_commands создаём новый список commands_chat и вписываем туда, по аналогии со списком выше, команды доступные в чате.

У меня на данный момент это две команды:
- help - Помощь по доступным командам
- about - Информация о боте

Ниже, под await bot.set_my_commands добавляем ещё одну такую же строчку. Изменив первый аргумент на новый список, а во втором прописав BotCommandScopeChat(chat_id=Secrets.group_id). Так мы определяем, что меню будет доступно в конкретном чате.

Новая строка:
from botlogic.settings import Secrets

await bot.set_my_commands(commands_chat, BotCommandScopeChat(chat_id=Secrets.group_id))


Теперь в чате будут отображаться только необходимые команды.

Ограничение существующих команд.
Теперь необходимо ограничить существующие команды от выполнения в чате. Это необходимо сделать, поскольку команду можно написать и не открывая меню и если команда там не указана, не значит, что она не может быть выполнена.

Откроем файл send_file.py и в самое начало функции send_file_start добавим условие:
from botlogic.settings import Secrets

if message.chat.id != Secrets.group_id:


Получится вот так:
async def send_file_start(message: Message, state: FSMContext):  
if message.chat.id != Secrets.group_id:
await message.answer(views.send_file_start_msg())
await state.set_state(SendFileSteps.get_code_from_user)


Во второй функции нет необходимости в проверке, поскольку в неё не попасть без первой.
Далее откроем файл simple.py и добавим такое же условие к функции start_command.

Функция about_command у меня универсальна, оставляем как есть.

Функция help_command выводит текст со списком доступных команд, следовательно, необходимо сделать две текстовые функции в файле views.py.
Сперва добавим условие, как и раньше, ниже прописываем else и повторяем команду с сообщением, изменив название функции, например, на help_chat_message.

Получается вот так:
async def help_command(message: Message):  
if message.chat.id != Secrets.group_id:
await message.answer(views.help_message())
else:
await message.answer(views.help_chat_message())


Переходим во views.py и создаём функцию, возвращающую текст с новым списком команд.


Завершение.
Готово. Бот подготовлен к работе в чате. В следующем посте начнём делать самую востребованную функцию.
Всё правильно, это погода.

Файлы к посту, можно получить в боте по коду: 385736
Пост на сайте.
Поддержать канал.
Классика..
👍2😱1
Акция! Только сегодня успейте записаться по ссыл....
👍5
AIOgram3 13. Прогноз погоды в боте - OpenWeatherMap

Самое востребованное в чате и наверное, одно из самых простых применений бота - прогноз погоды.

Использовать будем сервис OpenWeatherMap.

На бесплатном тарифе нам доступно 60 запросов в минуту и 1000 в день.


Регистрация и получение API-ключа.
Начнём с регистрации и получение ключа для API. После его получения, он начинает действовать в течении "пары часов", как раз успеем написать код.

Переходим на сайт: https://home.openweathermap.org/users/sign_in
Нажимаем на "Create an Account."

Вводим ник, почту, два раза пароль и отмечаем две первые галочки. Другие три галочки не трогаем, если конечно вы не желаете получать спам. Проходим капчу и нажимаем "Create Account".

Далее нас спросят с какой мы целью тут. Я выбрал "Education/Science". И ведь не соврал 😉. Нажимаем "Save".

Далее необходимо подтвердить электронную почту.
👍1
Сверху-справа нажимаем на свой ник, в выпадающем списке выбираем пункт "My API keys". Оказываемся на странице, где уже есть один ключ. Также после подтверждения почты, ключ и сопутствующая информация придёт на почту.

Как я и упомянул ранее, он активируется не сразу, а спустя какое-то время, поэтому переходим к коду.


Добавляем ключ.
Для начала, добавим API-ключ в наши "секреты".

Откроем файл settings.py и добавим новую строку в классе Secrets:
weather_key: str = 'ваш ключ'



Получение погоды.
В пакете utils создадим новый файл get_weather.py.

Создаём функцию request_weather, принимающую один аргумент city.

Код функции:
import requests  

from botlogic.settings import Secrets


def request_weather(city):
result = requests.get("https://api.openweathermap.org/data/2.5/find",
params={
'q': city,
'type': 'like',
'units': 'metric',
'lang': 'ru',
'APPID': Secrets.weather_key,
}).json()
if result['count'] == 0:
return 0
else:
return generate_result(result, city)


В переменную result попадает JSON с ответом от сервера.
С помощью библиотеки requests делаем GET-запрос. Первым аргументом передаём API URL.
Вторым аргументом передаём словарь параметров, подробнее по пунктам:
- q - отправляемый запрос, в нашем случае это город.
- type - тип поиска, в нашем случае ищем по совпадению.
- units - система измерения, в нашем случае метрическая.
- lang - язык.
- APPID - API-ключ.

Далее идёт проверка, если в ответе count равен нулю, то либо пользователь ошибся в названии города, либо такого города нет в базе сервиса. Возвращаем 0.
Иначе, возвращаем результат выполнения функции генерирующей строку с результатом.


Парсинг ответа и генерация строки с результатом.
Под функцией request_weather создаём новую функцию generate_result, принимающую данные из сервиса data и город city.

Код функции:
def generate_result(data, city):
temp = int(data['list'][0]['main']['temp'])
feels_like = data['list'][0]['main']['feels_like']
pressure = int(data['list'][0]['main']['pressure']) * 0.75
humidity = data['list'][0]['main']['humidity']
wind_speed = int(data['list'][0]['wind']['speed'])
rain = 'не ожидается' if data['list'][0]['rain'] is None else 'ожидается'
snow = 'не ожидается' if data['list'][0]['snow'] is None else 'ожидается'
weather = data['list'][0]['weather'][0]['description']
return f'''
<b>Прогноз погоды в городе {city}</b>

Сейчас температура {temp}°C
Ощущается как {feels_like}°
⛅️{weather}⛅️
💨 Скорость ветра {wind_speed}м/с 💨
Давление {pressure} мм рт.ст.
Влажность {humidity}%
💦 Дождь {rain}
❄️ Снег {snow}
'''


Тут мы из ответа получаем значения в 8 переменных, затем формируем строку с результатом.

Строку и необходимые данные можно кастомизировать по желанию.


Команда получения погоды.
Теперь перейдём к созданию команды. В пакете handlers создадим файл weather_fsm.py.

Создадим асинхронную функцию get_weather_command, принимающую два аргумента: message и state.

В теле функции прописываем две команды:
- Отправка сообщения пользователю. В сообщении просим ввести город или отменить запрос.
- Переход на следующий шаг в машине состояний.

Код функции:
from aiogram.fsm.context import FSMContext  
from aiogram.types import Message

from botlogic import views
from botlogic.utils.statesform import GetWeatherSteps


async def get_weather_command(message: Message, state: FSMContext):
await message.answer(views.enter_city())
await state.set_state(GetWeatherSteps.BY_CITY)


Для первой команды необходимо открыть файл views.py и создать функцию enter_city, возвращающую строку с просьбой ввести город или отменить запрос.

Для второй команды необходимо открыть файл statesform.py и создать новый класс GetWeatherSteps:
class GetWeatherSteps(StatesGroup):  
BY_CITY = State()
👍1