Дневник разработчицы
407 subscribers
111 photos
9 videos
14 files
145 links
Откровенные истории о проектах, работе, учебе и открытиях в мире программирования. Присоединяйтесь и вдохновляйтесь вместе со мной!
Download Telegram
В одном из моих проектов есть функционал сбора новостей из разных rss-каналов. Да, rss все еще живы. Я обрабатываю более 140 источников. Тут наткнулась на сайт https://sledcom.ru, увидела у них rss, но, видимо, не судьба... Все мои попытки достучаться до них были обречены на провал.

В браузере все открывается ок, на сертификат нареканий нет, а вот при запросе через requests падает ошибка [SSL: WRONG_SIGNATURE_TYPE]. Все мои попытки отключить валидацию сертификата были провальными. Мои попытки и ошибка: https://pastebin.com/DXnFZidT

Такие дела.
Недавно в своем проекте поженила асинхронный telethon с django3.1. Авторизацию сделала на коленке через админку.
Честно, понимаю, что можно было бы нормально через view с шаблончиками, но не хотелось заморачиваться. Поэтому сделала очень примитивно с помощью логики, зашитой в обработку полей после сохранения нового объекта https://pastebin.com/SJSwhNYG

Жутенький код, конечно, но он работает. Локально все заработало прекрасно. Я авторизовала несколько клиентов, запустила их, начала получать от них сообщения (с помощью другого метода).

