This commit is contained in:
Евгений Александрович 2025-03-28 18:32:31 +03:00
commit 1ef959b606
10 changed files with 1219 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Файлы, игнорируемые по умолчанию
/shelf/
/workspace.xml
# Запросы HTTP-клиента в редакторе
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

10
.idea/ChatSockerRSA.iml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.11 (ChatSockerRSA)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,619 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="CommandLineInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="CythonUsageBeforeDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false">
<Languages>
<language minSize="54" name="Python" />
</Languages>
</inspection_tool>
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="%}{%" />
<item index="1" class="java.lang.String" itemvalue="maxlength" />
<item index="2" class="java.lang.String" itemvalue="href" />
<item index="3" class="java.lang.String" itemvalue="article.status" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="10">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
<item index="6" class="java.lang.String" itemvalue="small" />
<item index="7" class="java.lang.String" itemvalue="style" />
<item index="8" class="java.lang.String" itemvalue="td" />
<item index="9" class="java.lang.String" itemvalue="li" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="PoetryPackageVersionsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyAbstractClassInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyArgumentEqualDefaultInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyArgumentListInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyAssignmentToLoopOrWithParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyAsyncCallInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyAttributeOutsideInitInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyAugmentAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyBroadExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyByteLiteralInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyCallingNonCallableInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyChainedComparisonsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyClassHasNoInitInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyClassVarInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyComparisonWithNoneInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true">
<option name="ourVersions">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="3.13" />
<item index="1" class="java.lang.String" itemvalue="3.11" />
<item index="2" class="java.lang.String" itemvalue="3.12" />
<item index="3" class="java.lang.String" itemvalue="3.10" />
<item index="4" class="java.lang.String" itemvalue="3.9" />
</list>
</value>
</option>
</scope>
<option name="ourVersions">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="3.13" />
<item index="1" class="java.lang.String" itemvalue="3.12" />
<item index="2" class="java.lang.String" itemvalue="3.11" />
<item index="3" class="java.lang.String" itemvalue="3.10" />
<item index="4" class="java.lang.String" itemvalue="3.9" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyDataclassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDecoratorInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDefaultArgumentInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDeprecationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDictCreationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDictDuplicateKeysInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDocstringTypesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyDunderSlotsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyExceptClausesOrderInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyExceptionInheritInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyFinalInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyFromFutureImportInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyGlobalUndefinedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyInconsistentIndentationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyIncorrectDocstringInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyInitNewSignatureInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyInterpreterInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyListCreationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMandatoryEncodingInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMethodFirstArgAssignmentInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMethodMayBeStaticInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMethodOverridingInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMethodParametersInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMissingConstructorInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMissingOrEmptyDocstringInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyNamedTupleInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyNestedDecoratorsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyNewStyleGenericSyntaxInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyNonAsciiCharInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyNoneFunctionAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyOldStyleClassesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyOverloadsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyOverridesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true">
<option name="ignoredPackages">
<value>
<list size="119">
<item index="0" class="java.lang.String" itemvalue="asyncpg" />
<item index="1" class="java.lang.String" itemvalue="psycopg2" />
<item index="2" class="java.lang.String" itemvalue="uvloop" />
<item index="3" class="java.lang.String" itemvalue="httptools" />
<item index="4" class="java.lang.String" itemvalue="eventlet" />
<item index="5" class="java.lang.String" itemvalue="tqdm" />
<item index="6" class="java.lang.String" itemvalue="yadisk" />
<item index="7" class="java.lang.String" itemvalue="certifi" />
<item index="8" class="java.lang.String" itemvalue="charset-normalizer" />
<item index="9" class="java.lang.String" itemvalue="requests" />
<item index="10" class="java.lang.String" itemvalue="urllib3" />
<item index="11" class="java.lang.String" itemvalue="idna" />
<item index="12" class="java.lang.String" itemvalue="pathspec" />
<item index="13" class="java.lang.String" itemvalue="djlint" />
<item index="14" class="java.lang.String" itemvalue="PyYAML" />
<item index="15" class="java.lang.String" itemvalue="tzdata" />
<item index="16" class="java.lang.String" itemvalue="asgiref" />
<item index="17" class="java.lang.String" itemvalue="EditorConfig" />
<item index="18" class="java.lang.String" itemvalue="click" />
<item index="19" class="java.lang.String" itemvalue="html-tag-names" />
<item index="20" class="java.lang.String" itemvalue="sqlparse" />
<item index="21" class="java.lang.String" itemvalue="regex" />
<item index="22" class="java.lang.String" itemvalue="colorama" />
<item index="23" class="java.lang.String" itemvalue="Django" />
<item index="24" class="java.lang.String" itemvalue="ruff" />
<item index="25" class="java.lang.String" itemvalue="cssbeautifier" />
<item index="26" class="java.lang.String" itemvalue="html-void-elements" />
<item index="27" class="java.lang.String" itemvalue="jsbeautifier" />
<item index="28" class="java.lang.String" itemvalue="tomli" />
<item index="29" class="java.lang.String" itemvalue="pytils" />
<item index="30" class="java.lang.String" itemvalue="Pillow" />
<item index="31" class="java.lang.String" itemvalue="mysqlclient" />
<item index="32" class="java.lang.String" itemvalue="Werkzeug" />
<item index="33" class="java.lang.String" itemvalue="Flask-WTF" />
<item index="34" class="java.lang.String" itemvalue="Flask-MySQLdb" />
<item index="35" class="java.lang.String" itemvalue="MarkupSafe" />
<item index="36" class="java.lang.String" itemvalue="itsdangerous" />
<item index="37" class="java.lang.String" itemvalue="Jinja2" />
<item index="38" class="java.lang.String" itemvalue="Flask" />
<item index="39" class="java.lang.String" itemvalue="email-validator" />
<item index="40" class="java.lang.String" itemvalue="dnspython" />
<item index="41" class="java.lang.String" itemvalue="alembic" />
<item index="42" class="java.lang.String" itemvalue="async-timeout" />
<item index="43" class="java.lang.String" itemvalue="Flask-Principal" />
<item index="44" class="java.lang.String" itemvalue="Flask-Uploads" />
<item index="45" class="java.lang.String" itemvalue="python-dotenv" />
<item index="46" class="java.lang.String" itemvalue="cachelib" />
<item index="47" class="java.lang.String" itemvalue="redis" />
<item index="48" class="java.lang.String" itemvalue="Flask-Migrate" />
<item index="49" class="java.lang.String" itemvalue="Mako" />
<item index="50" class="java.lang.String" itemvalue="Markdown" />
<item index="51" class="java.lang.String" itemvalue="Flask-Session" />
<item index="52" class="java.lang.String" itemvalue="requests-oauthlib" />
<item index="53" class="java.lang.String" itemvalue="PyJWT" />
<item index="54" class="java.lang.String" itemvalue="social-auth-app-django" />
<item index="55" class="java.lang.String" itemvalue="oauthlib" />
<item index="56" class="java.lang.String" itemvalue="cffi" />
<item index="57" class="java.lang.String" itemvalue="cryptography" />
<item index="58" class="java.lang.String" itemvalue="defusedxml" />
<item index="59" class="java.lang.String" itemvalue="pycparser" />
<item index="60" class="java.lang.String" itemvalue="pytz" />
<item index="61" class="java.lang.String" itemvalue="python3-openid" />
<item index="62" class="java.lang.String" itemvalue="social-auth-core" />
<item index="63" class="java.lang.String" itemvalue="protobuf" />
<item index="64" class="java.lang.String" itemvalue="nodeenv" />
<item index="65" class="java.lang.String" itemvalue="identify" />
<item index="66" class="java.lang.String" itemvalue="pyflakes" />
<item index="67" class="java.lang.String" itemvalue="dill" />
<item index="68" class="java.lang.String" itemvalue="pycodestyle" />
<item index="69" class="java.lang.String" itemvalue="flake8" />
<item index="70" class="java.lang.String" itemvalue="tomlkit" />
<item index="71" class="java.lang.String" itemvalue="filelock" />
<item index="72" class="java.lang.String" itemvalue="platformdirs" />
<item index="73" class="java.lang.String" itemvalue="mccabe" />
<item index="74" class="java.lang.String" itemvalue="typing_extensions" />
<item index="75" class="java.lang.String" itemvalue="virtualenv" />
<item index="76" class="java.lang.String" itemvalue="pylint-protobuf" />
<item index="77" class="java.lang.String" itemvalue="distlib" />
<item index="78" class="java.lang.String" itemvalue="isort" />
<item index="79" class="java.lang.String" itemvalue="cfgv" />
<item index="80" class="java.lang.String" itemvalue="exceptiongroup" />
<item index="81" class="java.lang.String" itemvalue="pytest" />
<item index="82" class="java.lang.String" itemvalue="iniconfig" />
<item index="83" class="java.lang.String" itemvalue="pluggy" />
<item index="84" class="java.lang.String" itemvalue="packaging" />
<item index="85" class="java.lang.String" itemvalue="gunicorn" />
<item index="86" class="java.lang.String" itemvalue="referencing" />
<item index="87" class="java.lang.String" itemvalue="python-crontab" />
<item index="88" class="java.lang.String" itemvalue="python-dateutil" />
<item index="89" class="java.lang.String" itemvalue="psycopg2-binary" />
<item index="90" class="java.lang.String" itemvalue="wcwidth" />
<item index="91" class="java.lang.String" itemvalue="astroid" />
<item index="92" class="java.lang.String" itemvalue="kombu" />
<item index="93" class="java.lang.String" itemvalue="click-didyoumean" />
<item index="94" class="java.lang.String" itemvalue="jsonschema-specifications" />
<item index="95" class="java.lang.String" itemvalue="drf-spectacular" />
<item index="96" class="java.lang.String" itemvalue="rpds-py" />
<item index="97" class="java.lang.String" itemvalue="celery" />
<item index="98" class="java.lang.String" itemvalue="cron-descriptor" />
<item index="99" class="java.lang.String" itemvalue="prompt-toolkit" />
<item index="100" class="java.lang.String" itemvalue="djangorestframework" />
<item index="101" class="java.lang.String" itemvalue="vine" />
<item index="102" class="java.lang.String" itemvalue="billiard" />
<item index="103" class="java.lang.String" itemvalue="jsonschema" />
<item index="104" class="java.lang.String" itemvalue="uritemplate" />
<item index="105" class="java.lang.String" itemvalue="django-celery-beat" />
<item index="106" class="java.lang.String" itemvalue="six" />
<item index="107" class="java.lang.String" itemvalue="django-celery-results" />
<item index="108" class="java.lang.String" itemvalue="amqp" />
<item index="109" class="java.lang.String" itemvalue="attrs" />
<item index="110" class="java.lang.String" itemvalue="django-axes" />
<item index="111" class="java.lang.String" itemvalue="pylint" />
<item index="112" class="java.lang.String" itemvalue="django-redis" />
<item index="113" class="java.lang.String" itemvalue="django-timezone-field" />
<item index="114" class="java.lang.String" itemvalue="click-repl" />
<item index="115" class="java.lang.String" itemvalue="click-plugins" />
<item index="116" class="java.lang.String" itemvalue="inflection" />
<item index="117" class="java.lang.String" itemvalue="pyproject_hooks" />
<item index="118" class="java.lang.String" itemvalue="python-docx" />
</list>
</value>
</option>
</scope>
<option name="ignoredPackages">
<value>
<list size="119">
<item index="0" class="java.lang.String" itemvalue="asyncpg" />
<item index="1" class="java.lang.String" itemvalue="psycopg2" />
<item index="2" class="java.lang.String" itemvalue="uvloop" />
<item index="3" class="java.lang.String" itemvalue="httptools" />
<item index="4" class="java.lang.String" itemvalue="eventlet" />
<item index="5" class="java.lang.String" itemvalue="tqdm" />
<item index="6" class="java.lang.String" itemvalue="yadisk" />
<item index="7" class="java.lang.String" itemvalue="certifi" />
<item index="8" class="java.lang.String" itemvalue="charset-normalizer" />
<item index="9" class="java.lang.String" itemvalue="requests" />
<item index="10" class="java.lang.String" itemvalue="urllib3" />
<item index="11" class="java.lang.String" itemvalue="idna" />
<item index="12" class="java.lang.String" itemvalue="pathspec" />
<item index="13" class="java.lang.String" itemvalue="djlint" />
<item index="14" class="java.lang.String" itemvalue="PyYAML" />
<item index="15" class="java.lang.String" itemvalue="tzdata" />
<item index="16" class="java.lang.String" itemvalue="asgiref" />
<item index="17" class="java.lang.String" itemvalue="EditorConfig" />
<item index="18" class="java.lang.String" itemvalue="click" />
<item index="19" class="java.lang.String" itemvalue="html-tag-names" />
<item index="20" class="java.lang.String" itemvalue="sqlparse" />
<item index="21" class="java.lang.String" itemvalue="regex" />
<item index="22" class="java.lang.String" itemvalue="colorama" />
<item index="23" class="java.lang.String" itemvalue="Django" />
<item index="24" class="java.lang.String" itemvalue="ruff" />
<item index="25" class="java.lang.String" itemvalue="cssbeautifier" />
<item index="26" class="java.lang.String" itemvalue="html-void-elements" />
<item index="27" class="java.lang.String" itemvalue="jsbeautifier" />
<item index="28" class="java.lang.String" itemvalue="tomli" />
<item index="29" class="java.lang.String" itemvalue="pytils" />
<item index="30" class="java.lang.String" itemvalue="Pillow" />
<item index="31" class="java.lang.String" itemvalue="mysqlclient" />
<item index="32" class="java.lang.String" itemvalue="Werkzeug" />
<item index="33" class="java.lang.String" itemvalue="Flask-WTF" />
<item index="34" class="java.lang.String" itemvalue="Flask-MySQLdb" />
<item index="35" class="java.lang.String" itemvalue="MarkupSafe" />
<item index="36" class="java.lang.String" itemvalue="itsdangerous" />
<item index="37" class="java.lang.String" itemvalue="Jinja2" />
<item index="38" class="java.lang.String" itemvalue="Flask" />
<item index="39" class="java.lang.String" itemvalue="email-validator" />
<item index="40" class="java.lang.String" itemvalue="dnspython" />
<item index="41" class="java.lang.String" itemvalue="alembic" />
<item index="42" class="java.lang.String" itemvalue="async-timeout" />
<item index="43" class="java.lang.String" itemvalue="Flask-Principal" />
<item index="44" class="java.lang.String" itemvalue="Flask-Uploads" />
<item index="45" class="java.lang.String" itemvalue="python-dotenv" />
<item index="46" class="java.lang.String" itemvalue="cachelib" />
<item index="47" class="java.lang.String" itemvalue="redis" />
<item index="48" class="java.lang.String" itemvalue="Flask-Migrate" />
<item index="49" class="java.lang.String" itemvalue="Mako" />
<item index="50" class="java.lang.String" itemvalue="Markdown" />
<item index="51" class="java.lang.String" itemvalue="Flask-Session" />
<item index="52" class="java.lang.String" itemvalue="requests-oauthlib" />
<item index="53" class="java.lang.String" itemvalue="PyJWT" />
<item index="54" class="java.lang.String" itemvalue="social-auth-app-django" />
<item index="55" class="java.lang.String" itemvalue="oauthlib" />
<item index="56" class="java.lang.String" itemvalue="cffi" />
<item index="57" class="java.lang.String" itemvalue="cryptography" />
<item index="58" class="java.lang.String" itemvalue="defusedxml" />
<item index="59" class="java.lang.String" itemvalue="pycparser" />
<item index="60" class="java.lang.String" itemvalue="pytz" />
<item index="61" class="java.lang.String" itemvalue="python3-openid" />
<item index="62" class="java.lang.String" itemvalue="social-auth-core" />
<item index="63" class="java.lang.String" itemvalue="protobuf" />
<item index="64" class="java.lang.String" itemvalue="nodeenv" />
<item index="65" class="java.lang.String" itemvalue="identify" />
<item index="66" class="java.lang.String" itemvalue="pyflakes" />
<item index="67" class="java.lang.String" itemvalue="dill" />
<item index="68" class="java.lang.String" itemvalue="pycodestyle" />
<item index="69" class="java.lang.String" itemvalue="flake8" />
<item index="70" class="java.lang.String" itemvalue="tomlkit" />
<item index="71" class="java.lang.String" itemvalue="filelock" />
<item index="72" class="java.lang.String" itemvalue="platformdirs" />
<item index="73" class="java.lang.String" itemvalue="mccabe" />
<item index="74" class="java.lang.String" itemvalue="typing_extensions" />
<item index="75" class="java.lang.String" itemvalue="virtualenv" />
<item index="76" class="java.lang.String" itemvalue="pylint-protobuf" />
<item index="77" class="java.lang.String" itemvalue="distlib" />
<item index="78" class="java.lang.String" itemvalue="isort" />
<item index="79" class="java.lang.String" itemvalue="cfgv" />
<item index="80" class="java.lang.String" itemvalue="exceptiongroup" />
<item index="81" class="java.lang.String" itemvalue="pytest" />
<item index="82" class="java.lang.String" itemvalue="iniconfig" />
<item index="83" class="java.lang.String" itemvalue="pluggy" />
<item index="84" class="java.lang.String" itemvalue="packaging" />
<item index="85" class="java.lang.String" itemvalue="gunicorn" />
<item index="86" class="java.lang.String" itemvalue="referencing" />
<item index="87" class="java.lang.String" itemvalue="python-crontab" />
<item index="88" class="java.lang.String" itemvalue="python-dateutil" />
<item index="89" class="java.lang.String" itemvalue="psycopg2-binary" />
<item index="90" class="java.lang.String" itemvalue="wcwidth" />
<item index="91" class="java.lang.String" itemvalue="astroid" />
<item index="92" class="java.lang.String" itemvalue="kombu" />
<item index="93" class="java.lang.String" itemvalue="click-didyoumean" />
<item index="94" class="java.lang.String" itemvalue="jsonschema-specifications" />
<item index="95" class="java.lang.String" itemvalue="drf-spectacular" />
<item index="96" class="java.lang.String" itemvalue="rpds-py" />
<item index="97" class="java.lang.String" itemvalue="celery" />
<item index="98" class="java.lang.String" itemvalue="cron-descriptor" />
<item index="99" class="java.lang.String" itemvalue="prompt-toolkit" />
<item index="100" class="java.lang.String" itemvalue="djangorestframework" />
<item index="101" class="java.lang.String" itemvalue="vine" />
<item index="102" class="java.lang.String" itemvalue="billiard" />
<item index="103" class="java.lang.String" itemvalue="jsonschema" />
<item index="104" class="java.lang.String" itemvalue="uritemplate" />
<item index="105" class="java.lang.String" itemvalue="django-celery-beat" />
<item index="106" class="java.lang.String" itemvalue="six" />
<item index="107" class="java.lang.String" itemvalue="django-celery-results" />
<item index="108" class="java.lang.String" itemvalue="amqp" />
<item index="109" class="java.lang.String" itemvalue="attrs" />
<item index="110" class="java.lang.String" itemvalue="django-axes" />
<item index="111" class="java.lang.String" itemvalue="pylint" />
<item index="112" class="java.lang.String" itemvalue="django-redis" />
<item index="113" class="java.lang.String" itemvalue="django-timezone-field" />
<item index="114" class="java.lang.String" itemvalue="click-repl" />
<item index="115" class="java.lang.String" itemvalue="click-plugins" />
<item index="116" class="java.lang.String" itemvalue="inflection" />
<item index="117" class="java.lang.String" itemvalue="pyproject_hooks" />
<item index="118" class="java.lang.String" itemvalue="python-docx" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPandasSeriesToListInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true">
<option name="ignoredErrors">
<list>
<option value="E712" />
<option value="W605" />
</list>
</option>
</scope>
<option name="ignoredErrors">
<list>
<option value="E712" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true">
<option name="ignoredErrors">
<list>
<option value="N806" />
<option value="N802" />
<option value="N813" />
</list>
</option>
</scope>
<option name="ignoredErrors">
<list>
<option value="N806" />
<option value="N802" />
<option value="N813" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPropertyAccessInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyPropertyDefinitionInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyProtectedMemberInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyProtocolInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyRedeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyRedundantParenthesesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyRelativeImportInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyReturnFromInitInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PySetFunctionToLiteralInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PySimplifyBooleanCheckInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PySingleQuotedDocstringInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyStatementEffectInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyStringFormatInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyStubPackagesAdvertiser" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyStubPackagesCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PySuperArgumentsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTestParametrizedInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTestUnpassedFixtureInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTrailingSemicolonInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTupleAssignmentBalanceInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTupleItemAssignmentInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTypeCheckerInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTypeHintsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyTypedDictInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyUnboundLocalVariableInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyUnnecessaryBackslashInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyUnreachableCodeInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WARNING" enabled="true">
<option name="ignoredIdentifiers">
<list>
<option value="private_key" />
</list>
</option>
</scope>
<option name="ignoredIdentifiers">
<list>
<option value="None" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyUnusedLocalInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="All Changed Files" level="WEAK WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="TaskProblemsInspection" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="TEXT_STYLE_ERROR" />
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

