Код на салфетке
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
Правительство РФ и США сегодня проведут проверку системы оповещения населения через мобильные телефоны.
А вы уже выключили мобилку и замотали её в фольгу?
Anonymous Poll
11%
А? ЧТО? ГДЕ МОЯ ФОЛЬГА?! *надевая шапочку*😱
4%
Пойду-ка я до магаза... Ковидные запасы гречки и туалетки кончаются😬
32%
Да всё нормуль будет, чипы от привики активируют и всё🙄
54%
О чём вы вообще?😁
🤔И где эта Света?
🤣3
Статистика сайта за 3 месяца

Месяц назад я закончил выкладывать на сайт вышедшие в Telegram-канале посты. На тот момент сайту было 2 месяца.

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

На втором месте переходы из Telegram-канала. Тут всё понятно.

Удивительно, но на первом месте стоят переходы из Dzen. Я туда дублирую посты, статистика там неважная - практически нет подписчиков, дочитываний статей очень мало, кликов из ленты и того меньше. Тем не менее это на данный момент основной источник трафика на сайт.

В сухом итоге:
- Посетители: Было 136, стало 210. Прирост 54.41%.
- Переходы из Dzen: Было 15.9%, стало 37.6%. Прирост 136.47%.
- Переходы из Telegram-канала: Было 12.2%, стало 24.2%. Прирост 98.36%.
- Переходы из поиска: Было 0%, стало 17.6%.

Я считаю, это хороший результат. Пишите в комментариях, если интересно наблюдать статистику каждый месяц.

Пост на сайте.
Поддержать канал.
🔥8
Которых нет.
🔥4😢2
AIOgram3 14. Фильтруем запрещённые слова

Следующей задачей для бота является фильтрация сообщений от запрещённый слов.

Задача не самая сложная, но есть пара нюансов.

Первый - что делать с сообщением?
Сообщение другого пользователя нельзя отредактировать удалив оттуда нежелательные слова. Его можно удалить, но бывают ситуации когда пользователь пишет большое сообщение и может вставить туда какое-то ругательство и весь его труд будет потерян.

Решение будет следующим:
- Пользовательское сообщение проверяется на наличие слова в нём
- В случае нахождения, создаётся копия сообщения
- Затем из копии удаляются / заменяются на звёздочки / модифицируются слова. Тут уже зависит от того, какой результат вы хотите получить. В моём случае слова будут заменяться на звёздочки.
- Далее удаляется оригинальное сообщение и от имени бота отправляется новое, содержащее имя отправителя и отредактированное сообщение.
Второй - сложность алгоритма и затрачиваемые ресурсы.
В используемом списке бранных слов насчитывается свыше 1000 слов.
Сообщение от пользователя очищается от пунктуации и разделяется на множество (set) слов.
При большом потоке сообщений, это может сильно увеличить нагрузку на сервер и замедлить работу бота.
Я опишу уже улучшенный вариант, к которому мы пришли с подписчиком @rusheslav после тестирования фильтра в чате, но если у вас есть комментарии или рекомендации по улучшению кода, буду рад!


Множество запрещённых слов.
Использовать будем готовый список: https://github.com/bars38/Russian_ban_words
Вы можете составить свой или найти другой.

Скачиваем файл words.txt. Для большей наглядности, я его переименовал в ban_words.txt.

В корне проекта создаём директорию res и помещаем туда файл.

Открываем файл settings.py и в начале, после импортов создаём константу BAN_WORDS.
В этой константе создаётся множество из слов в файле.
BAN_WORDS = set(line.strip() for line in open('res/ban_words.txt'))



Фильтрующая функция.
В пакете handlers создадим новый файл filter_words.py.

Создаём асинхронную функцию check_message, принимающую message.

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

from aiogram.types import Message

from botlogic import views
from botlogic.settings import BAN_WORDS, logger


async def check_message(message: Message):
contains_ban_word = False

if message.text:
message_words = set(message.text.translate(str.maketrans('', '', string.punctuation)).split())
filtered_message = message.text
for word in message_words:
if word.lower() in BAN_WORDS:
filtered_message = filtered_message.replace(word, "*" * len(word))
contains_ban_word = True

if contains_ban_word:
await message.delete()
logger.info(f"Удалено сообщение от пользователя {message.from_user.username}: {message.text}")
await message.answer_sticker('CAACAgIAAxkBAAEKbW1lGVW1I6zFVLyovwo2rSgIt1l35QADJQACYp0ISWYMy8-mubjIMAQ')
await message.answer(views.filtered_message(message.from_user.username, filtered_message))