После деплоя на сервер от aws (я использую elastic beanstalk на базе docker linux) я столкнулась с тем, что, судя по логам, код отправляется, телеграмм возвращает корректный ответ на запрос отправки кода, но вот до пользователя он не доходит. И я такая не одна, судя по гуглу. Создатель библиотеки предлагает создавать сессии локально и загружать их на сервер. Я так и сделала, конечно, в итоге, но это ужасно неудобно.(
Вот описание проблемы на стаковерфлоу https://stackoverflow.com/questions/61293179/code-is-not-delievered-by-sendcoderequest-without-error-and-this-happens-only-on
👍1
Продолжая тему rss и асинхронных задач в django. История про aiohttp и django3.1. Изначально в проекте я сделала логику, в рамках которой периодически запускалась задача на парсинг одной rss-ленты (не чаще, чем раз в минуту). Все мои 140+ rss обходились за 7-12 минут. В принципе, результат был более ли менее. Но потом я начала изучать asyncio и решила попробовать как оно будет работать с rss.
Переделала логику https://pastebin.com/uUheywAE
Задача на парсинг также запускает через celery, как и раньше. Но только запускаю я ее раз в 30 секунд. Получение данных, парсинг и сохранение в бд выполняется за 5-20 секунд (как правило, не все rss сразу). Теперь все rss обрабатываются в течение 2 минут.
Не все rss одинаково классно проходят с помощью этого метода. Некоторые падают по таймауту (единицы). Ради них пришлось оставить старый метод, который добирает остатки необработанных rss.

Хочется выделить курс https://www.udemy.com/course/bestpython/, где автор довольно подробно разобрал тему многопоточности в Python. Хотя, признаю, далеко не все понято с первого раза. Но завтра будет лучше... #Python, #Django
Мои конспекты в Xmind. Они постоянно дополняются и развиваются. Всем рекомендую эту программу, удобно конспектировать, удобно возвращаться к необходимой информации. #Tools
Я за сегодня сделала только 1 маленькую вещь: сделала в боте для генерации utm-меток обрезание всех параметров в исходной ссылке.

Да, у меня есть простой бот в телеграмме для smm-специалистов. Они отправляют боту голые ссылки, а бот им отвечает списками этих ссылок по соответствующими utm-метками для разных соц.сетей. Простая в реализации штука. Для нее нужно уметь работать с api телеграмм https://core.telegram.org/bots/api, также я использовала регулярные выражения для получения ссылки (https://regexr.com/3h8nh здесь можно найти готовые шаблоны и потестировать свои), для хранения и изменения списка меток использую табличку в виде csv-файла. Приложение крутится в AWS Lambda. Я его сделала за пару дней через пару месяцев изучения программирования. Это был уже не первый мой подобный проект.

В общем, в логах к моему приложению было обнаружено, что ребята присылают на вход ссылку с чужими utm-метками. Конечно, неправильно рубить все параметры url, так как там может быть что-то полезное, а стоило бы обрабатывать эту ситуацию с помощью условий, мягче, оставляя полезные параметры. Но, я знаю, что на проекте нет таких, поэтому не стала заморачиваться. Если вдруг понадобится обходить параметры, всегда можно доработать. А так, три строчки кода и проблема решена.

О таких нюансах можно не знать на старте, но при этом на самом деле не страшно запустить приложение с "проблемами", если им уже можно пользоваться и оно уже может решать какие-то задачи эффективнее.

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

Вот пример гайда как сделать простого бота на aws https://dev.to/nqcm/-building-a-telegram-bot-with-aws-api-gateway-and-aws-lambda-27fg
FYI вместо библиотеки requests я использовала встроенную urllib.request, что позволяет делать без дополнительных зависимостей.

https://github.com/mandrianova/telegram_bot а это мой первый проект, который я развиваю до сих пор, но уже на другом технологическом стеке и в рамках другого репозитория. В данном репозитории можно как раз подсмотреть логику работы с lambda. #Работа, #AWS
👍1🔥1
С каждой задачей я все больше начинаю понимать, меньше допускать примитивных ошибок, некоторые вещи перестают быть страшными.

Продолжаю использовать telethon в django. Сегодня делала функционал получения подписок и саму подписку на каналы.


telegram_client = create_client(client.session)
with telegram_client:
dialogs = telegram_client.get_dialogs()
...


def subscribe(client: TelegramBotClient, channel: Channel):
telegram_client = create_client(client.session)
with telegram_client:
result = telegram_client(functions.channels.JoinChannelRequest(f"@{channel.username}"))
add_log("join channel", result)


Простые, казалось бы функции, но для меня стало открытием, что можно использовать with, чтобы создать подключение на время выполнения подписки и получения списка диалогов. Если что это это условно синхронные функции telethon, которые на самом деле также используют asyncio.

Кстати, под капотом create_client зашито создание отдельного event loop для клиента:


api_id = os.getenv('TG_API_ID')
api_hash = os.getenv('TG_API_HASH')
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
client = TelegramClient(StringSession(session), int(api_id), api_hash, loop=loop)
client.parse_mode = 'html' # <- Render things nicely
return client


Теперь можно будет немного поправить функцию авторизации, так как теперь я знаю магию with.
Плюс заметила в логах, что я криво сделала сбор сообщений в каналах, так как на каждое сообщение я запрашивала информацию о чате с помощью бота телеграмма.
Переделала логику, стало немного лучше. Теперь я сначала проверяю наличие чата в своей бд.

Итого за сегодня:
- Примерно набросала в блокноте список функций для подписки
- Создала модель для хранения подписок и добавила ее в админку
- Сделала функцию получения списка подписок для клиента и собрала существующие подписки,
- Написала функцию для подписки и проверила ее, завтра по аналогии допишу функцию отписки

На завтра:
- Доделать логику подписки и отписки, сделать возможность управления через админку
- Автоматизировать подписки на новые каналы
- Выкатить новый функционал "на прод" #Работа
Сегодня уж очень медленно работает подключение к aws. И вот, пока мои обновления пытаются выехать на мой условный прод, я напишу о сегодняшнем дне.

Мой план был доделать подписки и я их доделала. Осталось вот задеплоить.

Теперь можно подписываться и отписываться от каналов вручную и автоматически. Кстати, у телеграмма есть какое-то ограничение на количество подписок в какой-то период времени. После 30 каналов он выплюнул мне предупреждение во флуде и ограничил меня на 300 секунд. Поэтому придется как-то плавно обрабатывать всю ту массу каналов, на которые хочется подписаться.

Меня тут разговоры про ООП заставили задуматься о том, что я с безумной радостью почти весь код пишу функциями. Я подсмотрела в ютубе на канале Диджитализируй идею про то, что логику в Django стоит выносить в папку services, я ее создала и разразилась там в своих проектах на джанге множеством функций. Так вот, сегодня я сделала странную вещь. Не знаю на сколько это вообще легитимно (скорее всего не очень), но я в класс модели для телеграмм клиента перенесла все базовые функции для клиента (подключение, авторизацию, проверку логина, отправку кода, подписку, отписку). Это так удобно, хотя модель стала просто жутко жирной.

Еще один момент про telethon. Ранее я писала про открытие с with. Так вот, сегодня решила отрефакторить свою функцию авторизации и выяснила, что с with библиотека пытается стартануть клиент и требует авторизацию, если нет авторизованной сессии. Пришлось все неавторизованные функции оборачивать самостоятельно в connect и disconnect. Например:



client = self.client
client.connect()
sent = client.send_code_request(phone=self.phone)
add_log(f'sent code {self.phone}', str(sent), 1)
self.session = StringSession.save(client.session)
TelegramBotClient.objects.filter(id=self.id).update(session=self.session)
client.disconnect()
Пока пишу, приложение задеплоилось. Первый выкат прошел не очень, так как я накосячила с миграциями. Решила поправить один момент, сделать проверку на уникальность для связки двух полей в модели сообщений (message_id и chat). Так как в теории в базе могли возникнуть дубли, то в миграции добавила функцию по дедубликации. Так вот, извлекла урок:
- тяжелые миграции лучше делать отдельным файлом миграций. Создать ручную миграцию можно с помощью команды
python manage.py makemigrations --empty yourappname
Про свои функции в миграциях можно почитать тут https://docs.djangoproject.com/en/3.1/ref/migration-operations/#runpython
Я их уже делала ранее для индексации текстов материалов для полнотекстового поиска. Я создала поле SearchVectorField и мне надо было его наполнить для старых материалов.

В этот раз я нашла на просторах интернета готовое решение по дедубликации материалов. Не очень красивое (очень некрасивое), но мне лень было придумывать самой:



Entry = apps.get_model("telegram", "Message")
for row in Entry.objects.all().reverse():
if Entry.objects.filter(message_id=row.message_id, chat=row.chat).count() > 1:
row.delete()
Функция выполняется довольно долго, поэтому при деплое все пошло не так. В логах информации об ошибках не было, только заглянув в таблицу django_migrations я увидела, что моя миграция не прошла. Впрочем, просто сделав запрос в бд, я поняла, что на проде такая дедубликация мне бы и не понадобилась. В итоге я просто повторно выкатила приложение и на второй раз все завелось успешно. Единственное, что я сделала, это перенесла операцию по дедубликации в конец списка, хотя не думаю, что это имело какой-либо смысл. Со второго раза миграции выполнились. #Работа, #AWS, #Django
Ура! Я за 1,5 месяца наконец-то перенесла весь изначальный функционал проекта полностью на сервер. То, что было сделано на коленке за несколько дней и запущено в виде простеньких скриптов на локальном компе, наконец-таки более ли менее оформлено в нормальную управляемую систему. А ведь изначально этим "сервисом" можно было управлять через файл конфига, а запуск проходил с помощью старта скрипта с бесконечным циклом.

Можно открывать шампанское и переходить к улучшению интерфейсов.

Следующий мой шаг - сделать управление через телеграмм бота, чтобы пользователям не нужно было заходить в админку. #Работа
Я - чертов графоман)
😁1