add authentication
This commit is contained in:
parent
85032a0c50
commit
6bbca80da9
|
@ -48,9 +48,13 @@ pip3 install -U -r requirements.txt
|
||||||
| `SESSION_STRING` (required) | String obtained by running `$ python3 app/generate_session_string.py`. (Login with the telegram account which is a participant of the given channel (or chat).
|
| `SESSION_STRING` (required) | String obtained by running `$ python3 app/generate_session_string.py`. (Login with the telegram account which is a participant of the given channel (or chat).
|
||||||
| `PORT` (optional) | Port on which app should listen to, defaults to 8080.
|
| `PORT` (optional) | Port on which app should listen to, defaults to 8080.
|
||||||
| `HOST` (optional) | Host name on which app should listen to, defaults to 0.0.0.0.
|
| `HOST` (optional) | Host name on which app should listen to, defaults to 0.0.0.0.
|
||||||
| `DEBUG` (optional) | Give some value to set logging level to debug, info by default.
|
| `DEBUG` (optional) | Give `true` to set logging level to debug, info by default.
|
||||||
| `BLOCK_DOWNLOADS` (optional) | Enable downloads or not. If provided, downloads will be disabled.
|
| `BLOCK_DOWNLOADS` (optional) | Enable downloads or not. If any value is provided, downloads will be disabled.
|
||||||
| `RESULTS_PER_PAGE` (optional) | Number of results to be returned per page defaults to 20.
|
| `RESULTS_PER_PAGE` (optional) | Number of results to be returned per page defaults to 20.
|
||||||
|
| `USERNAME` (optional) | Username for authentication, defaults to `''`.
|
||||||
|
| `PASSWORD` (optional) | Username for authentication, defaults to `''`.
|
||||||
|
| `SESSION_COOKIE_LIFETIME` (optional) | Number of minutes, for which authenticated session is valid for, after which user has to login again. defaults to 60.
|
||||||
|
| `SECRET_KEY` (optional) | Long string for signing the session cookies, required if authentication is enabled.
|
||||||
|
|
||||||
* **Setting value for `INDEX_SETTINGS`**
|
* **Setting value for `INDEX_SETTINGS`**
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,18 @@ host = os.environ.get("HOST", "0.0.0.0")
|
||||||
debug = bool(os.environ.get("DEBUG"))
|
debug = bool(os.environ.get("DEBUG"))
|
||||||
block_downloads = bool(os.environ.get("BLOCK_DOWNLOADS"))
|
block_downloads = bool(os.environ.get("BLOCK_DOWNLOADS"))
|
||||||
results_per_page = int(os.environ.get("RESULTS_PER_PAGE", "20"))
|
results_per_page = int(os.environ.get("RESULTS_PER_PAGE", "20"))
|
||||||
logo_folder = Path(
|
logo_folder = Path("/Temp/logo/" if platform.system() == "Windows" else "/tmp/logo")
|
||||||
"/Temp/logo/" if platform.system() == 'Windows' else '/tmp/logo'
|
|
||||||
)
|
|
||||||
logo_folder.mkdir(exist_ok=True)
|
logo_folder.mkdir(exist_ok=True)
|
||||||
|
username = os.environ.get("USERNAME", "")
|
||||||
|
password = os.environ.get("PASSWORD", "")
|
||||||
|
authenticated = username and password
|
||||||
|
SESSION_COOKIE_LIFETIME = int(os.environ.get("SESSION_COOKIE_LIFETIME") or "60")
|
||||||
|
try:
|
||||||
|
SECRET_KEY = os.environ["SECRET_KEY"]
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
if authenticated:
|
||||||
|
traceback.print_exc()
|
||||||
|
print("\n\nPlease set the SECRET_KEY environment variable correctly")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
SECRET_KEY = ""
|
||||||
|
|
42
app/main.py
42
app/main.py
|
@ -8,22 +8,54 @@ from aiohttp import web
|
||||||
|
|
||||||
from .telegram import Client
|
from .telegram import Client
|
||||||
from .routes import setup_routes
|
from .routes import setup_routes
|
||||||
from .views import Views
|
from .views import Views, middleware_factory
|
||||||
from .config import host, port, session_string, api_id, api_hash, debug
|
from .config import (
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
session_string,
|
||||||
|
api_id,
|
||||||
|
api_hash,
|
||||||
|
authenticated,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
SESSION_COOKIE_LIFETIME,
|
||||||
|
SECRET_KEY,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def inspect_rep():
|
||||||
|
@web.middleware
|
||||||
|
async def factory(request, handler):
|
||||||
|
print("Incoming Cookies", request.cookies)
|
||||||
|
response = await handler(request)
|
||||||
|
print("Outgoing Cookies", response.cookies)
|
||||||
|
return response
|
||||||
|
|
||||||
|
return factory
|
||||||
|
|
||||||
|
|
||||||
class Indexer:
|
class Indexer:
|
||||||
|
|
||||||
TEMPLATES_ROOT = pathlib.Path(__file__).parent / 'templates'
|
TEMPLATES_ROOT = pathlib.Path(__file__).parent / "templates"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.server = web.Application()
|
self.server = web.Application(
|
||||||
|
middlewares=[
|
||||||
|
inspect_rep(),
|
||||||
|
middleware_factory(),
|
||||||
|
]
|
||||||
|
)
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.tg_client = Client(session_string, api_id, api_hash)
|
self.tg_client = Client(session_string, api_id, api_hash)
|
||||||
|
|
||||||
|
self.server["is_authenticated"] = authenticated
|
||||||
|
self.server["username"] = username
|
||||||
|
self.server["password"] = password
|
||||||
|
self.server["SESSION_COOKIE_LIFETIME"] = SESSION_COOKIE_LIFETIME
|
||||||
|
self.server["SECRET_KEY"] = SECRET_KEY
|
||||||
|
|
||||||
async def startup(self):
|
async def startup(self):
|
||||||
await self.tg_client.start()
|
await self.tg_client.start()
|
||||||
|
@ -36,12 +68,10 @@ class Indexer:
|
||||||
|
|
||||||
self.server.on_cleanup.append(self.cleanup)
|
self.server.on_cleanup.append(self.cleanup)
|
||||||
|
|
||||||
|
|
||||||
async def cleanup(self, *args):
|
async def cleanup(self, *args):
|
||||||
await self.tg_client.disconnect()
|
await self.tg_client.disconnect()
|
||||||
log.debug("telegram client disconnected!")
|
log.debug("telegram client disconnected!")
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.loop.run_until_complete(self.startup())
|
self.loop.run_until_complete(self.startup())
|
||||||
web.run_app(self.server, host=host, port=port)
|
web.run_app(self.server, host=host, port=port)
|
||||||
|
|
|
@ -18,7 +18,12 @@ async def setup_routes(app, handler):
|
||||||
index_channel = index_settings["index_channel"]
|
index_channel = index_settings["index_channel"]
|
||||||
exclude_chats = index_settings["exclude_chats"]
|
exclude_chats = index_settings["exclude_chats"]
|
||||||
include_chats = index_settings["include_chats"]
|
include_chats = index_settings["include_chats"]
|
||||||
routes = [web.get("/", h.home)]
|
routes = [
|
||||||
|
web.get("/", h.home, name="home"),
|
||||||
|
web.get("/login", h.login_get, name="login_page"),
|
||||||
|
web.post("/login", h.login_post, name="login_handle"),
|
||||||
|
web.get("/logout", h.logout_get, name="logout"),
|
||||||
|
]
|
||||||
if index_all:
|
if index_all:
|
||||||
# print(await client.get_dialogs())
|
# print(await client.get_dialogs())
|
||||||
# dialogs = await client.get_dialogs()
|
# dialogs = await client.get_dialogs()
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
<footer class="text-center text-black w-full my-4 py-4 bg-gray-300 xl:text-xl">
|
</div>
|
||||||
<a href="https://github.com/odysseusmax/tg-index" target="_blank"> @odysseusmax </a>
|
<footer class="w-full my-4 py-4 bg-gray-300 xl:text-xl">
|
||||||
</footer>
|
<div class="mx-auto text-center text-black max-w-screen-xl">
|
||||||
|
<a href="https://github.com/odysseusmax/tg-index" target="_blank"> @odysseusmax </a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
</div>
|
</body>
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
|
@ -1,20 +1,26 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
<head>
|
||||||
<script src="https://cdn.fluidplayer.com/v3/current/fluidplayer.min.js"></script>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||||
{% if title %} {{title}} {% else %} Telegram Index {% endif %}
|
<script src="https://cdn.fluidplayer.com/v3/current/fluidplayer.min.js"></script>
|
||||||
</title>
|
|
||||||
|
|
||||||
</head>
|
<title>
|
||||||
<body>
|
{% if title %} {{title}} {% else %} Telegram Index {% endif %}
|
||||||
|
</title>
|
||||||
|
|
||||||
<div class="m-auto w-full xl:max-w-screen-xl min-h-screen">
|
</head>
|
||||||
|
|
||||||
<header class="flex justify-between bg-red-600 text-white mb-2 p-4 w-full sticky top-0 shadow">
|
<body>
|
||||||
<a href="/" class="text-left text-xl lg:text-2xl xl:text-3xl"> Telegram Index </a>
|
|
||||||
</header>
|
<header class="bg-red-600 text-white mb-2 p-4 w-full shadow">
|
||||||
|
<div class="mx-auto flex justify-between content-center max-w-screen-xl">
|
||||||
|
<a href="/" class="text-left text-xl lg:text-2xl xl:text-3xl"> Telegram Index </a>
|
||||||
|
{% if authenticated %} <a href="/logout" class="text-right"> Logout </a> {% else %} {%
|
||||||
|
endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<div class="m-auto w-full min-h-screen max-w-screen-xl">
|
|
@ -1,20 +1,21 @@
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
|
|
||||||
<h1 class=" my-2 text-2xl text-center font-bold text-green-400">
|
<h1 class=" my-2 text-2xl text-center font-bold text-green-400">
|
||||||
Available Sources
|
Available Sources
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="mx-auto my-2 p-2 w-full">
|
<div class="mx-auto my-2 p-2 w-full">
|
||||||
<div class="block p-4 md:flex md:flex-wrap md:justify-center w-full text-center break-words">
|
<div class="block p-4 md:flex md:flex-wrap md:justify-items-center w-full text-center break-words">
|
||||||
{% for chat in chats %}
|
{% for chat in chats %}
|
||||||
<a title="{{chat.name}}" href="/{{chat.page_id}}" class="flex flex-col justify-around w-full min-h-full md:w-2/5 lg:w-1/5 rounded my-2 md:mx-1 shadow-md hover:shadow-lg hover:border hover:border-blue-300 hover:bg-blue-100">
|
<a title="{{chat.name}}" href="/{{chat.page_id}}"
|
||||||
|
class="mx-auto flex flex-col justify-items-center w-2/3 min-h-full md:w-2/5 lg:w-1/4 rounded my-2 p-2 shadow-md hover:shadow-lg hover:border hover:border-blue-300 hover:bg-blue-100">
|
||||||
|
|
||||||
<img src="/{{chat.page_id}}/logo?big=1" class="w-full rounded-full ">
|
<img src="/{{chat.page_id}}/logo?big=1" class="w-full rounded-full ">
|
||||||
<div class="p-4 rounded">{{chat.name}}</div>
|
<div class="p-4 rounded">{{chat.name}}</div>
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
|
@ -0,0 +1,44 @@
|
||||||
|
{% include 'header.html' %}
|
||||||
|
|
||||||
|
<div class="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="max-w-md w-full space-y-8">
|
||||||
|
<div>
|
||||||
|
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
||||||
|
Sign in to view the contents
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
{% if error %}
|
||||||
|
<div>
|
||||||
|
<h2 class="p-2 text-center text-xl text-red-500 rounded border border-l-4 border-red-500">
|
||||||
|
{{ error }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<form class="mt-8 space-y-6" action="/login" method="POST">
|
||||||
|
<input type="hidden" name="remember" value="true">
|
||||||
|
<div class="rounded-md shadow-sm -space-y-px">
|
||||||
|
<div>
|
||||||
|
<label for="email-address" class="sr-only">Username</label>
|
||||||
|
<input id="email-address" name="username" type="text" required
|
||||||
|
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-900 placeholder-gray-700 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm mb-2"
|
||||||
|
placeholder="Username">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="password" class="sr-only">Password</label>
|
||||||
|
<input id="password" name="password" type="password" autocomplete="current-password" required
|
||||||
|
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-900 placeholder-gray-700 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm mt-2"
|
||||||
|
placeholder="Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit"
|
||||||
|
class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
|
Sign in
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
|
@ -1,8 +1,6 @@
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from telethon.utils import get_display_name
|
|
||||||
|
|
||||||
from .home_view import HomeView
|
from .home_view import HomeView
|
||||||
from .wildcard_view import WildcardView
|
from .wildcard_view import WildcardView
|
||||||
from .download import Download
|
from .download import Download
|
||||||
|
@ -10,30 +8,44 @@ from .index_view import IndexView
|
||||||
from .info_view import InfoView
|
from .info_view import InfoView
|
||||||
from .logo_view import LogoView
|
from .logo_view import LogoView
|
||||||
from .thumbnail_view import ThumbnailView
|
from .thumbnail_view import ThumbnailView
|
||||||
|
from .login_view import LoginView
|
||||||
|
from .logout_view import LogoutView
|
||||||
|
from .middlewhere import middleware_factory
|
||||||
|
|
||||||
|
|
||||||
class Views(HomeView, Download,
|
class Views(
|
||||||
IndexView, InfoView,
|
HomeView,
|
||||||
LogoView, ThumbnailView,
|
Download,
|
||||||
WildcardView):
|
IndexView,
|
||||||
|
InfoView,
|
||||||
|
LogoView,
|
||||||
|
ThumbnailView,
|
||||||
|
WildcardView,
|
||||||
|
LoginView,
|
||||||
|
LogoutView,
|
||||||
|
):
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
self.client = client
|
self.client = client
|
||||||
|
|
||||||
self.alias_ids = []
|
self.chat_ids = {}
|
||||||
self.chat_ids = []
|
|
||||||
|
|
||||||
def generate_alias_id(self, chat):
|
def generate_alias_id(self, chat):
|
||||||
chat_id = chat.id
|
chat_id = chat.id
|
||||||
title = chat.title
|
title = chat.title
|
||||||
while True:
|
while True:
|
||||||
alias_id = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(len(str(chat_id)))])
|
alias_id = "".join(
|
||||||
if alias_id in self.alias_ids:
|
[
|
||||||
|
random.choice(string.ascii_letters + string.digits)
|
||||||
|
for _ in range(len(str(chat_id)))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if alias_id in self.chat_ids:
|
||||||
continue
|
continue
|
||||||
self.alias_ids.append(alias_id)
|
|
||||||
self.chat_ids.append({
|
self.chat_ids[alias_id] = {
|
||||||
'chat_id': chat_id,
|
"chat_id": chat_id,
|
||||||
'alias_id': alias_id,
|
"alias_id": alias_id,
|
||||||
'title': title
|
"title": title,
|
||||||
})
|
}
|
||||||
|
|
||||||
return alias_id
|
return alias_id
|
||||||
|
|
|
@ -10,33 +10,35 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Download:
|
class Download:
|
||||||
|
|
||||||
async def download_get(self, req):
|
async def download_get(self, req):
|
||||||
return await self.handle_request(req)
|
return await self.handle_request(req)
|
||||||
|
|
||||||
|
|
||||||
async def download_head(self, req):
|
async def download_head(self, req):
|
||||||
return await self.handle_request(req, head=True)
|
return await self.handle_request(req, head=True)
|
||||||
|
|
||||||
|
|
||||||
async def handle_request(self, req, head=False):
|
async def handle_request(self, req, head=False):
|
||||||
if block_downloads:
|
if block_downloads:
|
||||||
return web.Response(status=403, text="403: Forbiden" if not head else None)
|
return web.Response(status=403, text="403: Forbiden" if not head else None)
|
||||||
|
|
||||||
file_id = int(req.match_info["id"])
|
file_id = int(req.match_info["id"])
|
||||||
alias_id = req.match_info['chat']
|
alias_id = req.match_info["chat"]
|
||||||
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
chat = self.chat_ids[alias_id]
|
||||||
chat_id = chat['chat_id']
|
chat_id = chat["chat_id"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
except:
|
except Exception:
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
if not message or not message.file:
|
if not message or not message.file:
|
||||||
log.debug(f"no result for {file_id} in {chat_id}")
|
log.debug(f"no result for {file_id} in {chat_id}")
|
||||||
return web.Response(status=410, text="410: Gone. Access to the target resource is no longer available!" if not head else None)
|
return web.Response(
|
||||||
|
status=410,
|
||||||
|
text="410: Gone. Access to the target resource is no longer available!"
|
||||||
|
if not head
|
||||||
|
else None,
|
||||||
|
)
|
||||||
|
|
||||||
media = message.media
|
media = message.media
|
||||||
size = message.file.size
|
size = message.file.size
|
||||||
|
@ -52,14 +54,14 @@ class Download:
|
||||||
return web.Response(
|
return web.Response(
|
||||||
status=416,
|
status=416,
|
||||||
text="416: Range Not Satisfiable" if not head else None,
|
text="416: Range Not Satisfiable" if not head else None,
|
||||||
headers = {
|
headers={"Content-Range": f"bytes */{size}"},
|
||||||
"Content-Range": f"bytes */{size}"
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not head:
|
if not head:
|
||||||
body = self.client.download(media, size, offset, limit)
|
body = self.client.download(media, size, offset, limit)
|
||||||
log.info(f"Serving file in {message.id} (chat {chat_id}) ; Range: {offset} - {limit}")
|
log.info(
|
||||||
|
f"Serving file in {message.id} (chat {chat_id}) ; Range: {offset} - {limit}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
body = None
|
body = None
|
||||||
|
|
||||||
|
@ -68,11 +70,7 @@ class Download:
|
||||||
"Content-Range": f"bytes {offset}-{limit}/{size}",
|
"Content-Range": f"bytes {offset}-{limit}/{size}",
|
||||||
"Content-Length": str(limit - offset),
|
"Content-Length": str(limit - offset),
|
||||||
"Accept-Ranges": "bytes",
|
"Accept-Ranges": "bytes",
|
||||||
"Content-Disposition": f'attachment; filename="{file_name}"'
|
"Content-Disposition": f'attachment; filename="{file_name}"',
|
||||||
}
|
}
|
||||||
|
|
||||||
return web.Response(
|
return web.Response(status=206 if offset else 200, body=body, headers=headers)
|
||||||
status=206 if offset else 200,
|
|
||||||
body=body,
|
|
||||||
headers=headers
|
|
||||||
)
|
|
||||||
|
|
|
@ -3,17 +3,19 @@ import aiohttp_jinja2
|
||||||
|
|
||||||
|
|
||||||
class HomeView:
|
class HomeView:
|
||||||
|
@aiohttp_jinja2.template("home.html")
|
||||||
@aiohttp_jinja2.template('home.html')
|
|
||||||
async def home(self, req):
|
async def home(self, req):
|
||||||
if len(self.chat_ids) == 1:
|
if len(self.chat_ids) == 1:
|
||||||
raise web.HTTPFound(f"{self.chat_ids[0]['alias_id']}")
|
raise web.HTTPFound(f"{self.chat_ids[0]['alias_id']}")
|
||||||
|
|
||||||
chats = []
|
return {
|
||||||
for chat in self.chat_ids:
|
"chats": [
|
||||||
chats.append({
|
{
|
||||||
'page_id': chat['alias_id'],
|
"page_id": chat["alias_id"],
|
||||||
'name': chat['title'],
|
"name": chat["title"],
|
||||||
'url': f"/{chat['alias_id']}"
|
"url": f"/{chat['alias_id']}",
|
||||||
})
|
}
|
||||||
return {'chats': chats}
|
for _, chat in self.chat_ids.items()
|
||||||
|
],
|
||||||
|
"authenticated": req.app["is_authenticated"],
|
||||||
|
}
|
||||||
|
|
|
@ -12,36 +12,39 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IndexView:
|
class IndexView:
|
||||||
|
@aiohttp_jinja2.template("index.html")
|
||||||
@aiohttp_jinja2.template('index.html')
|
|
||||||
async def index(self, req):
|
async def index(self, req):
|
||||||
alias_id = req.match_info['chat']
|
alias_id = req.match_info["chat"]
|
||||||
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
chat = self.chat_ids[alias_id]
|
||||||
log_msg = ''
|
log_msg = ""
|
||||||
try:
|
try:
|
||||||
offset_val = int(req.query.get('page', '1'))
|
offset_val = int(req.query.get("page", "1"))
|
||||||
except:
|
except Exception:
|
||||||
offset_val = 1
|
offset_val = 1
|
||||||
|
|
||||||
log_msg += f"page: {offset_val} | "
|
log_msg += f"page: {offset_val} | "
|
||||||
try:
|
try:
|
||||||
search_query = req.query.get('search', '')
|
search_query = req.query.get("search", "")
|
||||||
except:
|
except Exception:
|
||||||
search_query = ''
|
search_query = ""
|
||||||
|
|
||||||
log_msg += f"search query: {search_query} | "
|
log_msg += f"search query: {search_query} | "
|
||||||
offset_val = 0 if offset_val <=1 else offset_val-1
|
offset_val = 0 if offset_val <= 1 else offset_val - 1
|
||||||
try:
|
try:
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'entity': chat['chat_id'],
|
"entity": chat["chat_id"],
|
||||||
'limit': results_per_page,
|
"limit": results_per_page,
|
||||||
'add_offset': results_per_page*offset_val
|
"add_offset": results_per_page * offset_val,
|
||||||
}
|
}
|
||||||
if search_query:
|
if search_query:
|
||||||
kwargs.update({'search': search_query})
|
kwargs.update({"search": search_query})
|
||||||
|
|
||||||
messages = (await self.client.get_messages(**kwargs)) or []
|
messages = (await self.client.get_messages(**kwargs)) or []
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
log.debug("failed to get messages", exc_info=True)
|
log.debug("failed to get messages", exc_info=True)
|
||||||
messages = []
|
messages = []
|
||||||
|
|
||||||
log_msg += f"found {len(messages)} results | "
|
log_msg += f"found {len(messages)} results | "
|
||||||
log.debug(log_msg)
|
log.debug(log_msg)
|
||||||
results = []
|
results = []
|
||||||
|
@ -53,47 +56,46 @@ class IndexView:
|
||||||
media=True,
|
media=True,
|
||||||
thumbnail=f"/{alias_id}/{m.id}/thumbnail",
|
thumbnail=f"/{alias_id}/{m.id}/thumbnail",
|
||||||
mime_type=m.file.mime_type,
|
mime_type=m.file.mime_type,
|
||||||
insight = get_file_name(m),
|
insight=get_file_name(m),
|
||||||
human_size=get_human_size(m.file.size),
|
human_size=get_human_size(m.file.size),
|
||||||
url=f"/{alias_id}/{m.id}/view"
|
url=f"/{alias_id}/{m.id}/view",
|
||||||
)
|
)
|
||||||
elif m.message:
|
elif m.message:
|
||||||
entry = dict(
|
entry = dict(
|
||||||
file_id=m.id,
|
file_id=m.id,
|
||||||
media=False,
|
media=False,
|
||||||
mime_type='text/plain',
|
mime_type="text/plain",
|
||||||
insight = m.raw_text[:100],
|
insight=m.raw_text[:100],
|
||||||
url=f"/{alias_id}/{m.id}/view"
|
url=f"/{alias_id}/{m.id}/view",
|
||||||
)
|
)
|
||||||
if entry:
|
if entry:
|
||||||
results.append(entry)
|
results.append(entry)
|
||||||
prev_page = False
|
|
||||||
next_page = False
|
|
||||||
if offset_val:
|
|
||||||
query = {'page':offset_val}
|
|
||||||
if search_query:
|
|
||||||
query.update({'search':search_query})
|
|
||||||
prev_page = {
|
|
||||||
'url': str(req.rel_url.with_query(query)),
|
|
||||||
'no': offset_val
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(messages)==results_per_page:
|
prev_page = None
|
||||||
query = {'page':offset_val+2}
|
next_page = None
|
||||||
|
if offset_val:
|
||||||
|
query = {"page": offset_val}
|
||||||
if search_query:
|
if search_query:
|
||||||
query.update({'search':search_query})
|
query.update({"search": search_query})
|
||||||
next_page = {
|
prev_page = {"url": str(req.rel_url.with_query(query)), "no": offset_val}
|
||||||
'url': str(req.rel_url.with_query(query)),
|
|
||||||
'no': offset_val+2
|
if len(messages) == results_per_page:
|
||||||
|
query = {"page": offset_val + 2}
|
||||||
|
if search_query:
|
||||||
|
query.update({"search": search_query})
|
||||||
|
next_page = {
|
||||||
|
"url": str(req.rel_url.with_query(query)),
|
||||||
|
"no": offset_val + 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'item_list':results,
|
"item_list": results,
|
||||||
'prev_page': prev_page,
|
"prev_page": prev_page,
|
||||||
'cur_page' : offset_val+1,
|
"cur_page": offset_val + 1,
|
||||||
'next_page': next_page,
|
"next_page": next_page,
|
||||||
'search': search_query,
|
"search": search_query,
|
||||||
'name' : chat['title'],
|
"name": chat["title"],
|
||||||
'logo': f"/{alias_id}/logo",
|
"logo": f"/{alias_id}/logo",
|
||||||
'title' : "Index of " + chat['title']
|
"title": "Index of " + chat["title"],
|
||||||
|
"authenticated": req.app["is_authenticated"],
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,80 +13,95 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class InfoView:
|
class InfoView:
|
||||||
|
@aiohttp_jinja2.template("info.html")
|
||||||
@aiohttp_jinja2.template('info.html')
|
|
||||||
async def info(self, req):
|
async def info(self, req):
|
||||||
file_id = int(req.match_info["id"])
|
file_id = int(req.match_info["id"])
|
||||||
alias_id = req.match_info['chat']
|
alias_id = req.match_info["chat"]
|
||||||
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
chat = self.chat_ids[alias_id]
|
||||||
chat_id = chat['chat_id']
|
chat_id = chat["chat_id"]
|
||||||
try:
|
try:
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
except:
|
except Exception:
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
if not message or not isinstance(message, Message):
|
if not message or not isinstance(message, Message):
|
||||||
log.debug(f"no valid entry for {file_id} in {chat_id}")
|
log.debug(f"no valid entry for {file_id} in {chat_id}")
|
||||||
return {
|
return {
|
||||||
'found':False,
|
"found": False,
|
||||||
'reason' : "Resource you are looking for cannot be retrived!",
|
"reason": "Resource you are looking for cannot be retrived!",
|
||||||
|
"authenticated": req.app["is_authenticated"],
|
||||||
}
|
}
|
||||||
return_val = {}
|
|
||||||
|
return_val = {
|
||||||
|
"authenticated": req.app["is_authenticated"],
|
||||||
|
}
|
||||||
reply_btns = []
|
reply_btns = []
|
||||||
if message.reply_markup:
|
if message.reply_markup:
|
||||||
if isinstance(message.reply_markup, types.ReplyInlineMarkup):
|
if isinstance(message.reply_markup, types.ReplyInlineMarkup):
|
||||||
for button_row in message.reply_markup.rows:
|
reply_btns = [
|
||||||
btns = []
|
[
|
||||||
for button in button_row.buttons:
|
{"url": button.url, "text": button.text}
|
||||||
if isinstance(button, types.KeyboardButtonUrl):
|
for button in button_row.buttons
|
||||||
btns.append({'url': button.url, 'text': button.text})
|
if isinstance(button, types.KeyboardButtonUrl)
|
||||||
reply_btns.append(btns)
|
]
|
||||||
|
for button_row in message.reply_markup.rows
|
||||||
|
]
|
||||||
|
|
||||||
if message.file and not isinstance(message.media, types.MessageMediaWebPage):
|
if message.file and not isinstance(message.media, types.MessageMediaWebPage):
|
||||||
file_name = get_file_name(message)
|
file_name = get_file_name(message)
|
||||||
human_file_size = get_human_size(message.file.size)
|
human_file_size = get_human_size(message.file.size)
|
||||||
media = {
|
media = {"type": message.file.mime_type}
|
||||||
'type':message.file.mime_type
|
if "video/" in message.file.mime_type:
|
||||||
}
|
media["video"] = True
|
||||||
if 'video/' in message.file.mime_type:
|
elif "audio/" in message.file.mime_type:
|
||||||
media['video'] = True
|
media["audio"] = True
|
||||||
elif 'audio/' in message.file.mime_type:
|
elif "image/" in message.file.mime_type:
|
||||||
media['audio'] = True
|
media["image"] = True
|
||||||
elif 'image/' in message.file.mime_type:
|
|
||||||
media['image'] = True
|
|
||||||
|
|
||||||
if message.text:
|
if message.text:
|
||||||
caption = message.raw_text
|
caption = message.raw_text
|
||||||
else:
|
else:
|
||||||
caption = ''
|
caption = ""
|
||||||
caption_html = Markup.escape(caption).__str__().replace('\n', '<br>')
|
|
||||||
return_val = {
|
caption_html = Markup.escape(caption).__str__().replace("\n", "<br>")
|
||||||
'found': True,
|
return_val.update(
|
||||||
'name': file_name,
|
{
|
||||||
'file_id': file_id,
|
"found": True,
|
||||||
'human_size': human_file_size,
|
"name": file_name,
|
||||||
'media': media,
|
"file_id": file_id,
|
||||||
'caption_html': caption_html,
|
"human_size": human_file_size,
|
||||||
'title': f"Download | {file_name} | {human_file_size}",
|
"media": media,
|
||||||
'reply_btns': reply_btns,
|
"caption_html": caption_html,
|
||||||
'thumbnail': f"/{alias_id}/{file_id}/thumbnail",
|
"title": f"Download | {file_name} | {human_file_size}",
|
||||||
'download_url': '#' if block_downloads else f"/{alias_id}/{file_id}/download",
|
"reply_btns": reply_btns,
|
||||||
'page_id': alias_id,
|
"thumbnail": f"/{alias_id}/{file_id}/thumbnail",
|
||||||
'block_downloads': block_downloads
|
"download_url": "#"
|
||||||
}
|
if block_downloads
|
||||||
|
else f"/{alias_id}/{file_id}/download",
|
||||||
|
"page_id": alias_id,
|
||||||
|
"block_downloads": block_downloads,
|
||||||
|
}
|
||||||
|
)
|
||||||
elif message.message:
|
elif message.message:
|
||||||
text = message.raw_text
|
text = message.raw_text
|
||||||
text_html = Markup.escape(text).__str__().replace('\n', '<br>')
|
text_html = Markup.escape(text).__str__().replace("\n", "<br>")
|
||||||
return_val = {
|
return_val.update(
|
||||||
'found': True,
|
{
|
||||||
'media': False,
|
"found": True,
|
||||||
'text_html': text_html,
|
"media": False,
|
||||||
'reply_btns': reply_btns,
|
"text_html": text_html,
|
||||||
'page_id': alias_id
|
"reply_btns": reply_btns,
|
||||||
}
|
"page_id": alias_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return_val = {
|
return_val.update(
|
||||||
'found':False,
|
{
|
||||||
'reason' : "Some kind of resource that I cannot display",
|
"found": False,
|
||||||
}
|
"reason": "Some kind of resource that I cannot display",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
log.debug(f"data for {file_id} in {chat_id} returned as {return_val}")
|
log.debug(f"data for {file_id} in {chat_id} returned as {return_val}")
|
||||||
return return_val
|
return return_val
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import time
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
import aiohttp_jinja2
|
||||||
|
|
||||||
|
|
||||||
|
class LoginView:
|
||||||
|
@aiohttp_jinja2.template("login.html")
|
||||||
|
async def login_get(self, req):
|
||||||
|
return dict(authenticated=False, **req.query)
|
||||||
|
|
||||||
|
async def login_post(self, req):
|
||||||
|
post_data = await req.post()
|
||||||
|
location = req.app.router["login_page"].url_for()
|
||||||
|
if "username" not in post_data:
|
||||||
|
loc = location.with_query({"error": "Username missing"})
|
||||||
|
raise web.HTTPFound(location=loc)
|
||||||
|
|
||||||
|
if "password" not in post_data:
|
||||||
|
loc = location.with_query({"error": "Password missing"})
|
||||||
|
raise web.HTTPFound(location=loc)
|
||||||
|
|
||||||
|
authenticated = (post_data["username"] == req.app["username"]) and (
|
||||||
|
post_data["password"] == req.app["password"]
|
||||||
|
)
|
||||||
|
if not authenticated:
|
||||||
|
loc = location.with_query({"error": "Wrong Username or Passowrd"})
|
||||||
|
raise web.HTTPFound(location=loc)
|
||||||
|
|
||||||
|
resp = web.Response(
|
||||||
|
status=302, headers={"Location": str(req.app.router["home"].url_for())}
|
||||||
|
)
|
||||||
|
now = time.time()
|
||||||
|
resp.set_cookie(
|
||||||
|
name="_tgindex_session",
|
||||||
|
value=str(now),
|
||||||
|
max_age=60 * req.app["SESSION_COOKIE_LIFETIME"],
|
||||||
|
)
|
||||||
|
digest = hmac.new(
|
||||||
|
req.app["SECRET_KEY"].encode(), str(now).encode(), hashlib.sha256
|
||||||
|
).hexdigest()
|
||||||
|
resp.set_cookie(
|
||||||
|
name="_tgindex_secret",
|
||||||
|
value=digest,
|
||||||
|
max_age=60 * req.app["SESSION_COOKIE_LIFETIME"],
|
||||||
|
)
|
||||||
|
return resp
|
|
@ -1,7 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
import random
|
import random
|
||||||
import os
|
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from telethon.tl import types
|
from telethon.tl import types
|
||||||
|
@ -13,31 +12,32 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class LogoView:
|
class LogoView:
|
||||||
|
|
||||||
async def logo(self, req):
|
async def logo(self, req):
|
||||||
alias_id = req.match_info['chat']
|
alias_id = req.match_info["chat"]
|
||||||
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
chat = self.chat_ids[alias_id]
|
||||||
chat_id = chat['chat_id']
|
chat_id = chat["chat_id"]
|
||||||
chat_name = "Image not available"
|
chat_name = "Image not available"
|
||||||
logo_path = logo_folder.joinpath(f"{alias_id}.jpg")
|
logo_path = logo_folder.joinpath(f"{alias_id}.jpg")
|
||||||
if not logo_path.exists():
|
if not logo_path.exists():
|
||||||
try:
|
try:
|
||||||
photo = await self.client.get_profile_photos(chat_id)
|
photo = await self.client.get_profile_photos(chat_id)
|
||||||
except:
|
except Exception:
|
||||||
log.debug(f"Error in getting profile picture in {chat_id}", exc_info=True)
|
log.debug(
|
||||||
|
f"Error in getting profile picture in {chat_id}", exc_info=True
|
||||||
|
)
|
||||||
photo = None
|
photo = None
|
||||||
|
|
||||||
if not photo:
|
if not photo:
|
||||||
W, H = (160, 160)
|
W, H = (160, 160)
|
||||||
color = tuple([random.randint(0, 255) for i in range(3)])
|
color = tuple([random.randint(0, 255) for i in range(3)])
|
||||||
im = Image.new("RGB", (W,H), color)
|
im = Image.new("RGB", (W, H), color)
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
w, h = draw.textsize(chat_name)
|
w, h = draw.textsize(chat_name)
|
||||||
draw.text(((W-w)/2,(H-h)/2), chat_name, fill="white")
|
draw.text(((W - w) / 2, (H - h) / 2), chat_name, fill="white")
|
||||||
im.save(logo_path)
|
im.save(logo_path)
|
||||||
else:
|
else:
|
||||||
photo = photo[0]
|
photo = photo[0]
|
||||||
pos = -1 if req.query.get('big', None) else int(len(photo.sizes)/2)
|
pos = -1 if req.query.get("big", None) else int(len(photo.sizes) / 2)
|
||||||
size = self.client._get_thumb(photo.sizes, pos)
|
size = self.client._get_thumb(photo.sizes, pos)
|
||||||
if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
||||||
await self.client._download_cached_photo_size(size, logo_path)
|
await self.client._download_cached_photo_size(size, logo_path)
|
||||||
|
@ -46,11 +46,11 @@ class LogoView:
|
||||||
id=photo.id,
|
id=photo.id,
|
||||||
access_hash=photo.access_hash,
|
access_hash=photo.access_hash,
|
||||||
file_reference=photo.file_reference,
|
file_reference=photo.file_reference,
|
||||||
thumb_size=size.type
|
thumb_size=size.type,
|
||||||
)
|
)
|
||||||
await self.client.download_file(media, logo_path)
|
await self.client.download_file(media, logo_path)
|
||||||
|
|
||||||
with open(logo_path, 'rb') as fp:
|
with open(logo_path, "rb") as fp:
|
||||||
body = fp.read()
|
body = fp.read()
|
||||||
|
|
||||||
return web.Response(
|
return web.Response(
|
||||||
|
@ -58,6 +58,6 @@ class LogoView:
|
||||||
body=body,
|
body=body,
|
||||||
headers={
|
headers={
|
||||||
"Content-Type": "image/jpeg",
|
"Content-Type": "image/jpeg",
|
||||||
"Content-Disposition": 'inline; filename="logo.jpg"'
|
"Content-Disposition": 'inline; filename="logo.jpg"',
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
|
||||||
|
class LogoutView:
|
||||||
|
async def logout_get(self, req):
|
||||||
|
resp = web.Response(
|
||||||
|
status=302, headers={"Location": str(req.app.router["home"].url_for())}
|
||||||
|
)
|
||||||
|
resp.del_cookie(name="_tgindex_session")
|
||||||
|
resp.del_cookie(name="_tgindex_secret")
|
||||||
|
return resp
|
|
@ -0,0 +1,49 @@
|
||||||
|
import time
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.web import middleware, HTTPFound
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def middleware_factory():
|
||||||
|
@middleware
|
||||||
|
async def factory(request, handler):
|
||||||
|
if request.app["is_authenticated"] and str(request.rel_url.path) not in [
|
||||||
|
"/login",
|
||||||
|
"/logout",
|
||||||
|
]:
|
||||||
|
cookies = request.cookies
|
||||||
|
url = request.app.router["login_page"].url_for()
|
||||||
|
if any(x not in cookies for x in ("_tgindex_session", "_tgindex_secret")):
|
||||||
|
raise HTTPFound(url)
|
||||||
|
|
||||||
|
tgindex_session = cookies["_tgindex_session"]
|
||||||
|
tgindex_secret = cookies["_tgindex_secret"]
|
||||||
|
calculated_digest = hmac.new(
|
||||||
|
request.app["SECRET_KEY"].encode(),
|
||||||
|
str(tgindex_session).encode(),
|
||||||
|
hashlib.sha256,
|
||||||
|
).hexdigest()
|
||||||
|
if tgindex_secret != calculated_digest:
|
||||||
|
raise HTTPFound(url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
created_at = (
|
||||||
|
float(tgindex_session) + request.app["SESSION_COOKIE_LIFETIME"]
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
time.time()
|
||||||
|
> created_at + 60 * request.app["SESSION_COOKIE_LIFETIME"]
|
||||||
|
):
|
||||||
|
raise HTTPFound(url)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(e, exc_info=True)
|
||||||
|
raise HTTPFound(url)
|
||||||
|
|
||||||
|
return await handler(request)
|
||||||
|
|
||||||
|
return factory
|
|
@ -11,21 +11,23 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ThumbnailView:
|
class ThumbnailView:
|
||||||
|
|
||||||
async def thumbnail_get(self, req):
|
async def thumbnail_get(self, req):
|
||||||
file_id = int(req.match_info["id"])
|
file_id = int(req.match_info["id"])
|
||||||
alias_id = req.match_info['chat']
|
alias_id = req.match_info["chat"]
|
||||||
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
chat = self.chat_ids[alias_id]
|
||||||
chat_id = chat['chat_id']
|
chat_id = chat["chat_id"]
|
||||||
try:
|
try:
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
except:
|
except Exception:
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
if not message or not message.file:
|
if not message or not message.file:
|
||||||
log.debug(f"no result for {file_id} in {chat_id}")
|
log.debug(f"no result for {file_id} in {chat_id}")
|
||||||
return web.Response(status=410, text="410: Gone. Access to the target resource is no longer available!")
|
return web.Response(
|
||||||
|
status=410,
|
||||||
|
text="410: Gone. Access to the target resource is no longer available!",
|
||||||
|
)
|
||||||
|
|
||||||
if message.document:
|
if message.document:
|
||||||
media = message.document
|
media = message.document
|
||||||
|
@ -43,10 +45,13 @@ class ThumbnailView:
|
||||||
im.save(temp, "PNG")
|
im.save(temp, "PNG")
|
||||||
body = temp.getvalue()
|
body = temp.getvalue()
|
||||||
else:
|
else:
|
||||||
thumb_pos = int(len(thumbnails)/2)
|
thumb_pos = int(len(thumbnails) / 2)
|
||||||
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
||||||
if not thumbnail or isinstance(thumbnail, types.PhotoSizeEmpty):
|
if not thumbnail or isinstance(thumbnail, types.PhotoSizeEmpty):
|
||||||
return web.Response(status=410, text="410: Gone. Access to the target resource is no longer available!")
|
return web.Response(
|
||||||
|
status=410,
|
||||||
|
text="410: Gone. Access to the target resource is no longer available!",
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(thumbnail, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
if isinstance(thumbnail, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
||||||
body = self.client._download_cached_photo_size(thumbnail, bytes)
|
body = self.client._download_cached_photo_size(thumbnail, bytes)
|
||||||
|
@ -55,17 +60,16 @@ class ThumbnailView:
|
||||||
id=media.id,
|
id=media.id,
|
||||||
access_hash=media.access_hash,
|
access_hash=media.access_hash,
|
||||||
file_reference=media.file_reference,
|
file_reference=media.file_reference,
|
||||||
thumb_size=thumbnail.type
|
thumb_size=thumbnail.type,
|
||||||
)
|
)
|
||||||
|
|
||||||
body = self.client.iter_download(actual_file)
|
body = self.client.iter_download(actual_file)
|
||||||
|
|
||||||
r = web.Response(
|
return web.Response(
|
||||||
status=200,
|
status=200,
|
||||||
body=body,
|
body=body,
|
||||||
headers={
|
headers={
|
||||||
"Content-Type": "image/jpeg",
|
"Content-Type": "image/jpeg",
|
||||||
"Content-Disposition": 'inline; filename="thumbnail.jpg"'
|
"Content-Disposition": 'inline; filename="thumbnail.jpg"',
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return r
|
|
||||||
|
|
|
@ -2,6 +2,5 @@ from aiohttp import web
|
||||||
|
|
||||||
|
|
||||||
class WildcardView:
|
class WildcardView:
|
||||||
|
|
||||||
async def wildcard(self, req):
|
async def wildcard(self, req):
|
||||||
raise web.HTTPFound('/')
|
raise web.HTTPFound("/")
|
||||||
|
|
Loading…
Reference in New Issue