Код на салфетке
2.21K subscribers
747 photos
15 videos
2 files
790 links
Канал для тех, кому интересно программирование на Python и не только.

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

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
*️⃣ Код можно использовать в новых проектах. Если вся информация и действия компактно собраны в классе, то его можно использовать в других проектах, создавая соответствующие объекты уже в них.

Что такое self?

В примере выше в двух методах (то есть функциях внутри) класса Student в качестве аргумента используется объект self. Редко кто в подробностях рассказывает о том, что это за объект. Чаще говорят о том, что он позволяет делать: он даёт доступ к атрибутам (элементам информации) класса из любого места внутри самого этого класса. В нашем примере с классом Student мы в момент инициации (создания) объекта этого класса методом __init__ ввели атрибут self.name. Благодаря этому ниже – в методе say_hello, куда мы передали объект self, – мы смогли использовать этот же атрибут для того, чтобы ученик говорил приветствие и представлялся по имени.

Но что же такое self? Обычно говорят, что это ссылка на текущий экземпляр класса. Тот самый, который создаётся в момент инициализации. Вернёмся к нашему примеру с классом Student и воспользуемся приёмом, который знаком любому начинающему питонисту: в любой непонятной ситуации выводи всё в консоль print’ом!


class Student:

def __init__(self, name=""):
self.name = name
print(self) # <__main__.Student object at 0x1062c7790>

def say_hello(self):
print(f"Привет, я {self.name}!")


stan = Student("Стэн")
print(stan) # <__main__.Student object at 0x1062c7790>


Мы добавили два print’а:
*️⃣ вывод объекта self в методе __init__ сразу после записи имени ученика в атрибут self.name,
*️⃣ вывод экземпляра класса Student сразу после его создания и записи в переменную stan.

Что происходит в результате? В момент инициализации объекта класса Student, который мы записываем в переменную stan, на печать выводится объект self. Из записи <__main__.Student object at 0x1062c7790> видно, что этот объект класса Student, находящийся в модуле main, хранится в ячейке памяти за номером 0x1062c7790. Дальше на печать выводится объект в переменной stan, и мы видим, что это тот же самый объект, хранящийся в той же ячейке. Именно это и означает формулировка “ссылка на текущий экземпляр класса”.

Что еще важно знать про self?

*️⃣ Вместо self можно использовать любое другое название для переменной текущего объекта, но выбранного имени необходимо последовательного придерживаться в коде класса:


class Student:

def __init__(child, name=""):
self.name = name

def say_hello(child):
print(f"Привет, я {child.name}!")


При этом стоит помнить, что классическим и общепринятым вариантом всё же считается self. Об этом вам будут стараться напоминать даже некоторые IDE.

*️⃣ Есть способ увидеть все атрибуты, записанные в self. Для этого нужно обратиться к словарю __dict__, который ассоциирован с self:


class Student:

def __init__(self, name="", age=0):
self.name = name
self.age = age
print(self.__dict__)

def say_hello(self):
print(f"Привет, я {self.name}!")


stan = Student("Стэн", 8)

# {'name': 'Стэн', 'age': 8}


Что такое __init__()?

Последний незнакомый элемент синтаксиса, который появился в нашем первом примере, - это метод __init__, известный также как конструктор класса. Если упростить, то он служит для записи передаваемых при создании объекта данных в атрибуты объекта self. Так, например, мы передавали имя ученика при создании экземпляра класса Student.
🔥5
Может ли класс существовать без __init__? Да, прекрасно может. Каждый раз, когда создаётся новый экземпляр класса, Python ищет в классе метод __init__. Если он его находит, то метод запускается автоматически. Если нет, то никакие атрибуты в self не записываются. Именно поэтому важно не допускать ошибок при написании данного метода. Если, например, указать _init__ (с одним подчеркиванием) или __int__ (без второй “i”), передав в них аргументы, они скорее всего никогда не попадут в self, потому что автоматически метод при инициализации объекта вызван не будет. Вызвать же написанный с ошибкой метод вручную разработчик едва ли догадается.

