поделал сайт, сделал orm-модели, requirements.txt

This commit is contained in:
2023-04-29 08:52:01 +07:00
parent 23a56ae0c7
commit 3ee35ae080
17 changed files with 556 additions and 90 deletions
BIN
View File
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
import os
from typing import List, Dict, Any
def get_log(log_id: int) -> List[Dict[str, Any]]:
log_dir = os.path.join("static/logs", str(log_id))
return_dir = []
for dir_path, _, filenames in os.walk(log_dir):
if dir_path != log_dir:
audio_file = os.path.join(dir_path, "audio.ogg")
if not os.path.exists(audio_file):
audio_file = os.path.join(dir_path, "audio.wav")
text_file = os.path.join(dir_path, "yandex-text.txt")
if not os.path.exists(text_file):
text_file = os.path.join(dir_path, "google-text.txt")
try:
return_dir.append({"id": log_id, "audio_file": f"/{audio_file}",
"text": open(text_file).read().split("\n")})
except UnicodeDecodeError:
pass
return return_dir
+8
View File
@@ -0,0 +1,8 @@
import os
from typing import List, Tuple
def get_logs() -> List[Tuple[int, str]]:
return [(int(os.path.basename(dir_path)), dir_path.split("/")[2].strip())
for dir_path, _, filenames in os.walk("static/logs")
if dir_path != "static/logs" and len(dir_path.split("/")) == 3]
+16
View File
@@ -0,0 +1,16 @@
import jwt
from fastapi import Cookie, Depends
from functions.admin.models import token, database
def is_logged_in(access_token: str = Cookie(None), db=Depends(database.get_db)):
if not access_token:
return False
response = db.query(token.Token).filter(token.Token.access_token == access_token).first()
if not response or not response.is_active:
return False
try:
jwt.decode(access_token, "secret", algorithms=["HS256"])
except jwt.exceptions.ExpiredSignatureError:
return False
return True
+18
View File
@@ -0,0 +1,18 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./db.sql"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Base.metadata.create_all(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
+14
View File
@@ -0,0 +1,14 @@
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from functions.admin.models.database import Base
class Token(Base):
__tablename__ = "tokens"
id = Column(Integer, primary_key=True, index=True)
access_token = Column(String(255), unique=True)
refresh_token = Column(String(255), unique=True)
is_active = Column(Boolean(), default=True)
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="tokens")
+19
View File
@@ -0,0 +1,19 @@
import hashlib
from sqlalchemy import Boolean, Column, Integer, String
from sqlalchemy.orm import relationship
from functions.admin.models.database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True)
email = Column(String(255), unique=True)
password_hash = Column(String(255))
is_active = Column(Boolean(), default=True)
tokens = relationship("Token", back_populates="user")
def check_password(self, password: str):
return self.password_hash == hashlib.sha384(password.encode()).hexdigest()
+10
View File
@@ -0,0 +1,10 @@
from fastapi import Request, Depends, responses, HTTPException
from functions.admin import get_log, is_logged_in
async def log(templates, request: Request, log_id: int, logged_in: bool = Depends(is_logged_in.is_logged_in)):
if logged_in:
if not get_log.get_log(log_id):
raise HTTPException(status_code=400)
return templates.TemplateResponse("log.html", {"request": request, "log": get_log.get_log(log_id)})
else:
return responses.RedirectResponse(url=f"/refresh?source=/logs/{log_id}", status_code=303)
+25
View File
@@ -0,0 +1,25 @@
from fastapi import Form, Depends, responses, HTTPException
from functions.admin.models import database, user, token
import jwt
from datetime import datetime, timedelta
async def logins(username: str = Form(...), password: str = Form(...), db=Depends(database.get_db)):
# username = "Dmitrium12"
response = db.query(user.User).filter(user.User.username == username).first()
if not response or not response.check_password(password):
raise HTTPException(status_code=400, detail="Неправильное имя пользователя или пароль")
access_token_expires = datetime.utcnow() + timedelta(minutes=15)
access_token_payload = {"sub": response.username, "exp": access_token_expires}
access_token = jwt.encode(access_token_payload, "secret", algorithm="HS256")
refresh_token_expires = datetime.utcnow() + timedelta(days=7)
refresh_token_payload = {"sub": response.username, "exp": refresh_token_expires}
refresh_token = jwt.encode(refresh_token_payload, "secret", algorithm="HS256")
db_token = token.Token(access_token=access_token, refresh_token=refresh_token, user=response)
db.add(db_token)
db.commit()
response = responses.RedirectResponse(url="/logs", status_code=303)
response.set_cookie(key="access_token", value=access_token, expires=int(access_token_expires.timestamp()))
response.set_cookie(key="refresh_token", value=refresh_token,
expires=int(refresh_token_expires.timestamp()))
return response
+8
View File
@@ -0,0 +1,8 @@
from fastapi import Request, Depends, responses
from functions.admin import get_logs, is_logged_in
async def logs(templates, request: Request, logged_in: bool = Depends(is_logged_in.is_logged_in)):
if logged_in:
return templates.TemplateResponse("logs.html", {"request": request, "logs": get_logs.get_logs()})
else:
return responses.RedirectResponse(url="/refresh?source=/logs", status_code=303)
+31
View File
@@ -0,0 +1,31 @@
from fastapi import Request, Depends, Cookie, responses
from functions.admin.models import database, user, token
import jwt
from datetime import datetime, timedelta
async def refresh_access_token(req: Request, refresh_token: str = Cookie(None), db=Depends(database.get_db)):
request_args = dict(req.query_params)
try:
refresh_token_payload = jwt.decode(refresh_token, "secret", algorithms=["HS256"])
except jwt.exceptions.DecodeError:
return responses.RedirectResponse(url="/login", status_code=303)
response = db.query(user.User).filter(user.User.username == refresh_token_payload["sub"]).first()
if not response:
return responses.RedirectResponse(url="/login", status_code=303)
access_token_expires = datetime.utcnow() + timedelta(minutes=15)
access_token_payload = {"sub": response.username, "exp": access_token_expires}
access_token = jwt.encode(access_token_payload, "secret", algorithm="HS256")
db.query(token.Token).filter(token.Token.refresh_token == refresh_token).update({
token.Token.access_token: access_token,
})
db.commit()
if request_args:
response = responses.RedirectResponse(url=request_args["source"], status_code=303)
else:
response = responses.RedirectResponse(url="/", status_code=303)
response.set_cookie(
key="access_token",
value=access_token,
expires=int(access_token_expires.timestamp())
)
return response
+127
View File
@@ -0,0 +1,127 @@
aiofiles==22.1.0
aiogram==2.23.1
aiohttp==3.8.3
aiolimiter==1.0.0
aiosignal==1.3.1
anyio==3.6.2
APScheduler==3.10.1
async-generator==1.10
async-timeout==4.0.2
attrs==22.1.0
audiosegment==0.23.0
Babel==2.9.1
bcrypt==4.0.1
beautifulsoup4==4.11.1
BibaAndBoba==1.2.2
boto3==1.26.13
botocore==1.29.13
cachetools==5.3.0
certifi==2022.9.24
cffi==1.15.1
charset-normalizer==2.1.1
click==8.1.3
contourpy==1.0.6
cryptography==38.0.3
cycler==0.11.0
DAWG-Python==0.7.2
deep-translator==1.9.2
dill==0.3.6
discord==2.2.2
discord.py==2.2.2
dnspython==2.3.0
docopt==0.6.2
email-validator==1.3.1
exceptiongroup==1.1.1
fastapi==0.95.1
fastapi-users==10.4.2
Flask==2.2.3
fonttools==4.38.0
frozenlist==1.3.3
geographiclib==2.0
geopy==2.3.0
greenlet==2.0.2
grpcio==1.50.0
grpcio-tools==1.48.2
h11==0.14.0
h2==4.1.0
hpack==4.0.0
httpcore==0.16.1
httpx==0.23.3
hyperframe==6.0.1
idna==3.4
itsdangerous==2.1.2
javascript==1!1.0.1
Jinja2==3.1.2
jmespath==1.0.1
joblib==1.2.0
kiwisolver==1.4.4
loguru==0.6.0
lxml==4.9.2
magic-filter==1.0.9
makefun==1.15.1
MarkupSafe==2.1.2
matplotlib==3.6.2
multidict==6.0.3
multiprocess==0.70.14
mutagen==1.46.0
nltk==3.8
numpy==1.23.5
openai==0.27.4
outcome==1.2.0
packaging==22.0
pandas==1.4.3
passlib==1.7.4
pathos==0.3.0
Pillow==9.4.0
pox==0.3.2
ppft==1.7.6.6
protobuf==3.19.6
pycparser==2.21
pydantic==1.10.2
pydub==0.25.1
PyExecJS==1.5.1
PyJWT==2.6.0
pymorphy2==0.9.1
pymorphy2-dicts-ru==2.4.417127.4579844
pyparsing==3.0.9
PySocks==1.7.1
pyTelegramBotAPI==4.7.1
python-dateutil==2.8.2
python-dotenv==1.0.0
python-multipart==0.0.6
python-telegram-bot==20.1
pytz==2022.6
pytz-deprecation-shim==0.1.0.post0
regex==2022.10.31
requests==2.28.1
rfc3986==1.5.0
rnnoise-wrapper @ git+https://github.com/Desklop/RNNoise_Wrapper@10647eba5c1dc678dc3fd443d111400792fefef6
s3transfer==0.6.0
scipy==1.10.0
selenium==4.8.3
six==1.16.0
sniffio==1.3.0
sortedcontainers==2.4.0
soupsieve==2.3.2.post1
speechkit==2.1.1
SpeechRecognition==3.8.1
SQLAlchemy==2.0.10
starlette==0.26.1
tornado==6.2
tqdm==4.64.1
translators==5.5.6
trio==0.22.0
trio-websocket==0.10.2
typing_extensions==4.4.0
tzdata==2022.7
tzlocal==4.3
urllib3==1.26.12
uvicorn==0.21.1
vk-api==11.9.9
webdriver-manager==3.8.6
webrtcvad==2.0.10
Werkzeug==2.2.3
wsproto==1.2.0
yandex-s3==0.1.1
yarl==1.8.2
yaweather==1.2.2
+110 -23
View File
@@ -1,52 +1,139 @@
/* Global styles */
body {
background-color: #333;
color: #fff;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0;
padding: 0;
background-color: #333;
}
h1 {
text-align: center;
margin-top: 50px;
margin-bottom: 30px;
a {
color: #333;
}
form {
ul {
list-style: none;
margin: 0;
padding: 0;
}
/* Header styles */
header {
background-color: #333;
}
nav {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
margin: 1rem;
}
label {
margin-bottom: 10px;
nav ul {
display: flex;
justify-content: flex-end;
align-items: center;
height: auto;
width: auto;
padding-left: 0;
}
input[type="password"] {
padding: 10px;
border-radius: 5px;
border: none;
background-color: #555;
color: #fff;
width: 100%;
max-width: 300px; /* добавлено, чтобы форма не была слишком широкой */
box-sizing: border-box; /* добавлено, чтобы input не выходил за границы родительского элемента */
margin-bottom: 20px; /* добавлено, чтобы был отступ между input и кнопкой */
nav li {
margin-right: 20px;
position: relative
}
button[type="submit"] {
nav a {
text-decoration: none;
color: #333;
text-transform: uppercase
}
.auth-buttons a {
-webkit-appearance: button;
-moz-appearance: button;
text-decoration: none;
padding: 10px 20px;
border-radius: 25px; /* более округлая форма */
border: none;
background-color: #4CAF50;
color: #fff;
cursor: pointer;
box-shadow: 0 6px 0 #3e8e41; /* тень при наведении курсора */
box-shadow: 0 5px 0 #3e8e41; /* тень при наведении курсора */
transition: box-shadow 0.2s ease-in-out;
}
button[type="submit"]:hover {
.auth-buttons a:hover {
box-shadow: 0 3px 0 #3e8e41;
}
/* Bot section styles */
.bot-section {
margin-top: 2rem;
padding: 2rem;
background-color: #333;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
text-align: center
}
.bot-section h1 {
font-size: 2.5rem;
}
.bot-image img {
width: 300px;
height: auto;
margin-bottom: 2rem
}
.bot-features h2 {
font-size: 1.5rem;
margin-bottom: 1rem
}
.bot-features ul {
margin-bottom: 2rem
}
.bot-features li {
margin-bottom: .5rem
}
.bot-usage h2 {
font-size: 1.5rem;
margin-bottom: 1rem
}
.bot-usage ol {
margin-bottom: 2rem;
text-align: left
}
.bot-usage li {
margin-bottom: .5rem
}
.bot-feedback h2 {
font-size: 1.5rem;
margin-bottom: 1rem
}
/* Footer styles */
footer {
background-color: #1e1e1e;
color: #fff;
text-align: center;
padding: 1rem;
}
+53
View File
@@ -0,0 +1,53 @@
body {
background-color: #333;
color: #fff;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
margin-top: 50px;
margin-bottom: 30px;
}
form {
display: flex;
flex-direction: column;
align-items: center;
}
label {
margin-bottom: 10px;
}
input {
padding: 10px;
border-radius: 5px;
border: none;
background-color: #555;
color: #fff;
width: 100%;
max-width: 300px; /* добавлено, чтобы форма не была слишком широкой */
box-sizing: border-box; /* добавлено, чтобы input не выходил за границы родительского элемента */
margin-bottom: 20px; /* добавлено, чтобы был отступ между input и кнопкой */
}
button[type="submit"] {
padding: 10px 20px;
border-radius: 25px; /* более округлая форма */
border: none;
background-color: #4CAF50;
color: #fff;
cursor: pointer;
box-shadow: 0 6px 0 #3e8e41; /* тень при наведении курсора */
transition: box-shadow 0.2s ease-in-out;
width: 100px;
}
button[type="submit"]:hover {
box-shadow: 0 3px 0 #3e8e41;
}
+54 -10
View File
@@ -1,17 +1,61 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>audio resive</title>
<meta charset="UTF-8">
<title>Telegram Bot</title>
<link href="{{ url_for('static', path='/css/index.css') }}" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Login</h1>
<form method="post" action="/login">
<label for="password">Password:</label>
<input type="password" name="password" id="password">
<button type="submit">Login</button>
</form>
</div>
<header>
<nav>
<div></div>
<div class="auth-buttons">
<a href="/login" class="button">Log In</a>
<a href="/register" class="button">Sign Up</a>
</div>
</nav>
</header>
<section class="bot-section">
<h1>Телеграм-бот для перевода голосовых сообщений и общения с ChatGPT с функцией сохранения истории.</h1>
<p>Разработанный нами Telegram-бот может переводить голосовые сообщения и общаться с ChatGPT,
сохраняя историю запросов. Этот бот создан для того, чтобы облегчить вашу жизнь,
предоставляя вам безупречный опыт перевода голосовых сообщений и общения с ChatGPT без необходимости
переключаться между различными приложениями. Бот интегрирован в Telegram, что означает,
что вы можете использовать его на любом устройстве, поддерживающем этот мессенджер.</p>
<div class="bot-image">
<img src="https://via.placeholder.com/300x200" alt="">
</div>
<div class="bot-features">
<h2>Фишки:</h2>
<ul>
<li>Функция перевода голосовых сообщений, поддерживающая несколько языков.</li>
<li>Функция общения, которая позволяет вам общаться с ChatGPT для любых запросов или вопросов, которые у вас
могут возникнуть.
</li>
<li>Функция сохранения истории, которая отслеживает все ваши запросы и беседы для последующего
использования.
</li>
<li>Понятный интерфейс, который делает его простым в использовании для любого человека, независимо от его
технических знаний.
</li>
</ul>
</div>
<div class="bot-usage">
<h2>Как пользоваться:</h2>
<ol>
<li>Откройте телеграм.</li>
<li>Найдите нашего бота и запустите его</li>
<li>Теперь вы можете отправлять ему голосовые сообщения, чтобы получить расшифровку, а так же добавлять в
ваши группы, чтобы бот и там мог расшифровывать голосовые сообщения.
</li>
<li>С помощью команды /r вы можете задавать вопросы боту, а команда /r_clear удалит всю переписку, чтобы вы
могли начать новую.
</li>
</ol>
</div>
</section>
<footer>
<p>&copy; 2021 Telegram Bot. All rights reserved.</p>
</footer>
</body>
</html>
</html>
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>audio resive</title>
<link href="{{ url_for('static', path='/css/login.css') }}" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Login</h1>
<form method="post" action="/logins">
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<label for="password">Password:</label>
<input type="password" name="password" id="password">
<button type="submit">Login</button>
</form>
</div>
</body>
</html>
+23 -57
View File
@@ -1,71 +1,37 @@
from typing import Dict, Any, List, Tuple, Optional
import os
from fastapi import FastAPI, Request, Form, Depends, Cookie, responses, templating, HTTPException
from starlette.staticfiles import StaticFiles
from fastapi import FastAPI, Request, Form, Depends, Cookie, responses, templating, staticfiles
from functions.admin import is_logged_in
from functions.admin.models import database
from functions.admin.templates import logins, refresh, logs, log
app = FastAPI()
templates = templating.Jinja2Templates(directory="static/templates")
app.mount("/static", StaticFiles(directory="static"), name="static")
def is_logged_in(logged_in: Optional[str] = Cookie(None)):
return bool(logged_in)
app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")
@app.get("/", response_class=responses.HTMLResponse)
def index(request: Request):
def login(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.post("/login")
async def login(password: str = Form(...)):
if password == "password":
response = responses.RedirectResponse(url="/logs", status_code=303)
response.set_cookie(key="logged_in", value="true")
return response
return {"message": "Неправильный пароль"}
@app.get("/login", response_class=responses.HTMLResponse)
def login(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
@app.post("/logins")
async def logins_response(username: str = Form(...), password: str = Form(...), db=Depends(database.get_db)):
return await logins.logins(username, password, db)
@app.get("/refresh")
async def refresh_access_token(req: Request, refresh_token: str = Cookie(None), db=Depends(database.get_db)):
return await refresh.refresh_access_token(req, refresh_token, db)
@app.get("/logs", response_class=responses.HTMLResponse)
async def logs(request: Request, logged_in: bool = Depends(is_logged_in)):
if logged_in:
return templates.TemplateResponse("logs.html", {"request": request, "logs": get_logs()})
else:
return responses.RedirectResponse(url="/", status_code=303)
async def logs_response(request: Request, logged_in: bool = Depends(is_logged_in.is_logged_in)):
return await logs.logs(templates, request, logged_in)
@app.get("/logs/{log_id}", response_class=responses.HTMLResponse)
async def log(request: Request, log_id: int, logged_in: bool = Depends(is_logged_in)):
if logged_in:
if not get_log(log_id):
raise HTTPException(status_code=400)
return templates.TemplateResponse("log.html", {"request": request,
"log": get_log(log_id)})
else:
responses.RedirectResponse(url="/login", status_code=303)
def get_logs() -> List[Tuple[int, str]]:
return [(int(os.path.basename(dir_path)), dir_path.split("/")[2].strip())
for dir_path, _, filenames in os.walk("static/logs")
if dir_path != "static/logs" and len(dir_path.split("/")) == 3]
def get_log(log_id: int) -> List[Dict[str, Any]]:
log_dir = os.path.join("static/logs", str(log_id))
return_dir = []
for dir_path, _, filenames in os.walk(log_dir):
if dir_path != log_dir:
audio_file = os.path.join(dir_path, "audio.ogg")
if not os.path.exists(audio_file):
audio_file = os.path.join(dir_path, "audio.wav")
text_file = os.path.join(dir_path, "yandex-text.txt")
if not os.path.exists(text_file):
text_file = os.path.join(dir_path, "google-text.txt")
try:
return_dir.append({"id": log_id, "audio_file": f"/{audio_file}",
"text": open(text_file).read().split("\n")})
except UnicodeDecodeError:
pass
return return_dir
async def log_response(request: Request, log_id: int, logged_in: bool = Depends(is_logged_in.is_logged_in)):
return await log.log(templates, request, log_id, logged_in)