mirror of
https://github.com/ijaric/voice_assistant.git
synced 2025-05-24 14:33:26 +00:00
feat: [#49] tg_bot
This commit is contained in:
parent
bc88ceffec
commit
8486d2e0bf
3
src/bot_aiogram/Makefile
Normal file
3
src/bot_aiogram/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
include ../../common_makefile.mk
|
||||
|
||||
PROJECT_FOLDERS = tgbot
|
3
src/bot_aiogram/poetry.toml
Normal file
3
src/bot_aiogram/poetry.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[virtualenvs]
|
||||
create = true
|
||||
in-project = true
|
142
src/bot_aiogram/pyproject.toml
Normal file
142
src/bot_aiogram/pyproject.toml
Normal file
|
@ -0,0 +1,142 @@
|
|||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["poetry-core"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ['py311']
|
||||
|
||||
[tool.isort]
|
||||
known_first_party = ["backend", "tests"]
|
||||
line_length = 120
|
||||
profile = "black"
|
||||
py_version = "311"
|
||||
|
||||
[tool.poetry]
|
||||
authors = ["jsdio@jsdio.ru"]
|
||||
description = ""
|
||||
name = "fastapi_project"
|
||||
readme = "README.md"
|
||||
version = "0.1.0"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
aiogram = "~2.18"
|
||||
environs = "~9.0"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
python = "^3.11"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^23.7.0"
|
||||
isort = "^5.12.0"
|
||||
pylint = "^2.17.5"
|
||||
pylint-pydantic = "^0.2.4"
|
||||
pylint-pytest = "^1.1.2"
|
||||
pyright = "^1.1.318"
|
||||
pyupgrade = "^3.10.1"
|
||||
ruff = "^0.0.282"
|
||||
sort-all = "^1.2.0"
|
||||
toml-sort = "^0.23.1"
|
||||
|
||||
[tool.pylint]
|
||||
disable = [
|
||||
"broad-except",
|
||||
"cannot-enumerate-pytest-fixtures",
|
||||
"consider-using-from-import",
|
||||
"consider-using-sys-exit",
|
||||
"duplicate-code",
|
||||
"fixme",
|
||||
"missing-docstring",
|
||||
"no-member",
|
||||
"protected-access",
|
||||
"too-few-public-methods",
|
||||
"too-many-instance-attributes",
|
||||
"too-many-locals",
|
||||
"too-many-statements",
|
||||
"unnecessary-ellipsis"
|
||||
]
|
||||
extension-pkg-allow-list = [
|
||||
"orjson",
|
||||
"pydantic"
|
||||
]
|
||||
ignore-path = [
|
||||
"^.*venv/.*$"
|
||||
]
|
||||
load-plugins = [
|
||||
"pylint_pydantic",
|
||||
"pylint_pytest"
|
||||
]
|
||||
max-args = 15
|
||||
max-line-length = 120
|
||||
recursive = true
|
||||
|
||||
[tool.pylint.basic]
|
||||
argument-rgx = "^_{0,2}[a-z][a-z0-9_]*$"
|
||||
attr-rgx = "^_{0,2}[a-z][a-z0-9_]*$"
|
||||
class-attribute-rgx = "^_{0,2}[a-zA-Z][a-zA-Z0-9_]*$"
|
||||
variable-rgx = "^_{0,2}[a-z][a-z0-9_]*$"
|
||||
|
||||
[tool.pyright]
|
||||
exclude = [
|
||||
".pytest_cache",
|
||||
".venv"
|
||||
]
|
||||
pythonPlatform = "All"
|
||||
pythonVersion = "3.11"
|
||||
reportConstantRedefenition = "none"
|
||||
reportMissingTypeStubs = "none"
|
||||
reportPrivateUsage = "information"
|
||||
reportPropertyTypeMismatch = "warning"
|
||||
reportUninitializedInstanceVariable = "warning"
|
||||
reportUnknownMemberType = "none"
|
||||
reportUnnecessaryTypeIgnoreComment = "warning"
|
||||
reportUntypedFunctionDecorator = "warning"
|
||||
typeCheckingMode = "strict"
|
||||
useLibraryCodeForTypes = true
|
||||
venv = ".venv"
|
||||
venvPath = "."
|
||||
|
||||
[tool.ruff]
|
||||
ignore = [
|
||||
# Pyright automatically infers the type of `self`
|
||||
"ANN101",
|
||||
# Pyright automatically infers the type of `cls`
|
||||
"ANN102",
|
||||
# In some cases actively detrimental; somewhat conflicts with black
|
||||
"COM",
|
||||
# Ignore missing docstrings
|
||||
"D102",
|
||||
# In combination with D213, this results in noisy diffs and inconsistencies
|
||||
# See also <https://github.com/charliermarsh/ruff/issues/4174>.
|
||||
"D200",
|
||||
# This results inconsistencies between function and class docstrings
|
||||
# See also <https://github.com/charliermarsh/ruff/issues/4175>.
|
||||
"D202",
|
||||
# D211 is preferred since the extra blank line isn't visually useful
|
||||
"D203",
|
||||
# D213 is preferred since it's more readable and allows more characters
|
||||
"D212",
|
||||
# Ignore missing docstrings
|
||||
"D414",
|
||||
# Covered by D401, which is more restrictive
|
||||
"D415",
|
||||
# Type-checkers interpret redundant `as` as exporting an item
|
||||
"PLC0414",
|
||||
# Permit using alias for 'import'
|
||||
"PLR0402",
|
||||
# Causes churn and awful looking import blocks for little gain
|
||||
"TCH"
|
||||
]
|
||||
select = ["ALL"]
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"tests/*" = [
|
||||
"D100",
|
||||
"D103",
|
||||
"D104",
|
||||
"S101"
|
||||
]
|
||||
|
||||
[tool.tomlsort]
|
||||
all = true
|
||||
ignore_case = true
|
||||
in_place = true
|
33
src/bot_aiogram/tgbot/handlers/voice.py
Normal file
33
src/bot_aiogram/tgbot/handlers/voice.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import io
|
||||
|
||||
import aiogram
|
||||
import aiohttp
|
||||
|
||||
import tgbot.settings as tgbot_settings
|
||||
|
||||
|
||||
async def voice_response(message_voice: aiogram.types.Message):
|
||||
config: tgbot_settings.Settings = message_voice.bot.get("config")
|
||||
voice_file_id: str = message_voice.voice.file_id
|
||||
file_info = await message_voice.bot.get_file(voice_file_id)
|
||||
file_path: str = file_info.file_path
|
||||
voice_data: io.BytesIO = io.BytesIO()
|
||||
voice_data.name = "voice.ogg"
|
||||
voice_data.seek(0)
|
||||
await message_voice.bot.download_file(file_path, destination=voice_data)
|
||||
await message_voice.bot.send_chat_action(message_voice.from_user.id, "typing")
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{config.api.api_url}/api/v1/voice/",
|
||||
data={"voice": voice_data},
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
voice_answer = await resp.read()
|
||||
await message_voice.answer_voice(voice_answer)
|
||||
else:
|
||||
await message_voice.answer("Not recognized text")
|
||||
await session.close()
|
||||
|
||||
|
||||
def register_voice_response(dp: aiogram.Dispatcher):
|
||||
dp.register_message_handler(voice_response, content_types=aiogram.types.ContentType.VOICE)
|
4
src/bot_aiogram/tgbot/misc/utils.py
Normal file
4
src/bot_aiogram/tgbot/misc/utils.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import pathlib
|
||||
|
||||
BASE_PATH = pathlib.Path(__file__).parent.parent.parent.parent.resolve()
|
||||
ENV_PATH = BASE_PATH / ".env"
|
9
src/bot_aiogram/tgbot/settings.py
Normal file
9
src/bot_aiogram/tgbot/settings.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import pydantic
|
||||
import pydantic_settings
|
||||
|
||||
import tgbot.split_settings as app_split_settings
|
||||
|
||||
|
||||
class Settings(pydantic_settings.BaseSettings):
|
||||
api: app_split_settings.ApiSettings = pydantic.Field(default_factory=app_split_settings.ApiSettings)
|
||||
tgbot: app_split_settings.TgBotSettings = pydantic.Field(default_factory=app_split_settings.TgBotSettings)
|
7
src/bot_aiogram/tgbot/split_settings/__init__.py
Normal file
7
src/bot_aiogram/tgbot/split_settings/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from .api import *
|
||||
from .tgbot import *
|
||||
|
||||
__all__ = [
|
||||
"ApiSettings",
|
||||
"TgBotSettings",
|
||||
]
|
20
src/bot_aiogram/tgbot/split_settings/api.py
Normal file
20
src/bot_aiogram/tgbot/split_settings/api.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import pydantic_settings
|
||||
|
||||
import tgbot.split_settings.utils as split_settings_utils
|
||||
|
||||
|
||||
class ApiSettings(pydantic_settings.BaseSettings):
|
||||
model_config = pydantic_settings.SettingsConfigDict(
|
||||
env_file=split_settings_utils.ENV_PATH,
|
||||
env_prefix="API_",
|
||||
env_file_encoding="utf-8",
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
url: str
|
||||
port: int
|
||||
protocol: str
|
||||
|
||||
@property
|
||||
def api_url(self) -> str:
|
||||
return f"{self.protocol}://{self.url}:{self.port}"
|
22
src/bot_aiogram/tgbot/split_settings/tgbot.py
Normal file
22
src/bot_aiogram/tgbot/split_settings/tgbot.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import pydantic
|
||||
import pydantic_settings
|
||||
|
||||
import tgbot.split_settings.utils as split_settings_utils
|
||||
|
||||
|
||||
class TgBotSettings(pydantic_settings.BaseSettings):
|
||||
model_config = pydantic_settings.SettingsConfigDict(
|
||||
env_file=split_settings_utils.ENV_PATH,
|
||||
env_prefix="BOT_",
|
||||
env_file_encoding="utf-8",
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
token: pydantic.SecretStr = pydantic.Field(
|
||||
default=..., validation_alias=pydantic.AliasChoices("token", "bot_token")
|
||||
)
|
||||
admins: str = pydantic.Field(default="")
|
||||
|
||||
@pydantic.field_validator("admins")
|
||||
def validate_bot_admins(cls, v: str) -> list[int]:
|
||||
return list(map(int, v.split(",")))
|
4
src/bot_aiogram/tgbot/split_settings/utils.py
Normal file
4
src/bot_aiogram/tgbot/split_settings/utils.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import pathlib
|
||||
|
||||
BASE_PATH = pathlib.Path(__file__).parent.parent.parent.resolve()
|
||||
ENV_PATH = BASE_PATH / ".env"
|
Loading…
Reference in New Issue
Block a user