17
.idea/material_theme_project_new.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="-1e21af8c:195a4ec3861:-7ffe" />
</MTProjectMetadataState>
</option>
<option name="titleBarState">
<MTProjectTitleBarConfigState>
<option name="overrideColor" value="false" />
</MTProjectTitleBarConfigState>
</option>
</component>
</project>

6
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 (ChatSockerRSA)" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ChatSockerRSA.iml" filepath="$PROJECT_DIR$/.idea/ChatSockerRSA.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

396
CSRSA_client.py Normal file
View File

@ -0,0 +1,396 @@
import socket
import threading
import rsa
import os
from pathlib import Path
import time
from datetime import datetime
import urwid
import sys
import locale
class ChatClient:
def __init__(self, host='127.0.0.1', port=5555):
# Настройка локализации
locale.setlocale(locale.LC_ALL, '')
os.environ['LANG'] = 'ru_RU.UTF-8'
self.host = host
self.port = port
self.nickname = None
self.client_public_key = None
self.client_private_key = None
self.server_public_key = None
self.current_chat = []
self.file_transfers = {}
self.current_file = None
self.input_history = []
self.history_index = -1
self.last_update = time.time()
self.update_interval = 5 # Интервал обновления в секундах
# Цветовая схема
self.palette = [
('header', 'white', 'dark blue'),
('chat', 'light gray', 'black'),
('input', 'white', 'black'),
('status', 'white', 'dark blue'),
('nick', 'light cyan', 'black'),
('server', 'light green', 'black'),
('file', 'yellow', 'black'),
('error', 'light red', 'black'),
('own', 'light magenta', 'black'),
('highlight', 'black', 'light gray'),
]
self.generate_keys()
self.setup_client()
self.setup_ui()
def generate_keys(self):
(self.client_public_key, self.client_private_key) = rsa.newkeys(2048)
def setup_client(self):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((self.host, self.port))
except Exception as e:
print(f"Не удалось подключиться к серверу: {e}")
sys.exit(1)
def setup_ui(self):
# Создаем виджеты чата
self.chat_content = urwid.SimpleListWalker([])
self.chat_list = urwid.ListBox(self.chat_content)
# Исправленная строка - правильно закрываем скобки Frame
chat_frame = urwid.Frame(
urwid.AttrMap(self.chat_list, 'chat'),
header=urwid.AttrMap(urwid.Text(" ЧАТ "), 'header'))
# Поле ввода
self.input_edit = urwid.Edit(caption="Сообщение: ")
input_pile = urwid.Pile([
urwid.AttrMap(self.input_edit, 'input'),
urwid.Divider('-')
])
# Статус бар
self.status_text = urwid.Text("")
status_bar = urwid.AttrMap(self.status_text, 'status')
# Главный layout
self.main_frame = urwid.Frame(
body=chat_frame,
footer=urwid.Pile([
input_pile,
status_bar
])
)
# Главный цикл
self.loop = urwid.MainLoop(
self.main_frame,
palette=self.palette,
unhandled_input=self.handle_input
)
def handle_input(self, key):
if key == 'enter':
self.process_command(self.input_edit.get_edit_text())
self.input_edit.set_edit_text("")
self.history_index = -1
elif key == 'up':
self.navigate_history(-1)
elif key == 'down':
self.navigate_history(1)
elif key == 'f1':
self.show_help()
elif key == 'ctrl u':
self.input_edit.set_edit_text("")
def navigate_history(self, direction):
if not self.input_history:
return
if direction == -1 and abs(self.history_index) < len(self.input_history):
self.history_index -= 1
elif direction == 1 and self.history_index < -1:
self.history_index += 1
if -len(self.input_history) <= self.history_index <= -1:
self.input_edit.set_edit_text(self.input_history[self.history_index])
def process_command(self, msg):
if not msg.strip():
return
# Добавляем в историю
self.input_history.append(msg)
if len(self.input_history) > 100:
self.input_history.pop(0)
if msg.lower() == '/exit':
self.client.close()
raise urwid.ExitMainLoop()
elif msg.lower() == '/help':
self.show_help()
elif msg.lower() in ('y', 'n') and hasattr(self, 'current_file') and self.current_file.get('awaiting_response'):
response = '/accept_file' if msg.lower() == 'y' else '/reject_file'
encrypted_response = rsa.encrypt(response.encode('utf-8'), self.server_public_key)
self.client.send(encrypted_response)
if msg.lower() == 'y':
self.add_message(f"[ФАЙЛ] Ожидайте получения файла {self.current_file['file_name']}...", 'file')
else:
self.add_message(f"[ФАЙЛ] Вы отказались от получения файла {self.current_file['file_name']}", 'file')
del self.current_file
self.set_status("")
elif msg.startswith('/file'):
self.send_file(msg)
else:
# Отображаем свое сообщение
self.add_message(f"{self.nickname}: {msg}", 'own')
# Отправляем на сервер
try:
encrypted_msg = rsa.encrypt(msg.encode('utf-8'), self.server_public_key)
self.client.send(encrypted_msg)
except Exception as e:
self.add_message(f"[ОШИБКА] Не удалось отправить сообщение: {str(e)}", 'error')
def send_file(self, command):
try:
parts = command.split(' ', 2)
if len(parts) < 3:
self.add_message("[ОШИБКА] Использование: /file <получатель> <путь_к_файлу>", 'error')
self.set_status("Используйте: /file <ник> <путь_к_файлу>")
return
recipient = parts[1]
file_path = parts[2]
if not os.path.exists(file_path):
self.add_message(f"[ОШИБКА] Файл {file_path} не найден", 'error')
self.set_status(f"Файл не найден: {file_path}")
return
file_size = os.path.getsize(file_path)
if file_size > 1024 * 1024: # 1MB лимит
self.add_message("[ОШИБКА] Файл слишком большой (макс. 1MB)", 'error')
self.set_status("Файл слишком большой (макс. 1MB)")
return
with open(file_path, 'rb') as f:
file_data = f.read()
file_name = os.path.basename(file_path)
full_command = f"/file {recipient} {file_name} {file_data.decode('latin1')}"
encrypted_msg = rsa.encrypt(full_command.encode('latin1'), self.server_public_key)
self.client.send(encrypted_msg)
self.add_message(f"[ФАЙЛ] Запрос на отправку {file_name} пользователю {recipient} отправлен", 'file')
self.set_status(f"Запрос на отправку файла {file_name} отправлен")
except Exception as e:
self.add_message(f"[ОШИБКА при отправке файла] {str(e)}", 'error')
self.set_status("Ошибка при отправке файла")
def receive(self):
try:
# Получаем публичный ключ сервера
self.server_public_key = rsa.PublicKey.load_pkcs1(self.client.recv(2048))
# Отправляем свой публичный ключ
self.client.send(self.client_public_key.save_pkcs1())
# Получаем никнейм
self.set_status("Введите ваш никнейм и нажмите Enter")
while not self.nickname:
time.sleep(0.1)
# Отправляем никнейм на сервер
encrypted_nickname = rsa.encrypt(self.nickname.encode('utf-8'), self.server_public_key)
self.client.send(encrypted_nickname)
self.set_status(f"Подключено как {self.nickname}")
while True:
try:
encrypted_msg = self.client.recv(4096)
if not encrypted_msg:
break
# Расшифровываем сообщение
msg = rsa.decrypt(encrypted_msg, self.client_private_key).decode('utf-8')
if msg.startswith('/file_notification'):
self.handle_file_notification(msg)
elif msg.startswith('/file_data'):
self.handle_file_data(msg)
else:
self.add_message(msg, 'server' if msg.startswith('[СЕРВЕР]') else 'nick')
except Exception as e:
self.add_message(f"[ОШИБКА] {str(e)}", 'error')
break
except Exception as e:
self.add_message(f"[ОШИБКА] {str(e)}", 'error')
finally:
self.client.close()
raise urwid.ExitMainLoop()
def handle_file_notification(self, notification):
parts = notification.split(' ', 2)
if len(parts) < 3:
return
sender = parts[1]
file_name = parts[2]
self.add_message(f"[ФАЙЛ] {sender} хочет отправить вам файл: {file_name}", 'file')
self.set_status(f"Принять файл {file_name} от {sender}? (y/n)")
self.current_file = {
'sender': sender,
'file_name': file_name,
'awaiting_response': True
}
def handle_file_data(self, file_data):
if not hasattr(self, 'current_file'):
return
try:
downloads_dir = Path("downloads")
downloads_dir.mkdir(exist_ok=True)
file_path = downloads_dir / self.current_file['file_name']
with open(file_path, 'wb') as f:
f.write(file_data[len('/file_data '):].encode('latin1'))
self.add_message(f"[ФАЙЛ] Файл {self.current_file['file_name']} сохранен в {file_path}", 'file')
self.set_status(f"Файл получен: {self.current_file['file_name']}")
except Exception as e:
self.add_message(f"[ОШИБКА] Не удалось сохранить файл: {str(e)}", 'error')
finally:
if hasattr(self, 'current_file'):
del self.current_file
def add_message(self, text, style=None):
if style:
self.chat_content.append(urwid.AttrMap(urwid.Text(text), style))
else:
self.chat_content.append(urwid.Text(text))
# Автопрокрутка вниз
if len(self.chat_content) > 0:
self.chat_list.set_focus(len(self.chat_content) - 1)
def set_status(self, message, timeout=3):
self.status_text.set_text(f" {message} ")
if timeout > 0:
threading.Timer(timeout, lambda: self.set_status("")).start()
def update_ui(self, loop=None, user_data=None):
"""Функция для обновления интерфейса"""
current_time = time.time()
if current_time - self.last_update >= self.update_interval:
self.last_update = current_time
# Здесь можно добавить любые обновления интерфейса
# Например, обновление времени в статус баре
self.loop.draw_screen()
# Планируем следующее обновление
self.loop.set_alarm_in(self.update_interval, self.update_ui)
def show_help(self):
help_text = [
"СПРАВКА ПО МЕССЕНДЖЕРУ",
"",
"Основные команды:",
"/exit - Выйти из программы",
"/help - Показать эту справку",
"",
"Отправка файлов:",
"/file <ник> <путь> - Отправить файл указанному пользователю",
"Пример: /file user2 /home/user/image.jpg",
"",
"При получении файла:",
"1. Вы получите уведомление о входящем файле",
"2. Введите 'y' для принятия или 'n' для отказа",
"",
"Горячие клавиши:",
"F1 - Показать/скрыть справку",
"Стрелки вверх/вниз - История сообщений",
"Ctrl+U - Очистить поле ввода"
]
help_box = urwid.ListBox(urwid.SimpleListWalker([
urwid.AttrMap(urwid.Text(line), 'highlight') for line in help_text
]))
overlay = urwid.Overlay(
urwid.LineBox(help_box, title="Помощь"),
self.main_frame,
align='center',
width=('relative', 80),
valign='middle',
height=('relative', 80)
)
def exit_help(key):
if key in ('f1', 'esc', 'enter'):
self.loop.widget = self.main_frame
self.loop.widget = overlay
self.loop.unhandled_input = exit_help
def run(self):
# Получаем никнейм
self.set_status("Введите ваш никнейм и нажмите Enter")
def set_nickname(edit, new_edit_text):
if new_edit_text.strip():
self.nickname = new_edit_text.strip()
self.input_edit.set_edit_text("")
self.loop.unhandled_input = self.handle_input
self.set_status(f"Подключено как {self.nickname}")
self.input_edit.set_caption("Сообщение: ")
# Запускаем периодическое обновление интерфейса
self.loop.set_alarm_in(self.update_interval, self.update_ui)
self.input_edit.set_caption("Никнейм: ")
self.loop.unhandled_input = lambda key: (
key == 'enter' and set_nickname(None, self.input_edit.get_edit_text())
)
# Запускаем поток для получения сообщений
receive_thread = threading.Thread(target=self.receive, daemon=True)
receive_thread.start()
# Запускаем главный цикл
self.loop.run()
if __name__ == "__main__":
try:
if len(sys.argv) > 1:
host = sys.argv[1]
port = int(sys.argv[2]) if len(sys.argv) > 2 else 5555
client = ChatClient(host, port)
else:
client = ChatClient()
client.run()
except KeyboardInterrupt:
if hasattr(client, 'client'):
client.client.close()
sys.exit(0)
except Exception as e:
print(f"Ошибка: {str(e)}")
sys.exit(1)

