поделал сайт, сделал orm-модели, requirements.txt
This commit is contained in:
@@ -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
|
||||||
@@ -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]
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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")
|
||||||
@@ -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()
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
@@ -1,52 +1,139 @@
|
|||||||
|
/* Global styles */
|
||||||
body {
|
body {
|
||||||
background-color: #333;
|
|
||||||
color: #fff;
|
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
background-color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
a {
|
||||||
text-align: center;
|
color: #333;
|
||||||
margin-top: 50px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header styles */
|
||||||
|
header {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
nav ul {
|
||||||
margin-bottom: 10px;
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="password"] {
|
nav li {
|
||||||
padding: 10px;
|
margin-right: 20px;
|
||||||
border-radius: 5px;
|
position: relative
|
||||||
border: none;
|
|
||||||
background-color: #555;
|
|
||||||
color: #fff;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 300px; /* добавлено, чтобы форма не была слишком широкой */
|
|
||||||
box-sizing: border-box; /* добавлено, чтобы input не выходил за границы родительского элемента */
|
|
||||||
margin-bottom: 20px; /* добавлено, чтобы был отступ между input и кнопкой */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
padding: 10px 20px;
|
||||||
border-radius: 25px; /* более округлая форма */
|
border-radius: 25px; /* более округлая форма */
|
||||||
border: none;
|
border: none;
|
||||||
background-color: #4CAF50;
|
background-color: #4CAF50;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: 0 6px 0 #3e8e41; /* тень при наведении курсора */
|
box-shadow: 0 5px 0 #3e8e41; /* тень при наведении курсора */
|
||||||
transition: box-shadow 0.2s ease-in-out;
|
transition: box-shadow 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
button[type="submit"]:hover {
|
.auth-buttons a:hover {
|
||||||
box-shadow: 0 3px 0 #3e8e41;
|
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;
|
||||||
}
|
}
|
||||||
@@ -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
@@ -1,17 +1,61 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<title>audio resive</title>
|
<meta charset="UTF-8">
|
||||||
|
<title>Telegram Bot</title>
|
||||||
<link href="{{ url_for('static', path='/css/index.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', path='/css/index.css') }}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header>
|
||||||
<h1>Login</h1>
|
<nav>
|
||||||
<form method="post" action="/login">
|
<div></div>
|
||||||
<label for="password">Password:</label>
|
<div class="auth-buttons">
|
||||||
<input type="password" name="password" id="password">
|
<a href="/login" class="button">Log In</a>
|
||||||
<button type="submit">Login</button>
|
<a href="/register" class="button">Sign Up</a>
|
||||||
</form>
|
</div>
|
||||||
</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>© 2021 Telegram Bot. All rights reserved.</p>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -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>
|
||||||
@@ -1,71 +1,37 @@
|
|||||||
from typing import Dict, Any, List, Tuple, Optional
|
from fastapi import FastAPI, Request, Form, Depends, Cookie, responses, templating, staticfiles
|
||||||
import os
|
from functions.admin import is_logged_in
|
||||||
from fastapi import FastAPI, Request, Form, Depends, Cookie, responses, templating, HTTPException
|
from functions.admin.models import database
|
||||||
from starlette.staticfiles import StaticFiles
|
from functions.admin.templates import logins, refresh, logs, log
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
templates = templating.Jinja2Templates(directory="static/templates")
|
templates = templating.Jinja2Templates(directory="static/templates")
|
||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")
|
||||||
|
|
||||||
|
|
||||||
def is_logged_in(logged_in: Optional[str] = Cookie(None)):
|
|
||||||
return bool(logged_in)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", response_class=responses.HTMLResponse)
|
@app.get("/", response_class=responses.HTMLResponse)
|
||||||
def index(request: Request):
|
def login(request: Request):
|
||||||
return templates.TemplateResponse("index.html", {"request": request})
|
return templates.TemplateResponse("index.html", {"request": request})
|
||||||
|
|
||||||
|
|
||||||
@app.post("/login")
|
@app.get("/login", response_class=responses.HTMLResponse)
|
||||||
async def login(password: str = Form(...)):
|
def login(request: Request):
|
||||||
if password == "password":
|
return templates.TemplateResponse("login.html", {"request": request})
|
||||||
response = responses.RedirectResponse(url="/logs", status_code=303)
|
|
||||||
response.set_cookie(key="logged_in", value="true")
|
|
||||||
return response
|
@app.post("/logins")
|
||||||
return {"message": "Неправильный пароль"}
|
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)
|
@app.get("/logs", response_class=responses.HTMLResponse)
|
||||||
async def logs(request: Request, logged_in: bool = Depends(is_logged_in)):
|
async def logs_response(request: Request, logged_in: bool = Depends(is_logged_in.is_logged_in)):
|
||||||
if logged_in:
|
return await logs.logs(templates, request, logged_in)
|
||||||
return templates.TemplateResponse("logs.html", {"request": request, "logs": get_logs()})
|
|
||||||
else:
|
|
||||||
return responses.RedirectResponse(url="/", status_code=303)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/logs/{log_id}", response_class=responses.HTMLResponse)
|
@app.get("/logs/{log_id}", response_class=responses.HTMLResponse)
|
||||||
async def log(request: Request, log_id: int, logged_in: bool = Depends(is_logged_in)):
|
async def log_response(request: Request, log_id: int, logged_in: bool = Depends(is_logged_in.is_logged_in)):
|
||||||
if logged_in:
|
return await log.log(templates, request, log_id, 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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user