Из всего указанного выше следует, что в качестве конструктора класса можно было бы использовать и созданный вручную метод. Разница будет лишь в том, что в отличие от __init__ он не будет вызван автоматически при создании экземпляра класса. Его нужно будет явным образом вызвать в коде, передав аргументы для записи в атрибуты уже в него:


class Student:

def initialize(self, name=""):
self.name = name

def say_hello(self):
print(f"Привет, я {self.name}!")


stan = Student()
stan.initialize("Стэн")
stan.say_hello() # Привет, я Стэн!


Некоторым IDE не понравится, что запись данных в атрибут класса происходит вне __init__, да и смысла в таком решении немного, но оно, тем не менее, возможно.

В следующий раз мы продолжим разговор о методах классов.

Пост на сайте
Поддержать проект

#Python #ООП #код_в_мешке #объект #атрибут #класс #init #self #метод
🔥9👍1
Приветствую!

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


Оглавления:
Для удобства навигации есть посты с оглавлениями по темам:

"Сайт на Django"
"Telegram-бот на AIOgram3"
"Применение Docker"
"Полезные инструменты"
"Вести с полей стажировки."
"Некий проект"
"Код в мешке"
"Boosty эксклюзив"


Ресурсы канала:

Уютный и немного безумный чат канала.
Бот с материалами к постам
Сайт со всеми постами
Канал в Dzen
Сообщество в VK


Поддержка.

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

Также поддержать канал можно на Boosty.

Или внеся сайт в исключения вашего блокировщика рекламы.
🔥5👍1
Приветствую, друзья!

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

Фильм: Искусственный разум

Год: 2001

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

https://www.sspoisk.ru/film/594/?utm_referrer=www.google.com

Приятного просмотра)
🔥4
Что выведет код с изображения ниже?
Final Results
21%
IndexError: list index out of range
66%
[]
3%
[10]
10%
[8, 10]
🤯2👍1😱1
Что выведет этот код? №6
🤯1😱1
Вчера была опубликована очередная интересная задача из рубрики "Что выведет данный код?". Задача вам понравилась, судя по вашей активности в комментариях. Сама задача не очень сложная, основная её сложность в понимании того, как работает цикл for. Зная принцип его работы, отследить процесс можно прямо в голове, не запуская код.

В викторине приняли участие 29 человек. Верных ответов было ... всего 3, а если убрать редакцию, то остаётся вовсе один. Кто же ты, единственный верный? Пришли скрин верного ответа в чат, впишем тебя в пост! Лидирующим вариантом стал второй, с пустым списком, а на втором месте ошибка выхода за границы списка.


Код задачи:
def delete_starting_evens(lst):
for _ in lst:
if lst[0] % 2 == 0:
lst.pop(0)
else:
break
return lst


my_list = [2, 6, 8, 10]

print(delete_starting_evens(my_list))



Структура задачи:
Сперва мы объявляем переменную my_list, записав в неё список с целыми числами.

Затем вызываем print, передавая в него функцию delete_starting_evens со списком в аргументе.

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

Так почему результат именно [8, 10], а не какой-нибудь другой?
Всё дело в устройстве работы цикла for, работу которого достаточно трудно порой отследить в голове, не вызывая print'ов. Особенно сложно приходится, когда список, по которому проходится цикл, сокращается в процессе работы цикла.

Цикл for работает "с начала и до победного", т.е., получив на вход список, он будет работать до тех пор, пока его не прервут или пока список не закончится.

Небольшое пояснение касательно переменной _. В питоне, если итерируемое значение цикла не используется, принято заменять его на символ нижнего подчёркивания - _. Однако при желании к этому символу также можно получить доступ как к любой другой переменной.


