Правительство РФ и США сегодня проведут проверку системы оповещения населения через мобильные телефоны.
А вы уже выключили мобилку и замотали её в фольгу?
А вы уже выключили мобилку и замотали её в фольгу?
Anonymous Poll
11%
А? ЧТО? ГДЕ МОЯ ФОЛЬГА?! *надевая шапочку*😱
4%
Пойду-ка я до магаза... Ковидные запасы гречки и туалетки кончаются😬
32%
Да всё нормуль будет, чипы от привики активируют и всё🙄
54%
О чём вы вообще?😁
Статистика сайта за 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%.
Я считаю, это хороший результат. Пишите в комментариях, если интересно наблюдать статистику каждый месяц.
Пост на сайте.
Поддержать канал.
Месяц назад я закончил выкладывать на сайт вышедшие в 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
AIOgram3 14. Фильтруем запрещённые слова
Следующей задачей для бота является фильтрация сообщений от запрещённый слов.
Задача не самая сложная, но есть пара нюансов.
Первый - что делать с сообщением?
Сообщение другого пользователя нельзя отредактировать удалив оттуда нежелательные слова. Его можно удалить, но бывают ситуации когда пользователь пишет большое сообщение и может вставить туда какое-то ругательство и весь его труд будет потерян.
Решение будет следующим:
- Пользовательское сообщение проверяется на наличие слова в нём
- В случае нахождения, создаётся копия сообщения
- Затем из копии удаляются / заменяются на звёздочки / модифицируются слова. Тут уже зависит от того, какой результат вы хотите получить. В моём случае слова будут заменяться на звёздочки.
- Далее удаляется оригинальное сообщение и от имени бота отправляется новое, содержащее имя отправителя и отредактированное сообщение.
Следующей задачей для бота является фильтрация сообщений от запрещённый слов.
Задача не самая сложная, но есть пара нюансов.
Первый - что делать с сообщением?
Сообщение другого пользователя нельзя отредактировать удалив оттуда нежелательные слова. Его можно удалить, но бывают ситуации когда пользователь пишет большое сообщение и может вставить туда какое-то ругательство и весь его труд будет потерян.
Решение будет следующим:
- Пользовательское сообщение проверяется на наличие слова в нём
- В случае нахождения, создаётся копия сообщения
- Затем из копии удаляются / заменяются на звёздочки / модифицируются слова. Тут уже зависит от того, какой результат вы хотите получить. В моём случае слова будут заменяться на звёздочки.
- Далее удаляется оригинальное сообщение и от имени бота отправляется новое, содержащее имя отправителя и отредактированное сообщение.
Второй - сложность алгоритма и затрачиваемые ресурсы.
В используемом списке бранных слов насчитывается свыше 1000 слов.
Сообщение от пользователя очищается от пунктуации и разделяется на множество (set) слов.
При большом потоке сообщений, это может сильно увеличить нагрузку на сервер и замедлить работу бота.
Я опишу уже улучшенный вариант, к которому мы пришли с подписчиком @rusheslav после тестирования фильтра в чате, но если у вас есть комментарии или рекомендации по улучшению кода, буду рад!
Множество запрещённых слов.
Использовать будем готовый список: https://github.com/bars38/Russian_ban_words
Вы можете составить свой или найти другой.
Скачиваем файл
В корне проекта создаём директорию
Открываем файл
В этой константе создаётся множество из слов в файле.
Фильтрующая функция.
В пакете
Создаём асинхронную функцию
Код функции:
По ходу действий:
Создаём переменную
После проверки на наличие сообщения создаём переменную
Далее проходимся циклом
Если слово есть среди запрещённых, заменяем его на звёздочки в количестве длинны слова и выставляем "флаг" о наличии в тексте запрещённых слов.
Далее проверка "флага", если переменная
В противном случае бот сперва удаляет исходное сообщение. Записывает сообщение и информацию от кого в лог файл. Затем отправляет стикер по его идентификатору, после чего отправляет сообщение с исправленным текстом.
Как получить идентификатор стикера.
Для получения идентификатора стикера, достаточно отправить боту https://t.iss.one/idstickerbot стикер, идентификатор которого хотите получить.
В ответ он пришлёт идентификатор.
Готово.
Завершение
Осталось только зарегистрировать обработчик. Добавляем в файл
Готово. Теперь бот фильтрует нежелательные слова в сообщениях. Конечно это не панацея и мы все знаем, как можно исхитриться в Русском языке, но основную часть отсеивать будет. А потом просто добавляем слова.
Дополнительно: Изменение логгера.
Изначально логгер находился в файле
Вынес логгер в файл
В используемом списке бранных слов насчитывается свыше 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. Определил два обработчика, для терминала и для файла.Код нового логгера:
Файлы к посту, можно получить в боте по коду: 702209
Пост на сайте.
Поддержать канал.
# Логгер
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
Пост на сайте.
Поддержать канал.
Возрастной опрос =)
Сколько вам лет?
Сколько вам лет?
Anonymous Poll
3%
До 15
0%
От 16 до 18
13%
От 19 до 25
40%
От 26 до 35
36%
От 36 до 45
6%
От 46 до 55
1%
От 56
1%
Не скажу, я стесняшка☺️
😱5
Соберём результаты опроса по возрасту подписчиков.
В опросе приняло участие 47 человек!
Получаются следующие цифры:
2 подписчика до 15 лет.
На третьем месте подписчики от 19 до 25 лет - в числе 8 проголосовавших.
Бо́льшая часть, а именно 19 человек в возрасте от 26 до 35.
Не отстают и подписчики от 36 до 45, их насчитывается 16.
Один подписчик в возрасте 46-55.
И одна(ин) стеснительный, пожелавший остаться неизвестным)
В комментарии к опросу пришло предложение сделать опрос по городам подписчиков.
Вместо опроса, пишите в комментариях из какого вы города! Потом также оформлю результаты.
В опросе приняло участие 47 человек!
Получаются следующие цифры:
2 подписчика до 15 лет.
На третьем месте подписчики от 19 до 25 лет - в числе 8 проголосовавших.
Бо́льшая часть, а именно 19 человек в возрасте от 26 до 35.
Не отстают и подписчики от 36 до 45, их насчитывается 16.
Один подписчик в возрасте 46-55.
И одна(ин) стеснительный, пожелавший остаться неизвестным)
В комментарии к опросу пришло предложение сделать опрос по городам подписчиков.
Вместо опроса, пишите в комментариях из какого вы города! Потом также оформлю результаты.
Приветствую.
В комментариях к шуточному опросу про тестирование систем оповещения, зашла речь о бункерах. И мне вспомнился один хороший фильм на эту тему.
Посему, предлагаю ввести "пятничную" рубрику с фильмом на вечер от меня и с вашими вариантами в комментариях.
Фильм на сегодня: Взрыв из прошлого (1998).
Краткий синопсис: Адам Уэббер родился нормальным ребенком, но, по глупому недоразумению, провел 35 лет в бомбоубежище вместе со своими родителями. Наивный и искренний, он впервые оказывается среди людей в бушующем Лос-Анджелесе 90-х! И кто знает - может, он еще успеет наверстать упущенное?!
Посмотреть фильм можно по ссылке: https://www.sspoisk.ru/film/2408
Может быть у вас есть свой вариант фильма на вечер? Делитесь им в комментариях!
В комментариях к шуточному опросу про тестирование систем оповещения, зашла речь о бункерах. И мне вспомнился один хороший фильм на эту тему.
Посему, предлагаю ввести "пятничную" рубрику с фильмом на вечер от меня и с вашими вариантами в комментариях.
Фильм на сегодня: Взрыв из прошлого (1998).
Краткий синопсис: Адам Уэббер родился нормальным ребенком, но, по глупому недоразумению, провел 35 лет в бомбоубежище вместе со своими родителями. Наивный и искренний, он впервые оказывается среди людей в бушующем Лос-Анджелесе 90-х! И кто знает - может, он еще успеет наверстать упущенное?!
Посмотреть фильм можно по ссылке: https://www.sspoisk.ru/film/2408
Может быть у вас есть свой вариант фильма на вечер? Делитесь им в комментариях!
AIOgram3 15. Обработка события вступления или покидания чата
Последняя (на данный момент) задача - это приветствие нового участника чата и оповещение о том, что кто-то нас покинул.
Решение достаточно простое и пост будет коротким.
Последняя (на данный момент) задача - это приветствие нового участника чата и оповещение о том, что кто-то нас покинул.
Решение достаточно простое и пост будет коротким.
Обработка событий.
Откроем файл
Обе функции принимают
В теле функции напишем отправку сообщения в ответ на событие, обращаясь к методам
Код функций:
Регистрация обработчиков.
Переходим в файл
Файлы к посту, можно получить в боте по коду: 164296
Пост на сайте.
Поддержать канал.
#aiogram #гайды #python
Откроем файл
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
Django 31. Форма авторизации и кнопка выхода
Продолжаем развивать проект.
В этом и нескольких следующих постах, мы добавим возможность пользователям регистрироваться, авторизовываться на сайте и выходить с него, сбрасывать свой пароль, а также реализуем страницу с профилем пользователя.
И начнём мы с создания нового приложения.
Приложение user_app.
В прошлом посте я упоминал о важности разделения разных логических блоков на Django-приложения. Поэтому для пользователей сделаем отдельное приложение
Для создания нового приложения выполните в терминале следующую команду:
Создастся новая директория с основными файлами.
Сразу откроем файл
Продолжаем развивать проект.
В этом и нескольких следующих постах, мы добавим возможность пользователям регистрироваться, авторизовываться на сайте и выходить с него, сбрасывать свой пароль, а также реализуем страницу с профилем пользователя.
И начнём мы с создания нового приложения.
Приложение user_app.
В прошлом посте я упоминал о важности разделения разных логических блоков на Django-приложения. Поэтому для пользователей сделаем отдельное приложение
user_app.Для создания нового приложения выполните в терминале следующую команду:
python manage.py startapp user_app
Создастся новая директория с основными файлами.
Сразу откроем файл
settings.py и добавим новое приложение в список INSTALLED_APPS - 'user_app.apps.UserAppConfig',Авторизация.
Форма авторизации.
Создадим форму авторизации.
На самом деле, делать этого не обязательно.
Можно воспользоваться встроенной формой, собственно от которой будем наследоваться, однако тут встаёт вопрос с её отображением. У стандартной формы отсутствуют стили.
Можно конечно их прописать с помощью CSS-селекторов, но у меня в данный момент используется Bootstrap5.
Для того чтобы воспользоваться его возможностями, необходимо форме определить конкретные стили, а для этого нужно её переопределить.
В директории приложения
Создадим класс
Затем создадим два поля:
Первое будет
Первым атрибутом будет максимальная длина имени пользователя. По умолчанию в модели пользователя максимальная длина составляет 150 символов, укажем столько же или можете по своему усмотрению указать меньше.
Вторым будет название поля.
Третьим атрибутом будет виджет, в котором определим текстовое поле ввода. Внутри в качестве атрибутов присвоим полю класс, а также укажем текст внутри поля.
Второе поле
Первый атрибут такой же - максимальная длина, которая равна 128 символам.
Второй - название поля.
Третьим атрибутом определим виджет, в этот раз это будет поле ввода пароля. Точно также передаём в атрибутах класс и текст внутри поля.
Ниже создаём подкласс
Первым полем будет используемая модель пользователя. Поскольку мы пока используем стандартную модель, её и указываем. Если же вы используете расширенную модель пользователя, то указывайте её.
Вторым полем мы указываем поля формы. В нашем случае их всего два, имя пользователя и пароль.
Код:
Представление страницы авторизации.
Откроем файл
Напишем класс
В классе пропишем всего три поля:
-
-
-
Также переопределим метод
Код:
Форма авторизации.
Создадим форму авторизации.
На самом деле, делать этого не обязательно.
Можно воспользоваться встроенной формой, собственно от которой будем наследоваться, однако тут встаёт вопрос с её отображением. У стандартной формы отсутствуют стили.
Можно конечно их прописать с помощью 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