Wrapper script with disconnect handling; Basic dark mode fix; Include all assets locally

This commit is contained in:
octospacc 2023-09-05 18:15:43 +02:00
parent ef797f127c
commit b412e04726
33 changed files with 69 additions and 76 deletions

2
.gitignore vendored Normal file → Executable file
View File

@ -1,5 +1,6 @@
*.session *.session
*.session-journal *.session-journal
*.session.bak
.env .env
__pycache__/ __pycache__/
venv/ venv/
@ -12,3 +13,4 @@ Procfile
.vscode/ .vscode/
.gitignore .gitignore
pyproject.toml pyproject.toml
*.bak

View File

@ -3,10 +3,8 @@ import logging
from .main import Indexer from .main import Indexer
from .config import debug from .config import debug
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)
logging.getLogger("telethon").setLevel(logging.INFO if debug else logging.ERROR) logging.getLogger("telethon").setLevel(logging.INFO if debug else logging.ERROR)
logging.getLogger("aiohttp").setLevel(logging.INFO if debug else logging.ERROR) logging.getLogger("aiohttp").setLevel(logging.INFO if debug else logging.ERROR)
Indexer().run() Indexer().run()

View File

@ -5,7 +5,6 @@ import json
import sys import sys
import os import os
try: try:
port = int(os.environ.get("PORT", "8080")) port = int(os.environ.get("PORT", "8080"))
except Exception as e: except Exception as e:
@ -45,6 +44,7 @@ password = os.environ.get("PASSWORD", "")
SHORT_URL_LEN = int(os.environ.get("SHORT_URL_LEN", 3)) SHORT_URL_LEN = int(os.environ.get("SHORT_URL_LEN", 3))
authenticated = bool(username and password) authenticated = bool(username and password)
SESSION_COOKIE_LIFETIME = int(os.environ.get("SESSION_COOKIE_LIFETIME") or "60") SESSION_COOKIE_LIFETIME = int(os.environ.get("SESSION_COOKIE_LIFETIME") or "60")
try: try:
SECRET_KEY = os.environ["SECRET_KEY"] SECRET_KEY = os.environ["SECRET_KEY"]
if len(SECRET_KEY) != 32: if len(SECRET_KEY) != 32:

1
app/res/css/tailwind.rx.css Executable file

File diff suppressed because one or more lines are too long

0
app/icons/logout.png → app/res/icons/logout.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

0
app/icons/sun.png → app/res/icons/sun.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
app/templates/js/filesaver.min.js → app/res/js/filesaver.min.js vendored Normal file → Executable file
View File

View File

View File

View File

@ -1,5 +1,6 @@
import logging import logging
from typing import List from typing import List
from os import path
from aiohttp import web from aiohttp import web
from aiohttp.web_routedef import RouteDef from aiohttp.web_routedef import RouteDef
@ -10,7 +11,6 @@ from .views import Views
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def get_common_routes(handler: Views, alias_id: str) -> List[RouteDef]: def get_common_routes(handler: Views, alias_id: str) -> List[RouteDef]:
p = "/{chat:" + alias_id + "}" p = "/{chat:" + alias_id + "}"
return [ return [
@ -34,7 +34,6 @@ def get_common_routes(handler: Views, alias_id: str) -> List[RouteDef]:
), ),
] ]
async def setup_routes(app: web.Application, handler: Views): async def setup_routes(app: web.Application, handler: Views):
client = handler.client client = handler.client
index_all = index_settings["index_all"] index_all = index_settings["index_all"]
@ -49,6 +48,7 @@ async def setup_routes(app: web.Application, handler: Views):
web.post("/login", handler.login_post, name="login_handle"), web.post("/login", handler.login_post, name="login_handle"),
web.get("/logout", handler.logout_get, name="logout"), web.get("/logout", handler.logout_get, name="logout"),
web.get("/favicon.ico", handler.faviconicon, name="favicon"), web.get("/favicon.ico", handler.faviconicon, name="favicon"),
web.static("/static_res", f"{path.dirname(path.abspath(__file__))}/res", name="static_res"),
] ]
if index_all: if index_all:

View File

@ -4,7 +4,6 @@ import asyncio
from telethon import TelegramClient, utils from telethon import TelegramClient, utils
class Client(TelegramClient): class Client(TelegramClient):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__("tg-index.session", *args, **kwargs) super().__init__("tg-index.session", *args, **kwargs)