По ходу действий:
Создаём переменную contains_ban_word. Это "флаг", по умолчанию считаем, что в сообщении нет запрещённых слов.
После проверки на наличие сообщения создаём переменную message_words, в которой создаём множество очищенных от пунктуации слов. Множество не позволяет хранить в себе два одинаковых объекта, тем самым в переменной будут только уникальные слова. И переменную filtered_message, в которую помещаем копию сообщения.

Далее проходимся циклом for по множеству слов из сообщения.
Если слово есть среди запрещённых, заменяем его на звёздочки в количестве длинны слова и выставляем "флаг" о наличии в тексте запрещённых слов.

Далее проверка "флага", если переменная contains_ban_word по-прежнему в изначальном состоянии False, то бот ничего не делает.
В противном случае бот сперва удаляет исходное сообщение. Записывает сообщение и информацию от кого в лог файл. Затем отправляет стикер по его идентификатору, после чего отправляет сообщение с исправленным текстом.


Как получить идентификатор стикера.
Для получения идентификатора стикера, достаточно отправить боту https://t.iss.one/idstickerbot стикер, идентификатор которого хотите получить.
В ответ он пришлёт идентификатор.
Готово.


Завершение
Осталось только зарегистрировать обработчик. Добавляем в файл main.py следующую строку: dp.message.register(check_message). Отсутствие второго аргумента означает, что бот будет обрабатывать все сообщения.

Готово. Теперь бот фильтрует нежелательные слова в сообщениях. Конечно это не панацея и мы все знаем, как можно исхитриться в Русском языке, но основную часть отсеивать будет. А потом просто добавляем слова.


Дополнительно: Изменение логгера.
Изначально логгер находился в файле main.py и не записывал никуда события, только отображал в терминале.

Вынес логгер в файл settings.py. Определил два обработчика, для терминала и для файла.
Код нового логгера:
# Логгер  
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
"%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s")

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

file_handler = logging.FileHandler("logs.txt")
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

logger.addHandler(console_handler)
logger.addHandler(file_handler)


Файлы к посту, можно получить в боте по коду: 702209
Пост на сайте.
Поддержать канал.
Кто помнит карточки?))
👍11
Соберём результаты опроса по возрасту подписчиков.

В опросе приняло участие 47 человек!

Получаются следующие цифры:
2 подписчика до 15 лет.
На третьем месте подписчики от 19 до 25 лет - в числе 8 проголосовавших.
Бо́льшая часть, а именно 19 человек в возрасте от 26 до 35.
Не отстают и подписчики от 36 до 45, их насчитывается 16.
Один подписчик в возрасте 46-55.
И одна(ин) стеснительный, пожелавший остаться неизвестным)

В комментарии к опросу пришло предложение сделать опрос по городам подписчиков.
Вместо опроса, пишите в комментариях из какого вы города! Потом также оформлю результаты.
👀
😁4🔥1
Приветствую.

В комментариях к шуточному опросу про тестирование систем оповещения, зашла речь о бункерах. И мне вспомнился один хороший фильм на эту тему.

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

Фильм на сегодня: Взрыв из прошлого (1998).

Краткий синопсис: Адам Уэббер родился нормальным ребенком, но, по глупому недоразумению, провел 35 лет в бомбоубежище вместе со своими родителями. Наивный и искренний, он впервые оказывается среди людей в бушующем Лос-Анджелесе 90-х! И кто знает - может, он еще успеет наверстать упущенное?!

Посмотреть фильм можно по ссылке: https://www.sspoisk.ru/film/2408

Может быть у вас есть свой вариант фильма на вечер? Делитесь им в комментариях!
AIOgram3 15. Обработка события вступления или покидания чата

Последняя (на данный момент) задача - это приветствие нового участника чата и оповещение о том, что кто-то нас покинул.

Решение достаточно простое и пост будет коротким.
Обработка событий.
Откроем файл events.py в пакете handlers и создадим две асинхронные функции: on_user_join и on_user_left.
Обе функции принимают event - объект класса ChatMemberUpdated.

В теле функции напишем отправку сообщения в ответ на событие, обращаясь к методам new_chat_member и old_chat_member.

Код функций:
from aiogram.types import ChatMemberUpdated