Рассмотрим полный цикл работы функции:
1. Функция получает на вход список [2, 6, 8, 10].
2. Значение переменной _ = 2 (нулевой элемент текущего списка). Список сейчас такой: [>2, 6, 8, 10]. > - указание на каком элементе находится цикл.
3. В блоке if мы проверяем чётный или нет 0-й элемент списка. Он чётный, удаляем его.
4. Значение переменной _ = 8 (первый элемент текущего цикла). Список сейчас такой: [6, >8, 10].
5. Снова проверяем является ли нулевой элемент чётным. Является, удаляем его.
6. Значение переменной _ = ? (Второй элемент текущего цикла). Список сейчас такой: [8, 10, >?]. Как видим, сейчас значение переменной _ выходит за границы списка в его текущем виде. Значит, мы дошли до конца.
7. Цикл for заканчивает свою работу не заходя внутрь.
8. Возвращаем список.
9. Выводим его на экран.


Заключение.
В данной задаче показан пример того, что будет если изменять размер списка ДО текущего положения счётчика. Он достаточно безобидный. Однако, если изменять размер списка ПОСЛЕ счётчика, могут возникнуть проблемы, особенно, если обращаться к стоящим после счётчика элементам, т.к. после изменения списка, он перестраивается и порядок индексации изменяется.
🔥7🤯2❤‍🔥1
Первый созвон...
Автор: Некий Вестник Сплетен

Время шло, и нужно было начинать работу, а с чего её начинать, никто не знал. Путём обсуждения в чате было решено, что нам нужен общий созвон. Удобное для большинства участников время решили выбрать путём голосования, постепенно сужая выбор предлагаемых вариантов. Таким образом созвон был назначен на субботу 28 октября 2023 года в 17-00.

Начали с обсуждения самой идеи проекта. Изначально вариантов было много, и все они были очень интересными (возможно, мы захотим реализовать в будущем и их). В обсуждении активно участвовала большая часть команды, из-за чего оно получилось очень живым. В итоге выбор пал на сервис поиска проектов или коллег для совместной работы (сайт - биржа фриланса).
😎3🔥2
Определившись с направлением проекта, мы приступили к подбору подходящего названия для проекта, название команды и аватарки для более удобного поиска чата в Telegram (всё общение в нашей команде проходит на созвонах и в чате тг).

Устроили голосование за название проекта. Выбирать предлагалось из следующих вариантов:
- DevMatch (Developer Match)
- ЯСделяль
- I try - You pay
- Command&Project hub
- KekProjekt
- iCommand
- iSearchCommand
- CommandHub

По итогам был выбран вариант DevMatch (Developer Match). Сперва хотели от него отказаться, так как такой сайт уже есть на зарубежном пространстве, но мы в РФ, что они нам сделают?

Следом запустили ещё два голосования: выбор аватарки нашего чата и название команды.

Варианты названий команд были самыми разными:
- Victims of Сourses
- Некий проект
- DevMatch Team
- Order or the Crooked Finger
- team of commanders
- little hard workers
- Команда Вечное сияние чистого разума
- Слабоумие и отвага

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

Далее стали думать над составлением ТЗ.
Один из участников нашёл в интернете образец-черновик технического задания, затем Андрей (BackEnd) скинул еще один вариант и мы начали работу над ТЗ, опираясь на эти файлы как на шаблоны.

Были внесены первые корректировки в ТЗ. На начальном этапе в основном этим занимались Иван и Антон.

Особо много там не написали, поскольку опыта в этом деле у всех было мало.

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

Оставался ещё один важный вопрос: в каком сервисе распределять задачи по участникам?

Евгением (UX/UI) было предложено использование сервиса YouGile для распределения задач. На нём мы и остановились. Пришлось изучать новые инструменты работы.

Итак, работа закипела.

Пост на сайте
Поддержать проект

#код_на_салфетке #техническое_задание #созвон #некий_проект #биржа_фриланса #название_проекта #командная_работа
🔥4😎1
Приветствую.

По постам "Некого Вестника" вы в курсе, что у нас есть "Некий Проект". Если простыми словами, то это биржа фриланса с упором на командную работу.