21
app/templates/footer.html Normal file → Executable file
View File

@ -1,17 +1,16 @@
</div> </div>
<script> <script>
function toggleTheme() { function toggleTheme() {
var Doc = document.documentElement; // var Doc = document.documentElement;
var Class = Array.from(Doc.classList); // var Class = Array.from(Doc.classList);
if (Class.includes('light')) { // if (Class.includes('light')) {
Doc.className = 'dark'; // Doc.className = 'dark';
localStorage.theme = 'dark'; // localStorage.theme = 'dark';
} else // } else
if (Class.includes('dark')) { // if (Class.includes('dark')) {
Doc.className = 'light'; // Doc.className = 'light';
localStorage.theme = 'light'; // localStorage.theme = 'light';
}; // };
}; };
</script> </script>
</body> </body>

60
app/templates/header.html Normal file → Executable file
View File

@ -3,26 +3,48 @@
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.1.4/dist/tailwind-dark.min.css" href_="https://cdn.jsdelivr.net/npm/tailwindcss@2.1.4/dist/tailwind.min.css" rel="stylesheet"/> <link href="{{ app.router['static_res'].url_for(filename='css/tailwind.rx.css') }}" rel="stylesheet"/>
<script type="text/javascript">
{% include "./js/fluidplayer.min.js" %}
</script>
<script>
(function(){
var Doc = document.documentElement;
Doc.className = 'light';
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
Doc.className = 'dark';
} else {
Doc.className = 'light';
};
})();
</script>
<style> <style>
* { * {
font-family: 'Roboto', 'Liberation Sans', 'Noto Sans', sans-serif; font-family: 'Roboto', 'Liberation Sans', 'Noto Sans', sans-serif;
} }
</style> </style>
<style id="StylesheetDarkTheme">
@media (prefers-color-scheme: dark) {
body {
background-color: #000000 !important;
}
.shadow {
--tw-shadow: 0 1px 3px 0 rgba(255, 255, 255, 0.1), 0 1px 2px 0 rgba(255, 255, 255, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 255 255 #FFFF), var(--tw-ring-shadow, 255 255 #FFFF), var(--tw-shadow);
}
.hover\:shadow-lg:hover {
--tw-shadow: 0 10px 15px -3px rgba(255, 255, 255, 0.1), 0 4px 6px -2px rgba(255, 255, 255, 0.05);
box-shadow: var(--tw-ring-offset-shadow, 255 255 #FFFF), var(--tw-ring-shadow, 255 255 #FFFF), var(--tw-shadow);
}
.TgMessageCard {
color: #ffffff;
background-color: #000000;
}
.TgChatCard:hover {
background-color: #241501;
}
}
</style>
<script src="{{ app.router['static_res'].url_for(filename='js/fluidplayer.min.js') }}" type="text/javascript"></script>
<script>
(function(){
// var Doc = document.documentElement;
// Doc.className = 'light';
// if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
// Doc.className = 'dark';
// //StylesheetDarkTheme.innerHTML = StylesheetDarkTheme.innerHTML.trim().split('{').slice(1).join('{').split('}').slice(0, -1).join('}');
// } else {
// Doc.className = 'light';
// //StylesheetDarkTheme.innerHTML = `@media (prefers-color-scheme: dark) { ${StylesheetDarkTheme.innerHTML} }`;
// };
})();
</script>
<title> <title>
{% if title %} {{title}} {% else %} Telegram Index {% endif %} {% if title %} {{title}} {% else %} Telegram Index {% endif %}
</title> </title>
@ -37,12 +59,12 @@
<span style="vertical-align: super; white-space: pre;">[<a <span style="vertical-align: super; white-space: pre;">[<a
href="https://github.com/octospacc/TelegramIndex-Fork" class="font-bold underline" href="https://github.com/octospacc/TelegramIndex-Fork" class="font-bold underline"
>Source Code</a>]</span> >Source Code</a>]</span>
<button onclick="toggleTheme()" class="rounded-full bg-white"> <!--<button onclick="toggleTheme()" class="rounded-full bg-white">
<img src="https://s3.imgcdn.dev/nW9YV.png" class="h-8" alt="Theme"/> <img src="{{ app.router['static_res'].url_for(filename='icons/sun.png') }}" class="h-8" alt="Theme"/>
</button> </button>-->
{% if authenticated %} {% if authenticated %}
<button class="rounded-full bg-white"><a href="/logout"> <button class="rounded-full bg-white"><a href="/logout">
<img src="https://s3.imgcdn.dev/ndsjh.png" class="h-8" alt="Logout"> <img src="{{ app.router['static_res'].url_for(filename='icons/logout.png') }}" class="h-8" alt="Logout">
</a></button> </a></button>
{% else %} {% endif %} {% else %} {% endif %}
</div> </div>

5
app/templates/home.html Normal file → Executable file
View File

@ -8,11 +8,10 @@
<div class="flex flex-wrap gap-4 justify-center w-full"> <div class="flex flex-wrap gap-4 justify-center w-full">
{% for chat in chats %} {% for chat in chats %}
<a href="/{{chat.page_id}}" title="{{chat.name}}" <a href="/{{chat.page_id}}" title="{{chat.name}}"
class="justify-items-center min-h-full w-5/12 sm:w-1/4 md:w-1/5 lg:w-1/6 rounded p-2 text-center break-words shadow hover:shadow-md hover:bg-blue-100 dark:bg-red-700 dark:hover:bg-red-500"> class="TgChatCard justify-items-center min-h-full w-5/12 sm:w-1/4 md:w-1/5 lg:w-1/6 rounded p-2 text-center break-words shadow hover:shadow-md hover:bg-blue-100 dark:bg-red-700 dark:hover:bg-red-500"
>
<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-1 mt-2 rounded text-white bg-blue-500 dark:bg-yellow-500">{{chat.name}}</div> <div class="p-1 mt-2 rounded text-white bg-blue-500 dark:bg-yellow-500">{{chat.name}}</div>
</a> </a>
{% endfor %} {% endfor %}
</div> </div>

10
app/templates/index.html Normal file → Executable file
View File

@ -1,8 +1,6 @@
{% include 'header.html' %} {% block javascript %} {% include 'header.html' %} {% block javascript %}
<script type="text/javascript"> <script src="{{ app.router['static_res'].url_for(filename='js/filesaver.min.js') }}" type="text/javascript"></script>
{% include "./js/filesaver.min.js" %} <script src="{{ app.router['static_res'].url_for(filename='js/playlist.js') }}" type="text/javascript"></script>
{% include "./js/playlist.js" %}
</script>
{% endblock %} {% endblock %}
<div class="block md:flex justify-between items-center px-4 text-center border-b-2"> <div class="block md:flex justify-between items-center px-4 text-center border-b-2">
@ -45,7 +43,7 @@
<!-- Card --> <!-- Card -->
<div title="{% if item.media %} {{item.mime_type}} | {{item.human_size}} {% else %} Text message {% endif %}" <div title="{% if item.media %} {{item.mime_type}} | {{item.human_size}} {% else %} Text message {% endif %}"
class="text-sm items-center justify-center w-full min-h-full sm:w-2/5 md:w-1/4 lg:w-1/6 rounded m-2 shadow hover:shadow-lg dark:bg-red-700"> class="TgMessageCard text-sm items-center justify-center w-full min-h-full sm:w-2/5 md:w-1/4 lg:w-1/6 rounded m-2 shadow hover:shadow-lg dark:bg-black-700">
{% if item.media %} {% if item.media %}
@ -128,7 +126,7 @@
{% else %} {% else %}
<p class="my-4 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl"> <p data-text="No message to display!" class="my-4 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
No message to display! No message to display!
</p> </p>

0
app/templates/info.html Normal file → Executable file
View File

0
app/templates/login.html Normal file → Executable file
View File

0
app/templates/otg.html Normal file → Executable file
View File

View File

@ -3,7 +3,6 @@ from urllib.parse import quote
from telethon.tl.custom import Message from telethon.tl.custom import Message
def get_file_name(message: Message, quote_name: bool = True) -> str: def get_file_name(message: Message, quote_name: bool = True) -> str:
if message.file.name: if message.file.name:
name = message.file.name name = message.file.name
@ -12,7 +11,6 @@ def get_file_name(message: Message, quote_name: bool = True) -> str:
name = f"{message.date.strftime('%Y-%m-%d_%H:%M:%S')}{ext}" name = f"{message.date.strftime('%Y-%m-%d_%H:%M:%S')}{ext}"
return quote(name) if quote_name else name return quote(name) if quote_name else name
def get_human_size(num: Union[int, float]) -> str: def get_human_size(num: Union[int, float]) -> str:
base = 1024.0 base = 1024.0
sufix_list = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] sufix_list = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]