143
CSRSA_server.py Normal file
View File

@ -0,0 +1,143 @@
import socket
import threading
import rsa
from datetime import datetime
class Server:
def __init__(self, host='0.0.0.0', port=5555):
self.host = host
self.port = port
self.clients = {}
self.public_keys = {}
self.server_public_key = None
self.server_private_key = None
self.generate_keys()
self.setup_server()
def generate_keys(self):
(self.server_public_key, self.server_private_key) = rsa.newkeys(2048)
def setup_server(self):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host, self.port))
self.server.listen()
def broadcast(self, message, sender=None):
for client in self.clients:
if client != sender:
try:
client_public_key = self.public_keys[client]
encrypted_msg = rsa.encrypt(message.encode(), client_public_key)
client.send(encrypted_msg)
except:
self.remove_client(client)
def handle_client(self, client):
try:
# Отправляем клиенту публичный ключ сервера
client.send(self.server_public_key.save_pkcs1())
# Получаем публичный ключ клиента
client_public_key = rsa.PublicKey.load_pkcs1(client.recv(2048))
self.public_keys[client] = client_public_key
# Получаем имя клиента
encrypted_name = client.recv(1024)
client_name = rsa.decrypt(encrypted_name, self.server_private_key).decode()
self.clients[client] = client_name
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {client_name} подключился.")
self.broadcast(f"[СЕРВЕР] {client_name} присоединился к чату!", client)
while True:
try:
encrypted_msg = client.recv(4096)
if not encrypted_msg:
break
msg = rsa.decrypt(encrypted_msg, self.server_private_key).decode()
if msg.startswith('/file'):
self.handle_file_transfer(client, msg)
else:
self.broadcast(f"{self.clients[client]}: {msg}", client)
except Exception as e:
print(f"[ОШИБКА] {e}")
break
except Exception as e:
print(f"[ОШИБКА] {e}")
finally:
self.remove_client(client)
def handle_file_transfer(self, sender, command):
try:
parts = command.split(' ', 3)
if len(parts) < 4:
return
recipient_name = parts[1]
file_name = parts[2]
file_data = parts[3].encode()
# Находим получателя
recipient = None
for client in self.clients:
if self.clients[client] == recipient_name:
recipient = client
break
if recipient:
# Отправляем уведомление получателю
notification = f"/file_notification {self.clients[sender]} {file_name}"
encrypted_notification = rsa.encrypt(notification.encode(), self.public_keys[recipient])
recipient.send(encrypted_notification)
# Ждем подтверждения
response = rsa.decrypt(recipient.recv(1024), self.server_private_key).decode()
if response == "/accept_file":
# Отправляем файл
encrypted_file_data = rsa.encrypt(file_data, self.public_keys[recipient])
recipient.send(encrypted_file_data)
sender.send(rsa.encrypt(f"[СЕРВЕР] Файл {file_name} отправлен {recipient_name}".encode(),
self.public_keys[sender]))
else:
sender.send(rsa.encrypt(f"[СЕРВЕР] {recipient_name} отказался принять файл".encode(),
self.public_keys[sender]))
else:
sender.send(
rsa.encrypt(f"[СЕРВЕР] Пользователь {recipient_name} не найден".encode(), self.public_keys[sender]))
except Exception as e:
print(f"[ОШИБКА при передаче файла] {e}")
def remove_client(self, client):
if client in self.clients:
name = self.clients[client]
del self.clients[client]
if client in self.public_keys:
del self.public_keys[client]
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {name} отключился.")
self.broadcast(f"[СЕРВЕР] {name} покинул чат.", client)
client.close()
def run(self):
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Сервер запущен на {self.host}:{self.port}")
try:
while True:
client, address = self.server.accept()
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Новое соединение с {address[0]}:{address[1]}")
thread = threading.Thread(target=self.handle_client, args=(client,))
thread.start()
except KeyboardInterrupt:
print("\nСервер останавливается...")
finally:
self.server.close()
if __name__ == "__main__":
server = Server()
server.run()