Мы ещё достаточно далеки хотя бы для MVP, поскольку особых сроков и дедлайнов у нас нет, но работа идёт.

Сейчас нам в команду требуются фронтэнд разработчики знакомые с Vue3.

Проект пока больше учебный, но не исключен и коммерческий запуск в будущем.

Если вы хотите применить свои знания на практике и поработать в команде таких же новичков, то напишите мне @proDreams
🎉5
Прошёл ещё месяц учебы, вернее не учёбы, а её завершения.

Настало время "отчётного" поста на Plkabu.

https://pikabu.ru/story/obuchenie_vosemnadtsatyiy_mesyats_posledniy_11058755?utm_source=linkshare&utm_medium=sharing

Расчехляйте лайкомёты 😊
🔥7
Django 40. Собственные страницы ошибок
Автор: Иван Ашихмин

Ошибки на сайте случаются постоянно, но далеко не все из них происходят по вине программиста. Самая знаменитая ошибка – 404 Not Found – относится к пользовательским ошибкам, когда был совершён переход на несуществующую страницу. Ещё одна известная ошибка – 500 Internal Server Error – относится к ошибкам на стороне сервера. Это означает, что сервер не справился с какой-то задачей.

Всё это называется "Кодом состояния HTTP" или по простому "HTTP статус кодом".

Всего их пять видов:
1. 1xx - информационные.
2. 2xx - успешные.
3. 3xx - перенаправления.
4. 4xx - ошибки клиента.
5. 5xx - ошибки сервера.

Полный список кодов с описанием доступен в википедии: https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP

Нас интересуют 4xx и 5xx статус коды, проще говоря – ошибки.
🔥4
Мы с вами сделаем страницы отображающиеся при следующих ошибках:
- 400 Bad Request – когда был отправлен неправильный запрос или данные.
- 403 Forbidden – когда пользователь пытается получить доступ к страницам сайта, на просмотр которых у него нет разрешения.
- 404 Not Found – когда пользователь переходит на несуществующую страницу.
- 500 Internal Server Error – когда на стороне сервера что-то пошло не так и поднялась необработанная ошибка.


Утилитарное приложение utils_app.
Для обработки ошибок нам необходимо написать обработчики. Дабы не писать их в основных приложениях, создадим новое utils_app.
Для этого выполним команду: python manage.py startapp utils_app.

Не забудьте сразу добавить новое приложение в INSTALLED_APPS


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

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

Создадим четыре функции:
- bad_request_handler - для обработки 400-й ошибки.
- permission_denied_handler - для обработки 403-й ошибки.
- page_not_found_handler - для обработки 404-й ошибки.
- server_error_handler - для обработки 500-й ошибки.

Каждая функция принимает два аргумента request и exception. Обратите внимание, что аргумент exception не всегда передаётся в обработчик, по этому устанавливаем ему значение по умолчанию None.

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


Код функций:
from django.shortcuts import render  
from rest_framework import status


def bad_request_handler(request, exception=None):
return render(request, "utils_app/400.html", status=status.HTTP_400_BAD_REQUEST)


def permission_denied_handler(request, exception=None):
return render(request, "utils_app/403.html", status=status.HTTP_403_FORBIDDEN)


def page_not_found_handler(request, exception=None):
return render(request, "utils_app/404.html", status=status.HTTP_404_NOT_FOUND)


def server_error_handler(request, exception=None):
return render(request, "utils_app/500.html", status=status.HTTP_500_INTERNAL_SERVER_ERROR)



Шаблон страницы с ошибкой.
Нам нужно четыре шаблона. Создадим в директории с шаблонами новую директорию для приложения utils_app и в ней файлы шаблонов.

У меня четыре шаблона. Все четыре одинаковые, за исключением текста, поэтому покажу пример только одного шаблона:
{% extends "blog/base.html" %}
{% block title %}404 - страница не найдена!{% endblock %}

