add more tests (#79)

* add more tests

* add more tests & update poetry.lock
This commit is contained in:
Dmitry Afanasyev 2024-01-08 15:38:56 +03:00 committed by GitHub
parent de55d873f9
commit 7cbe7b7c50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 368 additions and 38 deletions

View File

@ -22,7 +22,7 @@ class Application:
self.app = FastAPI( self.app = FastAPI(
title="Chat gpt bot", title="Chat gpt bot",
description="Bot for proxy to chat gpt in telegram", description="Bot for proxy to chat gpt in telegram",
version="0.0.3", version="1.1.12",
docs_url=build_uri([settings.api_prefix, "docs"]), docs_url=build_uri([settings.api_prefix, "docs"]),
redoc_url=build_uri([settings.api_prefix, "redocs"]), redoc_url=build_uri([settings.api_prefix, "redocs"]),
openapi_url=build_uri([settings.api_prefix, "openapi.json"]), openapi_url=build_uri([settings.api_prefix, "openapi.json"]),

View File

@ -75,6 +75,38 @@ async def test_change_chatgpt_model_priority(
assert upd_model2.priority == priority assert upd_model2.priority == priority
@pytest.mark.parametrize(
"bot_api_key_header",
[
pytest.param({"BOT-API-KEY": ""}, id="empty key in header"),
pytest.param({"BOT-API-KEY": "Hello World"}, id="incorrect token"),
pytest.param({"Hello-World": "lasdkj3-wer-weqwe_34"}, id="correct token but wrong api key"),
pytest.param({}, id="no api key header"),
],
)
async def test_cant_change_chatgpt_model_priority_with_wrong_api_key(
dbsession: Session,
rest_client: AsyncClient,
faker: Faker,
test_settings: AppSettings,
bot_api_key_header: dict[str, str],
) -> None:
ChatGptModelFactory(priority=0)
model2 = ChatGptModelFactory(priority=1)
priority = faker.random_int(min=2, max=7)
user = UserFactory(username=test_settings.SUPERUSER)
AccessTokenFactory(user_id=user.id, token="lasdkj3-wer-weqwe_34") # noqa: S106
response = await rest_client.put(
url=f"/api/chatgpt/models/{model2.id}/priority",
json={"priority": priority},
headers=bot_api_key_header,
)
assert response.status_code == 403
changed_model = dbsession.query(ChatGptModels).filter_by(id=model2.id).one()
assert changed_model.priority == model2.priority
async def test_reset_chatgpt_models_priority( async def test_reset_chatgpt_models_priority(
dbsession: Session, dbsession: Session,
rest_client: AsyncClient, rest_client: AsyncClient,
@ -101,6 +133,37 @@ async def test_reset_chatgpt_models_priority(
assert model.priority == 0 assert model.priority == 0
@pytest.mark.parametrize(
"bot_api_key_header",
[
pytest.param({"BOT-API-KEY": ""}, id="empty key in header"),
pytest.param({"BOT-API-KEY": "Hello World"}, id="incorrect token"),
],
)
async def test_cant_reset_chatgpt_models_priority_with_wrong_api_key(
dbsession: Session, rest_client: AsyncClient, test_settings: AppSettings, bot_api_key_header: dict[str, str]
) -> None:
chat_gpt_models = ChatGptModelFactory.create_batch(size=2)
model_with_highest_priority = ChatGptModelFactory(priority=42)
priorities = sorted([model.priority for model in chat_gpt_models] + [model_with_highest_priority.priority])
user = UserFactory(username=test_settings.SUPERUSER)
AccessTokenFactory(user_id=user.id)
response = await rest_client.put(
url="/api/chatgpt/models/priority/reset",
headers=bot_api_key_header,
)
assert response.status_code == 403
models = dbsession.query(ChatGptModels).all()
changed_priorities = sorted([model.priority for model in models])
assert changed_priorities == priorities
async def test_create_new_chatgpt_model( async def test_create_new_chatgpt_model(
dbsession: Session, dbsession: Session,
rest_client: AsyncClient, rest_client: AsyncClient,
@ -142,6 +205,37 @@ async def test_create_new_chatgpt_model(
} }
async def test_cant_create_new_chatgpt_model_with_wrong_api_key(
dbsession: Session,
rest_client: AsyncClient,
faker: Faker,
test_settings: AppSettings,
) -> None:
ChatGptModelFactory.create_batch(size=2)
ChatGptModelFactory(priority=42)
user = UserFactory(username=test_settings.SUPERUSER)
AccessTokenFactory(user_id=user.id)
model_name = "new-gpt-model"
model_priority = faker.random_int(min=1, max=5)
models = dbsession.query(ChatGptModels).all()
assert len(models) == 3
response = await rest_client.post(
url="/api/chatgpt/models",
json={
"model": model_name,
"priority": model_priority,
},
)
assert response.status_code == 403
models = dbsession.query(ChatGptModels).all()
assert len(models) == 3
async def test_add_existing_chatgpt_model( async def test_add_existing_chatgpt_model(
dbsession: Session, dbsession: Session,
rest_client: AsyncClient, rest_client: AsyncClient,
@ -197,3 +291,27 @@ async def test_delete_chatgpt_model(
assert len(models) == 2 assert len(models) == 2
assert model not in models assert model not in models
async def test_cant_delete_chatgpt_model_with_wrong_api_key(
dbsession: Session,
rest_client: AsyncClient,
test_settings: AppSettings,
) -> None:
ChatGptModelFactory.create_batch(size=2)
model = ChatGptModelFactory(priority=42)
user = UserFactory(username=test_settings.SUPERUSER)
access_token = AccessTokenFactory(user_id=user.id)
models = dbsession.query(ChatGptModels).all()
assert len(models) == 3
response = await rest_client.delete(
url=f"/api/chatgpt/models/{model.id}",
headers={"ROOT-ACCESS": access_token.token},
)
assert response.status_code == 403
models = dbsession.query(ChatGptModels).all()
assert len(models) == 3

View File

@ -11,6 +11,7 @@ from sqlalchemy.orm import Session
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from constants import BotStagesEnum from constants import BotStagesEnum
from core.auth.models.users import User, UserQuestionCount
from core.bot.app import BotApplication, BotQueue from core.bot.app import BotApplication, BotQueue
from main import Application from main import Application
from settings.config import AppSettings from settings.config import AppSettings
@ -22,6 +23,7 @@ from tests.integration.factories.bot import (
CallBackFactory, CallBackFactory,
ChatGptModelFactory, ChatGptModelFactory,
) )
from tests.integration.factories.user import UserFactory, UserQuestionCountFactory
from tests.integration.utils import mocked_ask_question_api from tests.integration.utils import mocked_ask_question_api
pytestmark = [ pytestmark = [
@ -113,6 +115,33 @@ async def test_help_command(
) )
async def test_help_command_user_is_banned(
main_application: Application,
test_settings: AppSettings,
) -> None:
message = BotMessageFactory.create_instance(text="/help")
user = message["from"]
UserFactory(
id=user["id"],
first_name=user["first_name"],
last_name=user["last_name"],
username=user["username"],
is_active=False,
ban_reason="test reason",
)
with mock.patch.object(
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
) as mocked_send_message:
bot_update = BotUpdateFactory(message=message)
await main_application.bot_app.application.process_update(
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
)
assert mocked_send_message.call_args.kwargs["text"] == (
"You have banned for reason: *test reason*.\nPlease contact the /developer"
)
async def test_start_entry( async def test_start_entry(
main_application: Application, main_application: Application,
test_settings: AppSettings, test_settings: AppSettings,
@ -232,6 +261,35 @@ async def test_website_callback_action(
assert mocked_reply_text.call_args.args == ("Веб версия: http://localhost/chat/",) assert mocked_reply_text.call_args.args == ("Веб версия: http://localhost/chat/",)
async def test_website_callback_action_user_is_banned(
main_application: Application,
test_settings: AppSettings,
) -> None:
message = BotMessageFactory.create_instance(text="Список основных команд:")
user = message["from"]
UserFactory(
id=user["id"],
first_name=user["first_name"],
last_name=user["last_name"],
username=user["username"],
is_active=False,
ban_reason="test reason",
)
with mock.patch.object(telegram._message.Message, "reply_text") as mocked_reply_text:
bot_update = BotCallBackQueryFactory(
message=message,
callback_query=CallBackFactory(data=BotStagesEnum.website),
)
await main_application.bot_app.application.process_update(
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
)
assert mocked_reply_text.call_args.kwargs["text"] == (
"You have banned for reason: *test reason*.\nPlease contact the /developer"
)
async def test_bug_report_action( async def test_bug_report_action(
main_application: Application, main_application: Application,
test_settings: AppSettings, test_settings: AppSettings,
@ -261,6 +319,35 @@ async def test_bug_report_action(
) )
async def test_bug_report_action_user_is_banned(
main_application: Application,
test_settings: AppSettings,
) -> None:
message = BotMessageFactory.create_instance(text="/bug_report")
user = message["from"]
UserFactory(
id=user["id"],
first_name=user["first_name"],
last_name=user["last_name"],
username=user["username"],
is_active=False,
ban_reason="test reason",
)
with (
mock.patch.object(telegram._message.Message, "reply_text") as mocked_reply_text,
mock.patch.object(telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)),
):
bot_update = BotUpdateFactory(message=message)
await main_application.bot_app.application.process_update(
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
)
assert mocked_reply_text.call_args.kwargs["text"] == (
"You have banned for reason: *test reason*.\nPlease contact the /developer"
)
async def test_get_developer_action( async def test_get_developer_action(
main_application: Application, main_application: Application,
test_settings: AppSettings, test_settings: AppSettings,
@ -278,19 +365,28 @@ async def test_get_developer_action(
assert mocked_reply_text.call_args.args == ("Автор бота: *Дмитрий Афанасьев*\n\nTg nickname: *Balshtg*",) assert mocked_reply_text.call_args.args == ("Автор бота: *Дмитрий Афанасьев*\n\nTg nickname: *Balshtg*",)
async def test_ask_question_action( async def test_ask_question_action_bot_user_not_exists(
dbsession: Session, dbsession: Session,
main_application: Application, main_application: Application,
test_settings: AppSettings, test_settings: AppSettings,
) -> None: ) -> None:
ChatGptModelFactory.create_batch(size=3) ChatGptModelFactory.create_batch(size=3)
users = dbsession.query(User).all()
users_question_count = dbsession.query(UserQuestionCount).all()
assert len(users) == 0
assert len(users_question_count) == 0
message = BotMessageFactory.create_instance(text="Привет!")
user = message["from"]
with mock.patch.object( with mock.patch.object(
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs) telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
) as mocked_send_message, mocked_ask_question_api( ) as mocked_send_message, mocked_ask_question_api(
host=test_settings.GPT_BASE_HOST, host=test_settings.GPT_BASE_HOST,
return_value=Response(status_code=httpx.codes.OK, text="Привет! Как я могу помочь вам сегодня?"), return_value=Response(status_code=httpx.codes.OK, text="Привет! Как я могу помочь вам сегодня?"),
): ):
bot_update = BotUpdateFactory(message=BotMessageFactory.create_instance(text="Привет!")) bot_update = BotUpdateFactory(message=message)
bot_update["message"].pop("entities") bot_update["message"].pop("entities")
await main_application.bot_app.application.process_update( await main_application.bot_app.application.process_update(
@ -313,6 +409,114 @@ async def test_ask_question_action(
include=["text", "chat_id"], include=["text", "chat_id"],
) )
created_user = dbsession.query(User).filter_by(id=user["id"]).one()
assert created_user.username == user["username"]
created_user_question_count = dbsession.query(UserQuestionCount).filter_by(user_id=user["id"]).one()
assert created_user_question_count.question_count == 1
async def test_ask_question_action_bot_user_already_exists(
dbsession: Session,
main_application: Application,
test_settings: AppSettings,
) -> None:
ChatGptModelFactory.create_batch(size=3)
message = BotMessageFactory.create_instance(text="Привет!")
user = message["from"]
existing_user = UserFactory(
id=user["id"], first_name=user["first_name"], last_name=user["last_name"], username=user["username"]
)
existing_user_question_count = UserQuestionCountFactory(user_id=existing_user.id).question_count
users = dbsession.query(User).all()
assert len(users) == 1
with mock.patch.object(
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
) as mocked_send_message, mocked_ask_question_api(
host=test_settings.GPT_BASE_HOST,
return_value=Response(status_code=httpx.codes.OK, text="Привет! Как я могу помочь вам сегодня?"),
):
bot_update = BotUpdateFactory(message=message)
bot_update["message"].pop("entities")
await main_application.bot_app.application.process_update(
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
)
assert_that(mocked_send_message.call_args_list[0].kwargs).is_equal_to(
{
"text": (
"Ответ в среднем занимает 10-15 секунд.\n- Список команд: /help\n- Сообщить об ошибке: /bug_report"
),
"chat_id": bot_update["message"]["chat"]["id"],
},
include=["text", "chat_id"],
)
assert_that(mocked_send_message.call_args_list[1].kwargs).is_equal_to(
{
"text": "Привет! Как я могу помочь вам сегодня?",
"chat_id": bot_update["message"]["chat"]["id"],
},
include=["text", "chat_id"],
)
users = dbsession.query(User).all()
assert len(users) == 1
updated_user_question_count = dbsession.query(UserQuestionCount).filter_by(user_id=user["id"]).one()
assert updated_user_question_count.question_count == existing_user_question_count + 1
async def test_ask_question_action_user_is_banned(
dbsession: Session,
main_application: Application,
test_settings: AppSettings,
) -> None:
ChatGptModelFactory.create_batch(size=3)
users_question_count = dbsession.query(UserQuestionCount).all()
assert len(users_question_count) == 0
message = BotMessageFactory.create_instance(text="Привет!")
user = message["from"]
UserFactory(
id=user["id"],
first_name=user["first_name"],
last_name=user["last_name"],
username=user["username"],
is_active=False,
ban_reason="test reason",
)
with mock.patch.object(
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
) as mocked_send_message, mocked_ask_question_api(
host=test_settings.GPT_BASE_HOST,
return_value=Response(status_code=httpx.codes.OK, text="Привет! Как я могу помочь вам сегодня?"),
assert_all_called=False,
):
bot_update = BotUpdateFactory(message=message)
bot_update["message"].pop("entities")
await main_application.bot_app.application.process_update(
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
)
assert_that(mocked_send_message.call_args_list[0].kwargs).is_equal_to(
{
"text": ("You have banned for reason: *test reason*.\nPlease contact the /developer"),
"chat_id": bot_update["message"]["chat"]["id"],
},
include=["text", "chat_id"],
)
created_user = dbsession.query(User).filter_by(id=user["id"]).one()
assert created_user.username == user["username"]
assert created_user.is_active is False
created_user_question_count = dbsession.query(UserQuestionCount).filter_by(user_id=user["id"]).scalar()
assert created_user_question_count is None
async def test_ask_question_action_not_success( async def test_ask_question_action_not_success(
dbsession: Session, dbsession: Session,

View File

@ -2,7 +2,7 @@ import uuid
import factory import factory
from core.auth.models.users import AccessToken, User from core.auth.models.users import AccessToken, User, UserQuestionCount
from tests.integration.factories.utils import BaseModelFactory from tests.integration.factories.utils import BaseModelFactory
@ -29,3 +29,11 @@ class AccessTokenFactory(BaseModelFactory):
class Meta: class Meta:
model = AccessToken model = AccessToken
class UserQuestionCountFactory(BaseModelFactory):
user_id = factory.Sequence(lambda n: n + 1)
question_count = factory.Faker("random_int")
class Meta:
model = UserQuestionCount

View File

@ -9,11 +9,11 @@ from settings.config import settings
@contextmanager @contextmanager
def mocked_ask_question_api( def mocked_ask_question_api(
host: str, return_value: Response | None = None, side_effect: Any | None = None host: str, return_value: Response | None = None, side_effect: Any | None = None, assert_all_called: bool = True
) -> Iterator[respx.MockRouter]: ) -> Iterator[respx.MockRouter]:
with respx.mock( with respx.mock(
assert_all_mocked=True, assert_all_mocked=True,
assert_all_called=True, assert_all_called=assert_all_called,
base_url=host, base_url=host,
) as respx_mock: ) as respx_mock:
ask_question_route = respx_mock.post(url=settings.chatgpt_backend_url, name="ask_question") ask_question_route = respx_mock.post(url=settings.chatgpt_backend_url, name="ask_question")

58
poetry.lock generated
View File

@ -607,25 +607,25 @@ toml = ["tomli"]
[[package]] [[package]]
name = "cyclonedx-python-lib" name = "cyclonedx-python-lib"
version = "5.2.0" version = "6.3.0"
description = "Python library for CycloneDX" description = "Python library for CycloneDX"
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = ">=3.8,<4.0"
files = [ files = [
{file = "cyclonedx_python_lib-5.2.0-py3-none-any.whl", hash = "sha256:1b43065205cdc53490c825fcfbda73142b758aa40ca169c968e342e88d06734f"}, {file = "cyclonedx_python_lib-6.3.0-py3-none-any.whl", hash = "sha256:0e73c1036c2f7fc67adc28aef807e6b44340ea70202aab197fb06b20ea165de8"},
{file = "cyclonedx_python_lib-5.2.0.tar.gz", hash = "sha256:b9ebf2c0520721d2f8ee16aadc2bbb9d4e015862c84ab1691a49b177f3014d99"}, {file = "cyclonedx_python_lib-6.3.0.tar.gz", hash = "sha256:82f2489de3c0cadad5af1ad7fa6b6a185f985746370245d38769699c734533c6"},
] ]
[package.dependencies] [package.dependencies]
license-expression = ">=30,<31" license-expression = ">=30,<31"
packageurl-python = ">=0.11" packageurl-python = ">=0.11"
py-serializable = ">=0.15,<0.16" py-serializable = ">=0.16,<0.18"
sortedcontainers = ">=2.4.0,<3.0.0" sortedcontainers = ">=2.4.0,<3.0.0"
[package.extras] [package.extras]
json-validation = ["jsonschema[format] (>=4.18,<5.0)"] json-validation = ["jsonschema[format] (>=4.18,<5.0)"]
validation = ["jsonschema[format] (>=4.18,<5.0)", "lxml (>=4,<5)"] validation = ["jsonschema[format] (>=4.18,<5.0)", "lxml (>=4,<6)"]
xml-validation = ["lxml (>=4,<5)"] xml-validation = ["lxml (>=4,<6)"]
[[package]] [[package]]
name = "decorator" name = "decorator"
@ -761,19 +761,19 @@ typing = ["typing-extensions (>=4.8)"]
[[package]] [[package]]
name = "flake8" name = "flake8"
version = "6.1.0" version = "7.0.0"
description = "the modular source code checker: pep8 pyflakes and co" description = "the modular source code checker: pep8 pyflakes and co"
optional = false optional = false
python-versions = ">=3.8.1" python-versions = ">=3.8.1"
files = [ files = [
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"},
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"},
] ]
[package.dependencies] [package.dependencies]
mccabe = ">=0.7.0,<0.8.0" mccabe = ">=0.7.0,<0.8.0"
pycodestyle = ">=2.11.0,<2.12.0" pycodestyle = ">=2.11.0,<2.12.0"
pyflakes = ">=3.1.0,<3.2.0" pyflakes = ">=3.2.0,<3.3.0"
[[package]] [[package]]
name = "flake8-aaa" name = "flake8-aaa"
@ -956,17 +956,17 @@ flake8 = ">3.0.0"
[[package]] [[package]]
name = "flake8-noqa" name = "flake8-noqa"
version = "1.3.2" version = "1.4.0"
description = "Flake8 noqa comment validation" description = "Flake8 noqa comment validation"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "flake8-noqa-1.3.2.tar.gz", hash = "sha256:b12ddf7b02dedabaca0f807cb436ea7992cc0106cb6fa41e997ad45a0a3bf754"}, {file = "flake8-noqa-1.4.0.tar.gz", hash = "sha256:771765ab27d1efd157528379acd15131147f9ae578a72d17fb432ca197881243"},
{file = "flake8_noqa-1.3.2-py3-none-any.whl", hash = "sha256:a2c139c4cc223f268fb262cd32a46fa72f509225d038058baa87c0ff8ac4d348"}, {file = "flake8_noqa-1.4.0-py3-none-any.whl", hash = "sha256:4465e16a19be433980f6f563d05540e2e54797eb11facb9feb50fed60624dc45"},
] ]
[package.dependencies] [package.dependencies]
flake8 = ">=3.8.0,<7.0" flake8 = ">=3.8.0,<8.0"
typing-extensions = ">=3.7.4.2" typing-extensions = ">=3.7.4.2"
[package.extras] [package.extras]
@ -1410,13 +1410,13 @@ files = [
[[package]] [[package]]
name = "ipython" name = "ipython"
version = "8.19.0" version = "8.20.0"
description = "IPython: Productive Interactive Computing" description = "IPython: Productive Interactive Computing"
optional = false optional = false
python-versions = ">=3.10" python-versions = ">=3.10"
files = [ files = [
{file = "ipython-8.19.0-py3-none-any.whl", hash = "sha256:2f55d59370f59d0d2b2212109fe0e6035cfea436b1c0e6150ad2244746272ec5"}, {file = "ipython-8.20.0-py3-none-any.whl", hash = "sha256:bc9716aad6f29f36c449e30821c9dd0c1c1a7b59ddcc26931685b87b4c569619"},
{file = "ipython-8.19.0.tar.gz", hash = "sha256:ac4da4ecf0042fb4e0ce57c60430c2db3c719fa8bdf92f8631d6bd8a5785d1f0"}, {file = "ipython-8.20.0.tar.gz", hash = "sha256:2f21bd3fc1d51550c89ee3944ae04bbc7bc79e129ea0937da6e6c68bfdbf117a"},
] ]
[package.dependencies] [package.dependencies]
@ -2089,18 +2089,18 @@ pip = "*"
[[package]] [[package]]
name = "pip-audit" name = "pip-audit"
version = "2.6.2" version = "2.6.3"
description = "A tool for scanning Python environments for known vulnerabilities" description = "A tool for scanning Python environments for known vulnerabilities"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "pip_audit-2.6.2-py3-none-any.whl", hash = "sha256:ac3a4b6e977ef2c574aa8d19a5d71d12201bdb65bba2d67d9df49f53f0be5e7d"}, {file = "pip_audit-2.6.3-py3-none-any.whl", hash = "sha256:216983210db4a15393f9e80e4d24a805f5767e4c8e0c31fc70c336acc629613b"},
{file = "pip_audit-2.6.2.tar.gz", hash = "sha256:0bbd023a199a104b29f949f063a872d41113b5a9048285666820fa35a76a7794"}, {file = "pip_audit-2.6.3.tar.gz", hash = "sha256:bd796066f69684b2f4fc2c2b6d222589e23190db0bbde069cea5c2b0be2cc57d"},
] ]
[package.dependencies] [package.dependencies]
CacheControl = {version = ">=0.13.0", extras = ["filecache"]} CacheControl = {version = ">=0.13.0", extras = ["filecache"]}
cyclonedx-python-lib = ">=4,<6" cyclonedx-python-lib = ">=5,<7"
html5lib = ">=1.1" html5lib = ">=1.1"
packaging = ">=23.0.0" packaging = ">=23.0.0"
pip-api = ">=0.0.28" pip-api = ">=0.0.28"
@ -2112,7 +2112,7 @@ toml = ">=0.10"
[package.extras] [package.extras]
dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"]
doc = ["pdoc"] doc = ["pdoc"]
lint = ["interrogate", "mypy", "ruff (<0.1.9)", "types-html5lib", "types-requests", "types-toml"] lint = ["interrogate", "mypy", "ruff (<0.1.12)", "types-html5lib", "types-requests", "types-toml"]
test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"]
[[package]] [[package]]
@ -2216,13 +2216,13 @@ tests = ["pytest"]
[[package]] [[package]]
name = "py-serializable" name = "py-serializable"
version = "0.15.0" version = "0.17.1"
description = "Library for serializing and deserializing Python Objects to and from JSON and XML." description = "Library for serializing and deserializing Python Objects to and from JSON and XML."
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
files = [ files = [
{file = "py-serializable-0.15.0.tar.gz", hash = "sha256:8fc41457d8ee5f5c5a12f41fd87bf1a4f2ecf9da39fee92059b728e78f320771"}, {file = "py-serializable-0.17.1.tar.gz", hash = "sha256:875bb9c01df77f563dfcd1e75bb4244b5596083d3aad4ccd3fb63e1f5a9d3e5f"},
{file = "py_serializable-0.15.0-py3-none-any.whl", hash = "sha256:d3f1201b33420c481aa83f7860c7bf2c2f036ba3ea82b6e15a96696457c36cd2"}, {file = "py_serializable-0.17.1-py3-none-any.whl", hash = "sha256:389c2254d912bec3a44acdac667c947d73c59325050d5ae66386e1ed7108a45a"},
] ]
[package.dependencies] [package.dependencies]
@ -2407,13 +2407,13 @@ resolved_reference = "996cec42e9621701edb83354232b2c0ca0121560"
[[package]] [[package]]
name = "pyflakes" name = "pyflakes"
version = "3.1.0" version = "3.2.0"
description = "passive checker of Python programs" description = "passive checker of Python programs"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
] ]
[[package]] [[package]]
@ -3732,4 +3732,4 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "3a78cb3473202950a11ebbe28e5662f6a1a57508a0e2703761c3c448744b0bed" content-hash = "79650f1ee926621124c6b8cc3e3ddbf6c438ae15dee98f9bc9e1fddef07a8a70"

View File

@ -1,11 +1,11 @@
[tool.poetry] [tool.poetry]
name = "chat_gpt_bot" name = "chat_gpt_bot"
version = "1.4.1" version = "1.5.0"
description = "Bot to integrated with Chat gpt" description = "Bot to integrated with Chat gpt"
authors = ["Dmitry Afanasyev <Balshbox@gmail.com>"] authors = ["Dmitry Afanasyev <Balshbox@gmail.com>"]
[build-system] [build-system]
requires = ["poetry-core>=1.7.0"] requires = ["poetry-core>=1.7.1"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry.dependencies] [tool.poetry.dependencies]
@ -84,7 +84,7 @@ autoflake = "^2.2"
flake8-aaa = "^0.17.0" flake8-aaa = "^0.17.0"
flake8-variables-names = "^0.0.6" flake8-variables-names = "^0.0.6"
flake8-deprecated = "^2.2.1" flake8-deprecated = "^2.2.1"
flake8-noqa = "^1.3.2" flake8-noqa = "^1.4"
flake8-annotations-complexity = "^0.0.8" flake8-annotations-complexity = "^0.0.8"
flake8-useless-assert = "^0.4.4" flake8-useless-assert = "^0.4.4"
flake8-newspaper-style = "^0.4.3" flake8-newspaper-style = "^0.4.3"