2
app/views/__init__.py Normal file → Executable file
View File

@ -18,10 +18,8 @@ from .logout_view import LogoutView
from .faviconicon_view import FaviconIconView from .faviconicon_view import FaviconIconView
from .middlewhere import middleware_factory from .middlewhere import middleware_factory
TELEGRAM_CHAT = Union[Chat, User, Channel] TELEGRAM_CHAT = Union[Chat, User, Channel]
class Views( class Views(
HomeView, HomeView,
Download, Download,

2
app/views/base.py Normal file → Executable file
View File

@ -4,10 +4,8 @@ from telethon.tl.types import Chat, User, Channel
from ..telegram import Client from ..telegram import Client
TELEGRAM_CHAT = Union[Chat, User, Channel] TELEGRAM_CHAT = Union[Chat, User, Channel]
class BaseView: class BaseView:
client: Client client: Client
url_len: int url_len: int

2
app/views/download.py Normal file → Executable file
View File

@ -7,10 +7,8 @@ from app.util import get_file_name
from app.config import block_downloads from app.config import block_downloads
from .base import BaseView from .base import BaseView
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Download(BaseView): class Download(BaseView):
async def download_get(self, req: web.Request) -> web.Response: async def download_get(self, req: web.Request) -> web.Response:
return await self.handle_request(req) return await self.handle_request(req)

1
app/views/faviconicon_view.py Normal file → Executable file
View File

@ -6,7 +6,6 @@ from aiohttp import web
from app.config import logo_folder from app.config import logo_folder
from .base import BaseView from .base import BaseView
class FaviconIconView(BaseView): class FaviconIconView(BaseView):
async def faviconicon(self, req: web.Request) -> web.Response: async def faviconicon(self, req: web.Request) -> web.Response:
favicon_path = logo_folder.joinpath("favicon.ico") favicon_path = logo_folder.joinpath("favicon.ico")

1
app/views/home_view.py Normal file → Executable file
View File

@ -3,7 +3,6 @@ import aiohttp_jinja2
from .base import BaseView from .base import BaseView
class HomeView(BaseView): class HomeView(BaseView):
@aiohttp_jinja2.template("home.html") @aiohttp_jinja2.template("home.html")
async def home(self, req: web.Request) -> web.Response: async def home(self, req: web.Request) -> web.Response:

2
app/views/index_view.py Normal file → Executable file
View File

@ -10,10 +10,8 @@ from app.config import results_per_page, block_downloads
from app.util import get_file_name, get_human_size from app.util import get_file_name, get_human_size
from .base import BaseView from .base import BaseView
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class IndexView(BaseView): class IndexView(BaseView):
@aiohttp_jinja2.template("index.html") @aiohttp_jinja2.template("index.html")
async def index(self, req: web.Request) -> web.Response: async def index(self, req: web.Request) -> web.Response:

4
app/views/info_view.py Normal file → Executable file
View File

@ -11,10 +11,8 @@ from app.util import get_file_name, get_human_size
from app.config import block_downloads from app.config import block_downloads
from .base import BaseView from .base import BaseView
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class InfoView(BaseView): class InfoView(BaseView):
@aiohttp_jinja2.template("info.html") @aiohttp_jinja2.template("info.html")
async def info(self, req: web.Request) -> web.Response: async def info(self, req: web.Request) -> web.Response:
@ -63,7 +61,7 @@ class InfoView(BaseView):
media["image"] = True media["image"] = True
if message.text: if message.text:
caption = message.raw_text caption = message.text #message.raw_text
else: else:
caption = "" caption = ""

1
app/views/login_view.py Normal file → Executable file
View File

@ -5,7 +5,6 @@ import aiohttp_jinja2
from aiohttp_session import new_session from aiohttp_session import new_session
from .base import BaseView from .base import BaseView
class LoginView(BaseView): class LoginView(BaseView):
@aiohttp_jinja2.template("login.html") @aiohttp_jinja2.template("login.html")
async def login_get(self, req: web.Request) -> web.Response: async def login_get(self, req: web.Request) -> web.Response:

12
app/views/logo_view.py Normal file → Executable file
View File

@ -5,14 +5,11 @@ import random
from aiohttp import web from aiohttp import web
from telethon.tl import types from telethon.tl import types
from app.config import logo_folder from app.config import logo_folder
from .base import BaseView from .base import BaseView
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class LogoView(BaseView): class LogoView(BaseView):
async def logo(self, req: web.Request) -> web.Response: async def logo(self, req: web.Request) -> web.Response:
alias_id = req.match_info["chat"] alias_id = req.match_info["chat"]
@ -40,10 +37,11 @@ class LogoView(BaseView):
im = Image.new("RGB", (W, H), color) im = Image.new("RGB", (W, H), color)
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
font = ImageFont.truetype("arial.ttf", 50) font = ImageFont.truetype("arial.ttf", 50)
w, h = draw.textsize(chat_name, font=font) #w, h = draw.textsize(chat_name, font=font)
draw.text( #draw.text(
((W - w) / 2, (H - h) / 2), chat_name, fill="white", font=font # ((W - w) / 2, (H - h) / 2), chat_name, fill="white", font=font
) #)
draw.text((W, H), chat_name, fill="white", font=font)
im.save(logo_path) im.save(logo_path)
else: else:
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)

1
app/views/logout_view.py Normal file → Executable file
View File

@ -3,7 +3,6 @@ from aiohttp import web
from .base import BaseView from .base import BaseView
class LogoutView(BaseView): class LogoutView(BaseView):
async def logout_get(self, req: web.Request) -> web.Response: async def logout_get(self, req: web.Request) -> web.Response:
session = await get_session(req) session = await get_session(req)

4
app/views/middlewhere.py Normal file → Executable file
View File

@ -6,10 +6,8 @@ from aiohttp.web import middleware, HTTPFound, Response, Request
from aiohttp import BasicAuth, hdrs from aiohttp import BasicAuth, hdrs
from aiohttp_session import get_session from aiohttp_session import get_session
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def _do_basic_auth_check(request: Request) -> Union[None, bool]: def _do_basic_auth_check(request: Request) -> Union[None, bool]:
if "download_" not in request.match_info.route.name: if "download_" not in request.match_info.route.name:
return return
@ -47,7 +45,6 @@ def _do_basic_auth_check(request: Request) -> Union[None, bool]:
return True return True
async def _do_cookies_auth_check(request: Request) -> Union[None, bool]: async def _do_cookies_auth_check(request: Request) -> Union[None, bool]:
session = await get_session(request) session = await get_session(request)
if not session.get("logged_in", False): if not session.get("logged_in", False):
@ -56,7 +53,6 @@ async def _do_cookies_auth_check(request: Request) -> Union[None, bool]:
session["last_at"] = time.time() session["last_at"] = time.time()
return True return True
def middleware_factory() -> Coroutine: def middleware_factory() -> Coroutine:
@middleware @middleware
async def factory(request: Request, handler: Coroutine) -> Response: async def factory(request: Request, handler: Coroutine) -> Response:

2
app/views/thumbnail_view.py Normal file → Executable file
View File

@ -8,10 +8,8 @@ from telethon.tl import types, custom
from .base import BaseView from .base import BaseView
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class ThumbnailView(BaseView): class ThumbnailView(BaseView):
async def thumbnail_get(self, req: web.Request) -> web.Response: async def thumbnail_get(self, req: web.Request) -> web.Response:
file_id = int(req.match_info["id"]) file_id = int(req.match_info["id"])

1
app/views/wildcard_view.py Normal file → Executable file
View File

@ -2,7 +2,6 @@ from aiohttp import web
from .base import BaseView from .base import BaseView
class WildcardView(BaseView): class WildcardView(BaseView):
async def wildcard(self, req: web.Request) -> web.Response: async def wildcard(self, req: web.Request) -> web.Response:
return web.HTTPFound("/") return web.HTTPFound("/")

0
repl-config/.replit Normal file → Executable file
View File