{% block content %}
<div class="d-flex align-items-center justify-content-center vh-100">
<div class="text-center">
<h1 class="display-1 fw-bold">404</h1>
<p class="fs-3"><span class="text-danger">Ой!</span> Страница не найдена.</p>
<p class="lead">
Запрашиваемая вами страница не найдена.
</p>
<a href="{% url 'blog:index' %}" class="btn btn-primary my-btn">Вернуться на главную</a>
</div>
</div>
{% endblock %}


Выводим текст соответствующий статус-коду.


Подключение обработчиков.
Осталось подключить обработчики. Для этого откроем главный файл urls.py, расположенный в директории проекта рядом с файлом settings.py.
🔥5
В нём в конце файла добавляем четыре переменные. Значением переменных будет строка вида "название_приложения.файл_с_обработчиками.название_функции_обработчика".

handler400 = "utils_app.exception_handlers.bad_request_handler"  
handler403 = "utils_app.exception_handlers.permission_denied_handler"
handler404 = "utils_app.exception_handlers.page_not_found_handler"
handler500 = "utils_app.exception_handlers.server_error_handler"


Обратите внимание! Чтобы собственные страницы ошибок заработали, параметр DEBUG в файле settings.py должен быть установлен в False!


Заключение.
Вот таким вот простым и нехитрым способом можно сделать собственные страницы ошибок в Django.

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

Пост на сайте
Поддержать проект

#Python #Django #гайды #Bad_Request #страницы_ошибок #обработчики_ошибок #Forbidden #Internal_Server_Error #Not_Found
🔥8👍1
Что выведет код с изображения ниже?
Anonymous Quiz
16%
3
8%
2
45%
[0, 1, 2]
31%
TypeError
👍4🤯3
Что выведет этот код? №7
Вчерашняя задача - это яркий пример полиморфизма в ООП. Суть в том, что мы создаём "класс-потомок" и изменяем его поведение относительно наследуемого класса, но обо всём по порядку.

В викторине приняли участие 31 человек из них верно ответили всего 5. Большая часть сошлись во мнении, что должна выйти ошибка TypeError. Вторым популярным ответом стал [0, 1, 2], означающий, что должен вывестись весь список.


Код задачи:
class MyList(list):
def __init__(self, *args):
super().__init__()
for i in args:
self.append(i)

def __hash__(self):
return 1

def __str__(self):
return str(self.__len__())


lst = MyList(0, 1)
lst.append(2)
dct = {lst: lst}
print(dct[lst])



Разберём задачу.
Начнём с класса. Мы создали класс MyList и унаследовали его от встроенного в Python класса list. Этот класс, экземпляры хорошо известной нам структуры данных - списка.

В нашем вновь созданном классе мы переопределяем три метода:
- Конструктор класса __init__ – в этом методе инициализируем super-класс, т.е. вызываем метод __init__ из родительского класса. Далее в цикле добавляем все переданные позиционные аргументы в список, используя метод append, который мы унаследовали из родительского класса привычного нам списка.
- dunder-метод __hash__ – данный метод возвращает хэш-значение объекта класса. Если тип данных не хэшируется (как, например, типа данных список), то возвращается None. В нашем случае мы возвращаем значение 1, что соответствует True.
- dunder-метод __str__ – данный метод возвращает строковое представление объекта класса. Для списков это - "[1, 2, 3]", в нашем случае возвращается строка с указанием длинны списка (за это отвечает dunder-метод __len__).

После класса идёт ряд действий:
1. В переменную lst помещается объект созданного нами только что класса MyList с аргументами (0, 1). Тем самым формируется список [0, 1] (здесь и дальше мы будем употреблять слово "список" условно, называя так объекты нашего авторского класса, унаследованного от списка).
2. К списку добавляется число 2, получается следующий список - [0, 1, 2].
3. В переменную dct помещаем словарь, в котором ключ и значение - это значение, записанное в переменную lst (то есть объект нашего авторского класса со списком [0, 1, 2]).
4. Последним шагом выводим значение словаря передав объект из переменной lst как ключ.