async def on_user_join(event: ChatMemberUpdated):
await event.answer(views.join_message(event.new_chat_member.user.first_name))


async def on_user_left(event: ChatMemberUpdated):
await event.answer(views.left_message(event.old_chat_member.user.first_name))


Не забудьте написать функции, возвращающие текст сообщения в файле views.py.


Регистрация обработчиков.
Переходим в файл main.py и добавляем следующие строки:
from aiogram.filters import ChatMemberUpdatedFilter, IS_NOT_MEMBER, IS_MEMBER


dp.chat_member.register(on_user_join, ChatMemberUpdatedFilter(IS_NOT_MEMBER >> IS_MEMBER))
dp.chat_member.register(on_user_left, ChatMemberUpdatedFilter(IS_MEMBER >> IS_NOT_MEMBER))

Готово! Теперь если новый пользователь зайдёт в чат, его поприветствует бот, а если кто-то его покинет, бот уведомит и об этом.

Файлы к посту, можно получить в боте по коду: 164296
Пост на сайте.
Поддержать канал.

#aiogram #гайды #python
🔥1
😐И так каждый раз...
😭3😁1
👀
👍6😁1
Годовой курс длиной в ~16 месяцев, а результат тот же...
🔥8💯1
Django 31. Форма авторизации и кнопка выхода

Продолжаем развивать проект.

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

И начнём мы с создания нового приложения.


Приложение user_app.
В прошлом посте я упоминал о важности разделения разных логических блоков на Django-приложения. Поэтому для пользователей сделаем отдельное приложение user_app.

Для создания нового приложения выполните в терминале следующую команду:
python manage.py startapp user_app


Создастся новая директория с основными файлами.

Сразу откроем файл settings.py и добавим новое приложение в список INSTALLED_APPS - 'user_app.apps.UserAppConfig',
Авторизация.

Форма авторизации.
Создадим форму авторизации.

На самом деле, делать этого не обязательно.
Можно воспользоваться встроенной формой, собственно от которой будем наследоваться, однако тут встаёт вопрос с её отображением. У стандартной формы отсутствуют стили.
Можно конечно их прописать с помощью CSS-селекторов, но у меня в данный момент используется Bootstrap5.
Для того чтобы воспользоваться его возможностями, необходимо форме определить конкретные стили, а для этого нужно её переопределить.

В директории приложения user_app создадим новый файл forms.py.

Создадим класс LoginForm, унаследованный от AuthenticationForm.

Затем создадим два поля:

Первое будет username. В нём определим поле символов.
Первым атрибутом будет максимальная длина имени пользователя. По умолчанию в модели пользователя максимальная длина составляет 150 символов, укажем столько же или можете по своему усмотрению указать меньше.
Вторым будет название поля.
Третьим атрибутом будет виджет, в котором определим текстовое поле ввода. Внутри в качестве атрибутов присвоим полю класс, а также укажем текст внутри поля.

Второе поле password. Также является полем символов.
Первый атрибут такой же - максимальная длина, которая равна 128 символам.
Второй - название поля.
Третьим атрибутом определим виджет, в этот раз это будет поле ввода пароля. Точно также передаём в атрибутах класс и текст внутри поля.

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

Код:
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User


class LoginForm(AuthenticationForm):
username = forms.CharField(
max_length=150,
label='Имя пользователя',
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите имя пользователя'
})
)
password = forms.CharField(
max_length=128,
label='Пароль',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Введите пароль'
})
)

class Meta:
model = User
fields = ['username', 'password']



Представление страницы авторизации.
Откроем файл views.py.

Напишем класс CustomLoginView, унаследованный от LoginView.

В классе пропишем всего три поля:
- authentication_form - в этом поле указываем класс нашей переопределённой формы.
- template_name - в этом поле указываем HTML-файл шаблона.
- extra_context - в этом поле указываем дополнительные переменные, передаваемые в шаблон.

Также переопределим метод get_success_url. В этом методе, воспользовавшись функцией reverse_lazy указываем на какую страницу перейдёт пользователь после авторизации. Пока у нас нет страницы профиля, будем перенаправлять пользователя на главную страницу.

Код:
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy

from user_app.forms import LoginForm


class CustomLoginView(LoginView):
authentication_form = LoginForm
template_name = 'user_app/login.html'
extra_context = {'title': 'Авторизация на сайте'}

def get_success_url(self):
return reverse_lazy('blog:index')
👍1