Почему удалось добавить список как ключ?
Тут всё достаточно просто. Действительно, словарь не даёт делать ключом нехэшируемые объекты, и на то есть свои причины: списки и другие изменяемые типы данных не хэшируются, поскольку изменение приводило бы к изменению их хэша, а словари хранят ключи в хэш-таблице. Таким образом, записав значение по одному ключу, который в последствии изменится, мы не сможем получить записанные данные.

Что касается нашего случая, то когда мы переопределили dunder-метод __hash__, мы изменили поведение класса list, сделав его хэшируемым. Именно это позволило использовать переменную со списком в качестве ключа.

Почему в выводе 3?
И тут ответ "на поверхности". Когда для вывода объектов используется функция print, вызывается dunder-метод __str__. Мы переопределили его, возвращая вместо стандартного строкового представления списка – его длину.

Заключение.
Вот такая задачка у нас вышла. В задаче показан пример полиморфизма в действии, когда класс list изменяет своё поведение, будучи унаследован пользовательским классом.
🔥9🤯1
AIOgram3 16. Перевод голосовых сообщений в текст
Автор: Иван Ашихмин

Голосовые сообщения – весьма спорная тема. Кто-то без них жить не может, а кто-то ненавидит их всей душой. Если опустить прения на эту тему, то у голосовых сообщений есть одна проблема: их банально не всегда удобно слушать. Чаще всего голосовые сообщения – это "информация в моменте", и, когда появляется возможность прослушать их, они теряют свою актуальность.

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

Нейронных сетей, заточенных под распознавание голоса, свыше десятка, но они требуют серьёзных вычислительных мощностей, чего мой не самый мощный сервер не может предоставить.
🔥5😁1😱1
Готовые сервисы тоже есть, но практически все - платные. Не беря в расчёт "пробные периоды" из бесплатных (вернее с бесплатным лимитом в месяц) есть два сервиса:

- Google Cloud - думаю не стоит рассказывать о Google. Они предлагают бесплатный тариф - 60 минут в месяц.
- SpeechFlow - сервис, позиционирующий себя как "лидера рынка". Поддерживают транскрипцию с 14-ти языков. Предлагают бесплатный тариф: 30 минут онлайн-распознавания (на сайте) и пять часов по API в месяц.

Сервис от Google нам не подходит: 60 минут на целый месяц слишком мало, поэтому выбор был сделан в пользу SpeechFlow. Пять часов в месяц – тоже не то чтобы много, но с этим уже можно работать. Ну, и будет интересно посмотреть статистику длительности голосовых и их количества.


Регистрация и получение API-ключа.
Переходим на сайт сервиса: https://speechflow.io/ru/

Регистрация максимально проста, особенно если заходить через Google-аккаунт.

После регистрации попадаем в личный кабинет. Там в левой панели выбираем раздел "API".
На открывшейся странице нажимаем кнопку "Сгенерировать ключ API".
В появившемся окне будет две строки:

- KeyId - идентификатор ключа
- KeySecret - секретный ключ
ВНИМАНИЕ! Ключ отображается всего один раз! Сохраните его в удобном для вас месте.

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


Отслеживание и расшифровка голосовых сообщений.
В посте "AIOgram3 14. Фильтруем запрещённые слова" мы писали обработку для проверки сообщений на запрещённые слова. В этом посте мы дополним ее функционалом проверки голосовых сообщений.

Но сперва добавим ключи в класс Secrets.

Откроем файл settings.py и в классе Secrets добавим две строки:
audio_key_id: str = "ваш_KeyId"  
audio_key_secret: str = "ваш_KeySecret"


В значение полей вставьте полученные от SpeechFlow данные доступа.


Обработка голосовых сообщений.
Откройте файл filter_words.py.

Тут у нас уже есть функция check_message с условием if message.text. Для того, чтобы "отлавливать" голосовые сообщения, нужно другое условие - if message.voice.

Добавим его ниже, использовав elif.

Сперва нам необходимо скачать голосовое сообщение с сервера Telegram. Для этого выполним несколько шагов:
1. Создадим переменную file_id, в которую получим идентификатор голосового сообщения.
2. Создадим переменную file, в которую асинхронно получим информацию о файле голосового сообщения.
3. Создадим переменную file_path, в которую получим путь до файла.
4. Создадим переменную file_name, в которой укажем путь и имя сохраняемого файла на нашем сервере. Убедитесь, что директория, в которой вы собираетесь сохранять файл, существует.
5. Асинхронно выполним команду скачивания файла.

Получается вот такой блок кода:
file_id = message.voice.file_id  
file = await bot.get_file(file_id)
file_path = file.file_path
file_name = f"files/audio{file_id}.mp3"
await bot.download_file(file_path, file_name)


Создаём четыре переменные, которые понадобятся далее в коде:
1. headers – в этой переменной создаём словарь с ключами keyId и keySecret, в которые передаём значения соответствующих полей из класса Secrets.
2. create_url – в этой переменной определяем URL-адрес, на который отправляем файл для транскрипции. Обратите внимание на query-параметр lang: в нём указываем язык голосового сообщения.
👍3🔥1😱1
3. query_url – в этой переменной определяем URL-адрес, на который будем обращаться для получения результата транскрипции.
4. files – в этой переменной создаём словарь с ключом file, в значение которого помещаем открытый аудиофайл.

Блок кода:
headers = {"keyId": Secrets.audio_key_id, "keySecret": Secrets.audio_key_secret}  
create_url = "https://api.speechflow.io/asr/file/v1/create?lang=ru"
query_url = "https://api.speechflow.io/asr/file/v1/query?taskId="
files = {"file": open(file_name, "rb")}


Далее отправляем POST-запрос на адрес create_url и записываем ответ в переменную response.
Если статус-код ответа равен статус-коду 200, проваливаемся внутрь условия. Иначе завершаем выполнение обработки.

Внутри условия в переменную create_result получаем JSON ответа.
Затем к переменной query_url добавляем идентификатор задачи из create_result и query-параметр resultType.
Всего есть четыре значения resultType:

1. Тип результата по умолчанию: формат JSON для предложений и слов с указанием начального и конечного времени.
2. Формат JSON для сгенерированных субтитров с указанием начального и конечного времени.
3. Формат SRT для сгенерированных субтитров с указанием начального и конечного времени.
4. Чистый текстовый формат для результатов транскрипции без указания начального и конечного времени.

Для ответа в чате лучше всего подходит 4-й вариант.

Блок кода:
response = requests.post(create_url, headers=headers, files=files)  
if response.status_code 200:
create_result = response.json()
query_url += create_result["taskId"] + "&amp;resultType=4"


Запускаем бесконечный цикл.
Внутри цикла отправляем GET-запрос на адрес query_url и сохраняем ответ в переменную response.
Если статус-код ответа равен 200, проваливаемся внутрь условия. Иначе прерываем цикл и завершаем выполнение обработки.

В переменную query_result получаем JSON ответа.

Далее у нас два условия:

Если в query_result по ключу code лежит значение 11000, значит, транскрипция завершена.
Проваливаемся внутрь условия.

Проверяем, что в query_result по ключу result есть данные, иначе прерываем цикл. Такое может произойти, когда сервису не удалось распознать речь на голосовом или если оно было без речи вовсе.

В переменную result получаем значение ключа result из query_result. Для удобства можно дополнительно применить метод .replace(), чтобы заменить два переноса строки, например, на пробел.
Асинхронно отправляем ответ на голосовое сообщение.
Удаляем файл с сервера.
Прерываем цикл.

Иначе-если (elif) в query_result по ключу code лежит значение 11001, значит транскрипция ещё не завершена и стоит попробовать чуточку позже.
Выставляем сон на три секунды и переходим к следующей итерации цикла.

Если оба условия не были удовлетворены, то мы прерываем цикл.


Полный код:
🔥4😱1