mirror of
https://gitlab.com/octospacc/TelegramIndex-Fork.git
synced 2025-02-16 11:31:44 +01:00
new changes
This commit is contained in:
parent
2fffd9353d
commit
2c0160b21e
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
venv/
|
venv/
|
||||||
*.sh
|
*.sh
|
||||||
|
logo/
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from pathlib import Path
|
||||||
import traceback
|
import traceback
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
@ -24,8 +25,6 @@ except (KeyError, ValueError):
|
|||||||
try:
|
try:
|
||||||
index_settings_str = os.environ["INDEX_SETTINGS"].strip()
|
index_settings_str = os.environ["INDEX_SETTINGS"].strip()
|
||||||
index_settings = json.loads(index_settings_str)
|
index_settings = json.loads(index_settings_str)
|
||||||
otg_settings = index_settings['otg']
|
|
||||||
enable_otg = otg_settings['enable']
|
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print("\n\nPlease set the INDEX_SETTINGS environment variable correctly")
|
print("\n\nPlease set the INDEX_SETTINGS environment variable correctly")
|
||||||
@ -40,5 +39,7 @@ except (KeyError, ValueError):
|
|||||||
|
|
||||||
host = os.environ.get("HOST", "0.0.0.0")
|
host = os.environ.get("HOST", "0.0.0.0")
|
||||||
debug = bool(os.environ.get("DEBUG"))
|
debug = bool(os.environ.get("DEBUG"))
|
||||||
chat_ids = []
|
block_downloads = bool(os.environ.get("BLOCK_DOWNLOADS"))
|
||||||
alias_ids = []
|
results_per_page = int(os.environ.get("RESULTS_PER_PAGE", "20"))
|
||||||
|
logo_folder = Path('logo/')
|
||||||
|
logo_folder.mkdir(exist_ok=True)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from telethon.sync import TelegramClient
|
from telethon.sync import TelegramClient
|
||||||
from telethon.sessions import StringSession
|
from telethon.sessions import StringSession
|
||||||
|
|
||||||
api_id = int(input("Enter your API_ID: "))
|
api_id = int(os.environ.get('API_ID') or input("Enter your API_ID: "))
|
||||||
api_hash = input("Enter your API_HASH: ")
|
api_hash = os.environ.get('API_HASH') or input("Enter your API_HASH: ")
|
||||||
|
|
||||||
with TelegramClient(StringSession(), api_id, api_hash) as client:
|
with TelegramClient(StringSession(), api_id, api_hash) as client:
|
||||||
print(client.session.save())
|
print(client.session.save())
|
||||||
|
@ -3,75 +3,70 @@ import string
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
from telethon.tl.types import Channel, Chat, User
|
||||||
|
|
||||||
from .config import index_settings, alias_ids, chat_ids
|
from .config import index_settings
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_alias_id(chat):
|
|
||||||
chat_id = chat.id
|
|
||||||
title = chat.title
|
|
||||||
while True:
|
|
||||||
alias_id = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(len(str(chat_id)))])
|
|
||||||
if alias_id in alias_ids:
|
|
||||||
continue
|
|
||||||
alias_ids.append(alias_id)
|
|
||||||
chat_ids.append({
|
|
||||||
'chat_id': chat_id,
|
|
||||||
'alias_id': alias_id,
|
|
||||||
'title': title
|
|
||||||
})
|
|
||||||
return alias_id
|
|
||||||
|
|
||||||
|
|
||||||
async def setup_routes(app, handler):
|
async def setup_routes(app, handler):
|
||||||
h = handler
|
h = handler
|
||||||
client = h.client
|
client = h.client
|
||||||
p = r"/{chat:[^/]+}"
|
|
||||||
routes = [
|
|
||||||
web.get('/', h.home),
|
|
||||||
web.post('/otg', h.dynamic_view),
|
|
||||||
web.get('/otg', h.otg_view),
|
|
||||||
web.get(p, h.index),
|
|
||||||
web.get(p + r"/logo", h.logo),
|
|
||||||
web.get(p + r"/{id:\d+}/view", h.info),
|
|
||||||
web.get(p + r"/{id:\d+}/download", h.download_get),
|
|
||||||
web.head(p + r"/{id:\d+}/download", h.download_head),
|
|
||||||
web.get(p + r"/{id:\d+}/thumbnail", h.thumbnail_get),
|
|
||||||
web.view(r'/{wildcard:.*}', h.wildcard)
|
|
||||||
]
|
|
||||||
index_all = index_settings['index_all']
|
index_all = index_settings['index_all']
|
||||||
index_private = index_settings['index_private']
|
index_private = index_settings['index_private']
|
||||||
index_group = index_settings['index_group']
|
index_group = index_settings['index_group']
|
||||||
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)
|
||||||
|
]
|
||||||
if index_all:
|
if index_all:
|
||||||
|
#print(await client.get_dialogs())
|
||||||
async for chat in client.iter_dialogs():
|
async for chat in client.iter_dialogs():
|
||||||
alias_id = None
|
alias_id = None
|
||||||
if chat.id in exclude_chats:
|
if chat.id in exclude_chats:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if chat.is_user:
|
entity = chat.entity
|
||||||
if index_private:
|
|
||||||
alias_id = generate_alias_id(chat)
|
|
||||||
elif chat.is_channel:
|
|
||||||
if index_channel:
|
|
||||||
alias_id = generate_alias_id(chat)
|
|
||||||
else:
|
|
||||||
if index_group:
|
|
||||||
alias_id = generate_alias_id(chat)
|
|
||||||
|
|
||||||
if not alias_id:
|
if isinstance(entity, User) and not index_private:
|
||||||
|
print(f'{chat.title}, private: {index_private}')
|
||||||
continue
|
continue
|
||||||
log.debug(f"Index added for {chat.id} :: {chat.title} at /{alias_id}")
|
elif isinstance(entity, Channel) and not index_channel:
|
||||||
|
print(f'{chat.title}, channel: {index_channel}')
|
||||||
|
continue
|
||||||
|
elif isinstance(entity, Chat) and not index_group:
|
||||||
|
print(f'{chat.title}, group: {index_group}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
alias_id = h.generate_alias_id(chat)
|
||||||
|
p = "/{chat:" + alias_id + "}"
|
||||||
|
routes.extend([
|
||||||
|
web.get(p, h.index),
|
||||||
|
web.get(p + r"/logo", h.logo),
|
||||||
|
web.get(p + r"/{id:\d+}/view", h.info),
|
||||||
|
web.get(p + r"/{id:\d+}/download", h.download_get),
|
||||||
|
web.head(p + r"/{id:\d+}/download", h.download_head),
|
||||||
|
web.get(p + r"/{id:\d+}/thumbnail", h.thumbnail_get),
|
||||||
|
])
|
||||||
|
log.debug(f"Index added for {chat.id} at /{alias_id}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for chat_id in include_chats:
|
for chat_id in include_chats:
|
||||||
chat = await client.get_entity(chat_id)
|
chat = await client.get_entity(chat_id)
|
||||||
alias_id = generate_alias_id(chat)
|
alias_id = h.generate_alias_id(chat)
|
||||||
log.debug(f"Index added for {chat.id} :: {chat.title} at /{alias_id}")
|
p = "/{chat:" + alias_id + "}"
|
||||||
|
routes.extend([
|
||||||
|
web.get(p, h.index),
|
||||||
|
web.get(p + r"/logo", h.logo),
|
||||||
|
web.get(p + r"/{id:\d+}/view", h.info),
|
||||||
|
web.get(p + r"/{id:\d+}/download", h.download_get),
|
||||||
|
web.head(p + r"/{id:\d+}/download", h.download_head),
|
||||||
|
web.get(p + r"/{id:\d+}/thumbnail", h.thumbnail_get),
|
||||||
|
])
|
||||||
|
log.debug(f"Index added for {chat.id} at /{alias_id}")
|
||||||
|
routes.append(web.view(r'/{wildcard:.*}', h.wildcard))
|
||||||
app.add_routes(routes)
|
app.add_routes(routes)
|
||||||
|
@ -10,7 +10,7 @@ class Client(TelegramClient):
|
|||||||
def __init__(self, session_string, *args, **kwargs):
|
def __init__(self, session_string, *args, **kwargs):
|
||||||
super().__init__(StringSession(session_string), *args, **kwargs)
|
super().__init__(StringSession(session_string), *args, **kwargs)
|
||||||
self.log = logging.getLogger(__name__)
|
self.log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def download(self, file, file_size, offset, limit):
|
async def download(self, file, file_size, offset, limit):
|
||||||
part_size_kb = utils.get_appropriated_part_size(file_size)
|
part_size_kb = utils.get_appropriated_part_size(file_size)
|
||||||
@ -25,7 +25,7 @@ class Client(TelegramClient):
|
|||||||
async for chunk in self.iter_download(file, offset=first_part * part_size, request_size=part_size):
|
async for chunk in self.iter_download(file, offset=first_part * part_size, request_size=part_size):
|
||||||
if part == first_part:
|
if part == first_part:
|
||||||
yield chunk[first_part_cut:]
|
yield chunk[first_part_cut:]
|
||||||
elif part == last_part:
|
elif part == last_part-1:
|
||||||
yield chunk[:last_part_cut]
|
yield chunk[:last_part_cut]
|
||||||
else:
|
else:
|
||||||
yield chunk
|
yield chunk
|
||||||
|
@ -1,26 +1,29 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
|
|
||||||
<div class="p-4 space-y-8">
|
<div class="p-4 space-y-8">
|
||||||
{% if found %}
|
{% if found %}
|
||||||
{% if media %}
|
{% if media %}
|
||||||
<h1 class="text-blue-500 text-center text-lg lg:text-xl xl:text-3xl w-full break-all">
|
<h1 class="text-blue-500 text-center text-lg lg:text-xl xl:text-3xl w-full break-all">
|
||||||
Download {{name}}
|
Download {{name}}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="mx-auto w-full md:w-3/4 lg:w-2/5 p-2">
|
<div class="mx-auto w-full p-2 md:flex ">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
{% if media.image %}
|
{% if media.image %}
|
||||||
<img class="mx-auto rounded" src="../{{file_id}}/thumbnail" alt="{{name}}">
|
<img class="mx-auto rounded" src="../{{file_id}}/thumbnail" alt="{{name}}">
|
||||||
{% elif media.video %}
|
{% elif media.video %}
|
||||||
<div id="video-warning" class="mx-auto p-4 bg-gray-600 text-gray-300 rounded border text-center hidden break-words">
|
<div id="video-warning" class="mx-auto p-4 bg-gray-600 text-gray-300 rounded border text-center hidden break-words">
|
||||||
<p> Video {{name}} could not be played!</p>
|
<p> Video {{name}} could not be played!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="my-video-wrapper">
|
<div id="my-video-wrapper">
|
||||||
<video id="my-video-player" class="mx-auto rounded" controls poster="../{{file_id}}/thumbnail">
|
<video id="my-video-player" class="mx-auto rounded" controls poster="../{{file_id}}/thumbnail">
|
||||||
<source src="../{{file_id}}/download" type="video/mp4" />
|
<source src="../{{file_id}}/download" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var video = document.querySelector("video");
|
var video = document.querySelector("video");
|
||||||
var src = video.firstElementChild
|
var src = video.firstElementChild
|
||||||
@ -29,7 +32,7 @@
|
|||||||
console.log(evt);
|
console.log(evt);
|
||||||
document.getElementById('my-video-wrapper').style.display = 'none';
|
document.getElementById('my-video-wrapper').style.display = 'none';
|
||||||
document.getElementById('video-warning').style.display = 'block';
|
document.getElementById('video-warning').style.display = 'block';
|
||||||
});
|
});
|
||||||
var myFP = fluidPlayer(
|
var myFP = fluidPlayer(
|
||||||
'my-video-player',{
|
'my-video-player',{
|
||||||
"layoutControls": {
|
"layoutControls": {
|
||||||
@ -50,7 +53,7 @@
|
|||||||
<div id="audio-warning" class="mx-auto p-4 bg-gray-600 text-gray-300 rounded border text-center hidden break-words">
|
<div id="audio-warning" class="mx-auto p-4 bg-gray-600 text-gray-300 rounded border text-center hidden break-words">
|
||||||
<p> Audio {{name}} could not be played!</p>
|
<p> Audio {{name}} could not be played!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="my-audio-wrapper">
|
<div id="my-audio-wrapper">
|
||||||
<img class="mx-auto rounded w-full" src="../{{file_id}}/thumbnail" alt="{{name}}">
|
<img class="mx-auto rounded w-full" src="../{{file_id}}/thumbnail" alt="{{name}}">
|
||||||
<audio class="mx-auto" controls muted>
|
<audio class="mx-auto" controls muted>
|
||||||
@ -68,9 +71,9 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if caption_html %}
|
{% if caption_html %}
|
||||||
|
|
||||||
<div class="mx-auto mt-1">
|
<div class="mx-auto mt-1">
|
||||||
<div class="flex justify-center text-gray-300 font-mono">
|
<div class="flex justify-center text-gray-300 font-mono">
|
||||||
<p class="text-left w-full rounded p-4 bg-gray-900 shadow-lg"> {{ caption_html|safe }} </p>
|
<p class="text-left w-full rounded p-4 bg-gray-900 shadow-lg"> {{ caption_html|safe }} </p>
|
||||||
@ -87,15 +90,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center text-white">
|
<div class="text-center text-white">
|
||||||
|
|
||||||
<a class="rounded p-3 bg-indigo-500 hover:bg-indigo-700 shadow-lg" href="../{{file_id}}/download">Download Now! ({{ human_size }})</a>
|
<a class="rounded p-3 bg-indigo-500 hover:bg-indigo-700 shadow-lg" href="../{{file_id}}/download">Download Now! ({{ human_size }})</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="mx-auto flex flex-wrap justify-center w-full md:w-3/4 mt-1">
|
<div class="mx-auto flex flex-wrap justify-center w-full md:w-3/4 mt-1">
|
||||||
@ -115,15 +118,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1 class="text-blue-500 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl">Ooops...</h1>
|
<h1 class="text-blue-500 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl">Ooops...</h1>
|
||||||
|
|
||||||
<p class="text-center text-sm md:text-base lg:text-xl xl:text-2xl font-semibold">
|
<p class="text-center text-sm md:text-base lg:text-xl xl:text-2xl font-semibold">
|
||||||
{{ reason }}
|
{{ reason }}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
|
483
app/views.py
483
app/views.py
@ -1,483 +0,0 @@
|
|||||||
import logging
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
import random
|
|
||||||
import io
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
import aiohttp_jinja2
|
|
||||||
from jinja2 import Markup
|
|
||||||
from telethon.tl import types
|
|
||||||
from telethon.tl.custom import Message
|
|
||||||
from telethon.tl.types import User, Chat, Channel
|
|
||||||
|
|
||||||
from .util import get_file_name, get_human_size
|
|
||||||
from .config import otg_settings, chat_ids, enable_otg
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Views:
|
|
||||||
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
|
|
||||||
async def wildcard(self, req):
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
|
|
||||||
|
|
||||||
@aiohttp_jinja2.template('home.html')
|
|
||||||
async def home(self, req):
|
|
||||||
if len(chat_ids) == 1:
|
|
||||||
raise web.HTTPFound(f"{chat_ids[0]['alias_id']}")
|
|
||||||
|
|
||||||
chats = []
|
|
||||||
for chat in chat_ids:
|
|
||||||
chats.append({
|
|
||||||
'page_id': chat['alias_id'],
|
|
||||||
'name': chat['title'],
|
|
||||||
'url': req.rel_url.path + f"/{chat['alias_id']}"
|
|
||||||
})
|
|
||||||
return {'chats':chats, 'otg': enable_otg}
|
|
||||||
|
|
||||||
|
|
||||||
@aiohttp_jinja2.template('otg.html')
|
|
||||||
async def otg_view(self, req):
|
|
||||||
if not enable_otg:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
return_data = {}
|
|
||||||
error = req.query.get('e')
|
|
||||||
if error:
|
|
||||||
return_data.update({'error': error})
|
|
||||||
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def dynamic_view(self, req):
|
|
||||||
if not enable_otg:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
|
|
||||||
rel_url = req.rel_url
|
|
||||||
include_private = otg_settings['include_private']
|
|
||||||
include_group = otg_settings['include_group']
|
|
||||||
include_channel = otg_settings['include_channel']
|
|
||||||
post_data = await req.post()
|
|
||||||
raw_id = post_data.get('id')
|
|
||||||
if not raw_id:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
|
|
||||||
raw_id.replace('@', '')
|
|
||||||
try:
|
|
||||||
chat = await self.client.get_entity(raw_id)
|
|
||||||
except Exception as e:
|
|
||||||
log.debug(e, exc_info=True)
|
|
||||||
raise web.HTTPFound(rel_url.with_query({'e': f"No chat found with username {raw_id}"}))
|
|
||||||
|
|
||||||
if isinstance(chat, User) and not include_private:
|
|
||||||
raise web.HTTPFound(rel_url.with_query({'e': "Indexing private chats is not supported!!"}))
|
|
||||||
elif isinstance(chat, Channel) and not include_channel:
|
|
||||||
raise web.HTTPFound(rel_url.with_query({'e': "Indexing channels is not supported!!"}))
|
|
||||||
elif isinstance(chat, Chat) and not include_group:
|
|
||||||
raise web.HTTPFound(rel_url.with_query({'e': "Indexing group chats is not supported!!"}))
|
|
||||||
|
|
||||||
log.debug(f"chat {chat} accessed!!")
|
|
||||||
raise web.HTTPFound(f'/{chat.id}')
|
|
||||||
|
|
||||||
|
|
||||||
@aiohttp_jinja2.template('index.html')
|
|
||||||
async def index(self, req):
|
|
||||||
alias_id = req.match_info['chat']
|
|
||||||
chat = [i for i in chat_ids if i['alias_id'] == alias_id]
|
|
||||||
if not chat:
|
|
||||||
if not enable_otg:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
|
|
||||||
try:
|
|
||||||
chat_id = int(alias_id)
|
|
||||||
chat_ = await self.client.get_entity(chat_id)
|
|
||||||
chat_name = chat_.title
|
|
||||||
except:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
else:
|
|
||||||
chat = chat[0]
|
|
||||||
chat_id = chat['chat_id']
|
|
||||||
chat_name = chat['title']
|
|
||||||
log_msg = ''
|
|
||||||
try:
|
|
||||||
offset_val = int(req.query.get('page', '1'))
|
|
||||||
except:
|
|
||||||
offset_val = 1
|
|
||||||
log_msg += f"page: {offset_val} | "
|
|
||||||
try:
|
|
||||||
search_query = req.query.get('search', '')
|
|
||||||
except:
|
|
||||||
search_query = ''
|
|
||||||
log_msg += f"search query: {search_query} | "
|
|
||||||
offset_val = 0 if offset_val <=1 else offset_val-1
|
|
||||||
try:
|
|
||||||
kwargs = {
|
|
||||||
'entity': chat_id,
|
|
||||||
'limit': 20,
|
|
||||||
'add_offset': 20*offset_val
|
|
||||||
}
|
|
||||||
if search_query:
|
|
||||||
kwargs.update({'search': search_query})
|
|
||||||
messages = (await self.client.get_messages(**kwargs)) or []
|
|
||||||
|
|
||||||
except:
|
|
||||||
log.debug("failed to get messages", exc_info=True)
|
|
||||||
messages = []
|
|
||||||
log_msg += f"found {len(messages)} results | "
|
|
||||||
log.debug(log_msg)
|
|
||||||
results = []
|
|
||||||
for m in messages:
|
|
||||||
entry = None
|
|
||||||
if m.file and not isinstance(m.media, types.MessageMediaWebPage):
|
|
||||||
entry = dict(
|
|
||||||
file_id=m.id,
|
|
||||||
media=True,
|
|
||||||
thumbnail=f"/{alias_id}/{m.id}/thumbnail",
|
|
||||||
mime_type=m.file.mime_type,
|
|
||||||
insight = get_file_name(m),
|
|
||||||
date = str(m.date),
|
|
||||||
size=m.file.size,
|
|
||||||
human_size=get_human_size(m.file.size),
|
|
||||||
url=req.rel_url.path + f"/{m.id}/view"
|
|
||||||
)
|
|
||||||
elif m.message:
|
|
||||||
entry = dict(
|
|
||||||
file_id=m.id,
|
|
||||||
media=False,
|
|
||||||
mime_type='text/plain',
|
|
||||||
insight = m.raw_text[:100],
|
|
||||||
date = str(m.date),
|
|
||||||
size=len(m.raw_text),
|
|
||||||
human_size=get_human_size(len(m.raw_text)),
|
|
||||||
url=req.rel_url.path + f"/{m.id}/view"
|
|
||||||
)
|
|
||||||
if 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)==20:
|
|
||||||
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 {
|
|
||||||
'item_list':results,
|
|
||||||
'prev_page': prev_page,
|
|
||||||
'cur_page' : offset_val+1,
|
|
||||||
'next_page': next_page,
|
|
||||||
'search': search_query,
|
|
||||||
'name' : chat_name,
|
|
||||||
'logo': f"/{alias_id}/logo",
|
|
||||||
'title' : "Index of " + chat_name
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@aiohttp_jinja2.template('info.html')
|
|
||||||
async def info(self, req):
|
|
||||||
file_id = int(req.match_info["id"])
|
|
||||||
alias_id = req.match_info['chat']
|
|
||||||
chat = [i for i in chat_ids if i['alias_id'] == alias_id]
|
|
||||||
if not chat:
|
|
||||||
if not enable_otg:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
try:
|
|
||||||
chat_id = int(alias_id)
|
|
||||||
except:
|
|
||||||
raise web.HTTPFound('/')
|
|
||||||
else:
|
|
||||||
chat = chat[0]
|
|
||||||
chat_id = chat['chat_id']
|
|
||||||
try:
|
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
|
||||||
except:
|
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
|
||||||
message = None
|
|
||||||
if not message or not isinstance(message, Message):
|
|
||||||
log.debug(f"no valid entry for {file_id} in {chat_id}")
|
|
||||||
return {
|
|
||||||
'found':False,
|
|
||||||
'reason' : "Entry you are looking for cannot be retrived!",
|
|
||||||
}
|
|
||||||
return_val = {}
|
|
||||||
reply_btns = []
|
|
||||||
if message.reply_markup:
|
|
||||||
if isinstance(message.reply_markup, types.ReplyInlineMarkup):
|
|
||||||
for button_row in message.reply_markup.rows:
|
|
||||||
btns = []
|
|
||||||
for button in button_row.buttons:
|
|
||||||
if isinstance(button, types.KeyboardButtonUrl):
|
|
||||||
btns.append({'url': button.url, 'text': button.text})
|
|
||||||
reply_btns.append(btns)
|
|
||||||
if message.file and not isinstance(message.media, types.MessageMediaWebPage):
|
|
||||||
file_name = get_file_name(message)
|
|
||||||
file_size = message.file.size
|
|
||||||
human_file_size = get_human_size(file_size)
|
|
||||||
media = {
|
|
||||||
'type':message.file.mime_type
|
|
||||||
}
|
|
||||||
if 'video/' in message.file.mime_type:
|
|
||||||
media.update({
|
|
||||||
'video' : True
|
|
||||||
})
|
|
||||||
elif 'audio/' in message.file.mime_type:
|
|
||||||
media['audio'] = True
|
|
||||||
elif 'image/' in message.file.mime_type:
|
|
||||||
media['image'] = True
|
|
||||||
|
|
||||||
if message.text:
|
|
||||||
caption = message.raw_text
|
|
||||||
else:
|
|
||||||
caption = ''
|
|
||||||
caption_html = Markup.escape(caption).__str__().replace('\n', '<br>')
|
|
||||||
return_val = {
|
|
||||||
'found': True,
|
|
||||||
'name': file_name,
|
|
||||||
'file_id': file_id,
|
|
||||||
'size': file_size,
|
|
||||||
'human_size': human_file_size,
|
|
||||||
'media': media,
|
|
||||||
'caption_html': caption_html,
|
|
||||||
'caption': caption,
|
|
||||||
'title': f"Download | {file_name} | {human_file_size}",
|
|
||||||
'reply_btns': reply_btns,
|
|
||||||
'thumbnail': f"/{alias_id}/{file_id}/thumbnail",
|
|
||||||
'download_url': f"/{alias_id}/{file_id}/download",
|
|
||||||
'page_id': alias_id
|
|
||||||
}
|
|
||||||
elif message.message:
|
|
||||||
text = message.raw_text
|
|
||||||
text_html = Markup.escape(text).__str__().replace('\n', '<br>')
|
|
||||||
return_val = {
|
|
||||||
'found': True,
|
|
||||||
'media': False,
|
|
||||||
'text': text,
|
|
||||||
'text_html': text_html,
|
|
||||||
'reply_btns': reply_btns,
|
|
||||||
'page_id': alias_id
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return_val = {
|
|
||||||
'found':False,
|
|
||||||
'reason' : "Some kind of entry that I cannot display",
|
|
||||||
}
|
|
||||||
log.debug(f"data for {file_id} in {chat_id} returned as {return_val}")
|
|
||||||
return return_val
|
|
||||||
|
|
||||||
|
|
||||||
async def logo(self, req):
|
|
||||||
alias_id = req.match_info['chat']
|
|
||||||
chat = [i for i in chat_ids if i['alias_id'] == alias_id]
|
|
||||||
if not chat:
|
|
||||||
if not enable_otg:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
try:
|
|
||||||
chat_id = int(alias_id)
|
|
||||||
except:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
else:
|
|
||||||
chat = chat[0]
|
|
||||||
chat_id = chat['chat_id']
|
|
||||||
chat_name = "Image not available"
|
|
||||||
try:
|
|
||||||
photo = await self.client.get_profile_photos(chat_id)
|
|
||||||
except:
|
|
||||||
log.debug(f"Error in getting profile picture in {chat_id}", exc_info=True)
|
|
||||||
photo = None
|
|
||||||
if not photo:
|
|
||||||
W, H = (160, 160)
|
|
||||||
c = lambda : random.randint(0, 255)
|
|
||||||
color = tuple([c() for i in range(3)])
|
|
||||||
im = Image.new("RGB", (W,H), color)
|
|
||||||
draw = ImageDraw.Draw(im)
|
|
||||||
w, h = draw.textsize(chat_name)
|
|
||||||
draw.text(((W-w)/2,(H-h)/2), chat_name, fill="white")
|
|
||||||
temp = io.BytesIO()
|
|
||||||
im.save(temp, "PNG")
|
|
||||||
body = temp.getvalue()
|
|
||||||
else:
|
|
||||||
photo = photo[0]
|
|
||||||
pos = -1 if req.query.get('big', None) else int(len(photo.sizes)/2)
|
|
||||||
size = self.client._get_thumb(photo.sizes, pos)
|
|
||||||
if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
|
||||||
body = self.client._download_cached_photo_size(size, bytes)
|
|
||||||
else:
|
|
||||||
media = types.InputPhotoFileLocation(
|
|
||||||
id=photo.id,
|
|
||||||
access_hash=photo.access_hash,
|
|
||||||
file_reference=photo.file_reference,
|
|
||||||
thumb_size=size.type
|
|
||||||
)
|
|
||||||
body = self.client.iter_download(media)
|
|
||||||
|
|
||||||
r = web.Response(
|
|
||||||
status=200,
|
|
||||||
body=body,
|
|
||||||
headers={
|
|
||||||
"Content-Type": "image/jpeg",
|
|
||||||
"Content-Disposition": 'inline; filename="logo.jpg"'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
#r.enable_chunked_encoding()
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
async def download_get(self, req):
|
|
||||||
return await self.handle_request(req)
|
|
||||||
|
|
||||||
|
|
||||||
async def download_head(self, req):
|
|
||||||
return await self.handle_request(req, head=True)
|
|
||||||
|
|
||||||
|
|
||||||
async def thumbnail_get(self, req):
|
|
||||||
file_id = int(req.match_info["id"])
|
|
||||||
alias_id = req.match_info['chat']
|
|
||||||
chat = [i for i in chat_ids if i['alias_id'] == alias_id]
|
|
||||||
if not chat:
|
|
||||||
if not enable_otg:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
try:
|
|
||||||
chat_id = int(alias_id)
|
|
||||||
except:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
else:
|
|
||||||
chat = chat[0]
|
|
||||||
chat_id = chat['chat_id']
|
|
||||||
try:
|
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
|
||||||
except:
|
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
|
||||||
message = None
|
|
||||||
|
|
||||||
if not message or not message.file:
|
|
||||||
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 message.document:
|
|
||||||
media = message.document
|
|
||||||
thumbnails = media.thumbs
|
|
||||||
location = types.InputDocumentFileLocation
|
|
||||||
else:
|
|
||||||
media = message.photo
|
|
||||||
thumbnails = media.sizes
|
|
||||||
location = types.InputPhotoFileLocation
|
|
||||||
|
|
||||||
if not thumbnails:
|
|
||||||
c = lambda : random.randint(0, 255)
|
|
||||||
color = tuple([c() for i in range(3)])
|
|
||||||
im = Image.new("RGB", (100, 100), color)
|
|
||||||
temp = io.BytesIO()
|
|
||||||
im.save(temp, "PNG")
|
|
||||||
body = temp.getvalue()
|
|
||||||
else:
|
|
||||||
thumb_pos = int(len(thumbnails)/2)
|
|
||||||
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
|
||||||
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!")
|
|
||||||
|
|
||||||
if isinstance(thumbnail, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
|
||||||
body = self.client._download_cached_photo_size(thumbnail, bytes)
|
|
||||||
else:
|
|
||||||
actual_file = location(
|
|
||||||
id=media.id,
|
|
||||||
access_hash=media.access_hash,
|
|
||||||
file_reference=media.file_reference,
|
|
||||||
thumb_size=thumbnail.type
|
|
||||||
)
|
|
||||||
|
|
||||||
body = self.client.iter_download(actual_file)
|
|
||||||
|
|
||||||
r = web.Response(
|
|
||||||
status=200,
|
|
||||||
body=body,
|
|
||||||
headers={
|
|
||||||
"Content-Type": "image/jpeg",
|
|
||||||
"Content-Disposition": 'inline; filename="thumbnail.jpg"'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
#r.enable_chunked_encoding()
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_request(self, req, head=False):
|
|
||||||
file_id = int(req.match_info["id"])
|
|
||||||
alias_id = req.match_info['chat']
|
|
||||||
chat = [i for i in chat_ids if i['alias_id'] == alias_id]
|
|
||||||
if not chat:
|
|
||||||
if not enable_otg:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
try:
|
|
||||||
chat_id = int(alias_id)
|
|
||||||
except:
|
|
||||||
return web.Response(status=403, text="403: Forbiden")
|
|
||||||
else:
|
|
||||||
chat = chat[0]
|
|
||||||
chat_id = chat['chat_id']
|
|
||||||
|
|
||||||
try:
|
|
||||||
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
|
||||||
except:
|
|
||||||
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
|
||||||
message = None
|
|
||||||
|
|
||||||
if not message or not message.file:
|
|
||||||
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!")
|
|
||||||
|
|
||||||
media = message.media
|
|
||||||
size = message.file.size
|
|
||||||
file_name = get_file_name(message)
|
|
||||||
mime_type = message.file.mime_type
|
|
||||||
|
|
||||||
try:
|
|
||||||
offset = req.http_range.start or 0
|
|
||||||
limit = req.http_range.stop or size
|
|
||||||
if (limit > size) or (offset < 0) or (limit < offset):
|
|
||||||
raise ValueError("range not in acceptable format")
|
|
||||||
except ValueError:
|
|
||||||
return web.Response(
|
|
||||||
status=416,
|
|
||||||
text="416: Range Not Satisfiable",
|
|
||||||
headers = {
|
|
||||||
"Content-Range": f"bytes */{size}"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not head:
|
|
||||||
body = self.client.download(media, size, offset, limit)
|
|
||||||
log.info(f"Serving file in {message.id} (chat {chat_id}) ; Range: {offset} - {limit}")
|
|
||||||
else:
|
|
||||||
body = None
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
"Content-Type": mime_type,
|
|
||||||
"Content-Range": f"bytes {offset}-{limit}/{size}",
|
|
||||||
"Content-Length": str(limit - offset),
|
|
||||||
"Accept-Ranges": "bytes",
|
|
||||||
"Content-Disposition": f'attachment; filename="{file_name}"'
|
|
||||||
}
|
|
||||||
|
|
||||||
return web.Response(
|
|
||||||
status=206 if offset else 200,
|
|
||||||
body=body,
|
|
||||||
headers=headers
|
|
||||||
)
|
|
39
app/views/__init__.py
Normal file
39
app/views/__init__.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from telethon.utils import get_display_name
|
||||||
|
|
||||||
|
from .home_view import HomeView
|
||||||
|
from .wildcard_view import WildcardView
|
||||||
|
from .download import Download
|
||||||
|
from .index_view import IndexView
|
||||||
|
from .info_view import InfoView
|
||||||
|
from .logo_view import LogoView
|
||||||
|
from .thumbnail_view import ThumbnailView
|
||||||
|
|
||||||
|
|
||||||
|
class Views(HomeView, Download,
|
||||||
|
IndexView, InfoView,
|
||||||
|
LogoView, ThumbnailView,
|
||||||
|
WildcardView):
|
||||||
|
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
self.alias_ids = []
|
||||||
|
self.chat_ids = []
|
||||||
|
|
||||||
|
def generate_alias_id(self, chat):
|
||||||
|
chat_id = chat.id
|
||||||
|
title = chat.title
|
||||||
|
while True:
|
||||||
|
alias_id = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(len(str(chat_id)))])
|
||||||
|
if alias_id in self.alias_ids:
|
||||||
|
continue
|
||||||
|
self.alias_ids.append(alias_id)
|
||||||
|
self.chat_ids.append({
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'alias_id': alias_id,
|
||||||
|
'title': title
|
||||||
|
})
|
||||||
|
return alias_id
|
78
app/views/download.py
Normal file
78
app/views/download.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from app.util import get_file_name
|
||||||
|
from app.config import block_downloads
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Download:
|
||||||
|
|
||||||
|
async def download_get(self, req):
|
||||||
|
return await self.handle_request(req)
|
||||||
|
|
||||||
|
|
||||||
|
async def download_head(self, req):
|
||||||
|
return await self.handle_request(req, head=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_request(self, req, head=False):
|
||||||
|
if block_downloads:
|
||||||
|
return web.Response(status=403, text="403: Forbiden")
|
||||||
|
|
||||||
|
file_id = int(req.match_info["id"])
|
||||||
|
alias_id = req.match_info['chat']
|
||||||
|
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
||||||
|
chat_id = chat['chat_id']
|
||||||
|
|
||||||
|
try:
|
||||||
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
|
except:
|
||||||
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
|
message = None
|
||||||
|
|
||||||
|
if not message or not message.file:
|
||||||
|
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!")
|
||||||
|
|
||||||
|
media = message.media
|
||||||
|
size = message.file.size
|
||||||
|
file_name = get_file_name(message)
|
||||||
|
mime_type = message.file.mime_type
|
||||||
|
|
||||||
|
try:
|
||||||
|
offset = req.http_range.start or 0
|
||||||
|
limit = req.http_range.stop or size
|
||||||
|
if (limit > size) or (offset < 0) or (limit < offset):
|
||||||
|
raise ValueError("range not in acceptable format")
|
||||||
|
except ValueError:
|
||||||
|
return web.Response(
|
||||||
|
status=416,
|
||||||
|
text="416: Range Not Satisfiable",
|
||||||
|
headers = {
|
||||||
|
"Content-Range": f"bytes */{size}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if not head:
|
||||||
|
body = self.client.download(media, size, offset, limit)
|
||||||
|
log.info(f"Serving file in {message.id} (chat {chat_id}) ; Range: {offset} - {limit}")
|
||||||
|
else:
|
||||||
|
body = None
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": mime_type,
|
||||||
|
"Content-Range": f"bytes {offset}-{limit}/{size}",
|
||||||
|
"Content-Length": str(limit - offset),
|
||||||
|
"Accept-Ranges": "bytes",
|
||||||
|
"Content-Disposition": f'attachment; filename="{file_name}"'
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.Response(
|
||||||
|
status=206 if offset else 200,
|
||||||
|
body=body,
|
||||||
|
headers=headers
|
||||||
|
)
|
19
app/views/home_view.py
Normal file
19
app/views/home_view.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from aiohttp import web
|
||||||
|
import aiohttp_jinja2
|
||||||
|
|
||||||
|
|
||||||
|
class HomeView:
|
||||||
|
|
||||||
|
@aiohttp_jinja2.template('home.html')
|
||||||
|
async def home(self, req):
|
||||||
|
if len(self.chat_ids) == 1:
|
||||||
|
raise web.HTTPFound(f"{self.chat_ids[0]['alias_id']}")
|
||||||
|
|
||||||
|
chats = []
|
||||||
|
for chat in self.chat_ids:
|
||||||
|
chats.append({
|
||||||
|
'page_id': chat['alias_id'],
|
||||||
|
'name': chat['title'],
|
||||||
|
'url': f"/{chat['alias_id']}"
|
||||||
|
})
|
||||||
|
return {'chats': chats}
|
99
app/views/index_view.py
Normal file
99
app/views/index_view.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import aiohttp_jinja2
|
||||||
|
|
||||||
|
from telethon.tl import types
|
||||||
|
|
||||||
|
from app.config import results_per_page
|
||||||
|
from app.util import get_file_name, get_human_size
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView:
|
||||||
|
|
||||||
|
@aiohttp_jinja2.template('index.html')
|
||||||
|
async def index(self, req):
|
||||||
|
alias_id = req.match_info['chat']
|
||||||
|
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
||||||
|
log_msg = ''
|
||||||
|
try:
|
||||||
|
offset_val = int(req.query.get('page', '1'))
|
||||||
|
except:
|
||||||
|
offset_val = 1
|
||||||
|
log_msg += f"page: {offset_val} | "
|
||||||
|
try:
|
||||||
|
search_query = req.query.get('search', '')
|
||||||
|
except:
|
||||||
|
search_query = ''
|
||||||
|
log_msg += f"search query: {search_query} | "
|
||||||
|
offset_val = 0 if offset_val <=1 else offset_val-1
|
||||||
|
try:
|
||||||
|
kwargs = {
|
||||||
|
'entity': chat['chat_id'],
|
||||||
|
'limit': results_per_page,
|
||||||
|
'add_offset': results_per_page*offset_val
|
||||||
|
}
|
||||||
|
if search_query:
|
||||||
|
kwargs.update({'search': search_query})
|
||||||
|
messages = (await self.client.get_messages(**kwargs)) or []
|
||||||
|
|
||||||
|
except:
|
||||||
|
log.debug("failed to get messages", exc_info=True)
|
||||||
|
messages = []
|
||||||
|
log_msg += f"found {len(messages)} results | "
|
||||||
|
log.debug(log_msg)
|
||||||
|
results = []
|
||||||
|
for m in messages:
|
||||||
|
entry = None
|
||||||
|
if m.file and not isinstance(m.media, types.MessageMediaWebPage):
|
||||||
|
entry = dict(
|
||||||
|
file_id=m.id,
|
||||||
|
media=True,
|
||||||
|
thumbnail=f"/{alias_id}/{m.id}/thumbnail",
|
||||||
|
mime_type=m.file.mime_type,
|
||||||
|
insight = get_file_name(m),
|
||||||
|
human_size=get_human_size(m.file.size),
|
||||||
|
url=f"/{alias_id}/{m.id}/view"
|
||||||
|
)
|
||||||
|
elif m.message:
|
||||||
|
entry = dict(
|
||||||
|
file_id=m.id,
|
||||||
|
media=False,
|
||||||
|
mime_type='text/plain',
|
||||||
|
insight = m.raw_text[:100],
|
||||||
|
url=f"/{alias_id}/{m.id}/view"
|
||||||
|
)
|
||||||
|
if 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:
|
||||||
|
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 {
|
||||||
|
'item_list':results,
|
||||||
|
'prev_page': prev_page,
|
||||||
|
'cur_page' : offset_val+1,
|
||||||
|
'next_page': next_page,
|
||||||
|
'search': search_query,
|
||||||
|
'name' : chat['title'],
|
||||||
|
'logo': f"/{alias_id}/logo",
|
||||||
|
'title' : "Index of " + chat['title']
|
||||||
|
}
|
90
app/views/info_view.py
Normal file
90
app/views/info_view.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import aiohttp_jinja2
|
||||||
|
from telethon.tl import types
|
||||||
|
from telethon.tl.custom import Message
|
||||||
|
from jinja2 import Markup
|
||||||
|
|
||||||
|
from app.util import get_file_name, get_human_size
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InfoView:
|
||||||
|
|
||||||
|
@aiohttp_jinja2.template('info.html')
|
||||||
|
async def info(self, req):
|
||||||
|
file_id = int(req.match_info["id"])
|
||||||
|
alias_id = req.match_info['chat']
|
||||||
|
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
||||||
|
chat_id = chat['chat_id']
|
||||||
|
try:
|
||||||
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
|
except:
|
||||||
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
|
message = None
|
||||||
|
if not message or not isinstance(message, Message):
|
||||||
|
log.debug(f"no valid entry for {file_id} in {chat_id}")
|
||||||
|
return {
|
||||||
|
'found':False,
|
||||||
|
'reason' : "Resource you are looking for cannot be retrived!",
|
||||||
|
}
|
||||||
|
return_val = {}
|
||||||
|
reply_btns = []
|
||||||
|
if message.reply_markup:
|
||||||
|
if isinstance(message.reply_markup, types.ReplyInlineMarkup):
|
||||||
|
for button_row in message.reply_markup.rows:
|
||||||
|
btns = []
|
||||||
|
for button in button_row.buttons:
|
||||||
|
if isinstance(button, types.KeyboardButtonUrl):
|
||||||
|
btns.append({'url': button.url, 'text': button.text})
|
||||||
|
reply_btns.append(btns)
|
||||||
|
if message.file and not isinstance(message.media, types.MessageMediaWebPage):
|
||||||
|
file_name = get_file_name(message)
|
||||||
|
human_file_size = get_human_size(message.file.size)
|
||||||
|
media = {
|
||||||
|
'type':message.file.mime_type
|
||||||
|
}
|
||||||
|
if 'video/' in message.file.mime_type:
|
||||||
|
media['video'] : True
|
||||||
|
elif 'audio/' in message.file.mime_type:
|
||||||
|
media['audio'] = True
|
||||||
|
elif 'image/' in message.file.mime_type:
|
||||||
|
media['image'] = True
|
||||||
|
|
||||||
|
if message.text:
|
||||||
|
caption = message.raw_text
|
||||||
|
else:
|
||||||
|
caption = ''
|
||||||
|
caption_html = Markup.escape(caption).__str__().replace('\n', '<br>')
|
||||||
|
return_val = {
|
||||||
|
'found': True,
|
||||||
|
'name': file_name,
|
||||||
|
'file_id': file_id,
|
||||||
|
'human_size': human_file_size,
|
||||||
|
'media': media,
|
||||||
|
'caption_html': caption_html,
|
||||||
|
'title': f"Download | {file_name} | {human_file_size}",
|
||||||
|
'reply_btns': reply_btns,
|
||||||
|
'thumbnail': f"/{alias_id}/{file_id}/thumbnail",
|
||||||
|
'download_url': f"/{alias_id}/{file_id}/download",
|
||||||
|
'page_id': alias_id
|
||||||
|
}
|
||||||
|
elif message.message:
|
||||||
|
text = message.raw_text
|
||||||
|
text_html = Markup.escape(text).__str__().replace('\n', '<br>')
|
||||||
|
return_val = {
|
||||||
|
'found': True,
|
||||||
|
'media': False,
|
||||||
|
'text_html': text_html,
|
||||||
|
'reply_btns': reply_btns,
|
||||||
|
'page_id': alias_id
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return_val = {
|
||||||
|
'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}")
|
||||||
|
return return_val
|
63
app/views/logo_view.py
Normal file
63
app/views/logo_view.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import logging
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
import random
|
||||||
|
import os
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
from telethon.tl import types
|
||||||
|
|
||||||
|
from app.config import logo_folder
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoView:
|
||||||
|
|
||||||
|
async def logo(self, req):
|
||||||
|
alias_id = req.match_info['chat']
|
||||||
|
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
||||||
|
chat_id = chat['chat_id']
|
||||||
|
chat_name = "Image not available"
|
||||||
|
logo_path = logo_folder.joinpath(f"{alias_id}.jpg")
|
||||||
|
if not logo_path.exists():
|
||||||
|
try:
|
||||||
|
photo = await self.client.get_profile_photos(chat_id)
|
||||||
|
except:
|
||||||
|
log.debug(f"Error in getting profile picture in {chat_id}", exc_info=True)
|
||||||
|
photo = None
|
||||||
|
|
||||||
|
if not photo:
|
||||||
|
W, H = (160, 160)
|
||||||
|
color = tuple([random.randint(0, 255) for i in range(3)])
|
||||||
|
im = Image.new("RGB", (W,H), color)
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
w, h = draw.textsize(chat_name)
|
||||||
|
draw.text(((W-w)/2,(H-h)/2), chat_name, fill="white")
|
||||||
|
im.save(logo_path)
|
||||||
|
else:
|
||||||
|
photo = photo[0]
|
||||||
|
pos = -1 if req.query.get('big', None) else int(len(photo.sizes)/2)
|
||||||
|
size = self.client._get_thumb(photo.sizes, pos)
|
||||||
|
if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
||||||
|
await self.client._download_cached_photo_size(size, logo_path)
|
||||||
|
else:
|
||||||
|
media = types.InputPhotoFileLocation(
|
||||||
|
id=photo.id,
|
||||||
|
access_hash=photo.access_hash,
|
||||||
|
file_reference=photo.file_reference,
|
||||||
|
thumb_size=size.type
|
||||||
|
)
|
||||||
|
await self.client.download_file(media, logo_path)
|
||||||
|
|
||||||
|
with open(logo_path, 'rb') as fp:
|
||||||
|
body = fp.read()
|
||||||
|
|
||||||
|
return web.Response(
|
||||||
|
status=200,
|
||||||
|
body=body,
|
||||||
|
headers={
|
||||||
|
"Content-Type": "image/jpeg",
|
||||||
|
"Content-Disposition": 'inline; filename="logo.jpg"'
|
||||||
|
}
|
||||||
|
)
|
71
app/views/thumbnail_view.py
Normal file
71
app/views/thumbnail_view.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import logging
|
||||||
|
from PIL import Image
|
||||||
|
import random
|
||||||
|
import io
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
from telethon.tl import types
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ThumbnailView:
|
||||||
|
|
||||||
|
async def thumbnail_get(self, req):
|
||||||
|
file_id = int(req.match_info["id"])
|
||||||
|
alias_id = req.match_info['chat']
|
||||||
|
chat = [i for i in self.chat_ids if i['alias_id'] == alias_id][0]
|
||||||
|
chat_id = chat['chat_id']
|
||||||
|
try:
|
||||||
|
message = await self.client.get_messages(entity=chat_id, ids=file_id)
|
||||||
|
except:
|
||||||
|
log.debug(f"Error in getting message {file_id} in {chat_id}", exc_info=True)
|
||||||
|
message = None
|
||||||
|
|
||||||
|
if not message or not message.file:
|
||||||
|
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 message.document:
|
||||||
|
media = message.document
|
||||||
|
thumbnails = media.thumbs
|
||||||
|
location = types.InputDocumentFileLocation
|
||||||
|
else:
|
||||||
|
media = message.photo
|
||||||
|
thumbnails = media.sizes
|
||||||
|
location = types.InputPhotoFileLocation
|
||||||
|
|
||||||
|
if not thumbnails:
|
||||||
|
color = tuple([random.randint(0, 255) for i in range(3)])
|
||||||
|
im = Image.new("RGB", (100, 100), color)
|
||||||
|
temp = io.BytesIO()
|
||||||
|
im.save(temp, "PNG")
|
||||||
|
body = temp.getvalue()
|
||||||
|
else:
|
||||||
|
thumb_pos = int(len(thumbnails)/2)
|
||||||
|
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
||||||
|
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!")
|
||||||
|
|
||||||
|
if isinstance(thumbnail, (types.PhotoCachedSize, types.PhotoStrippedSize)):
|
||||||
|
body = self.client._download_cached_photo_size(thumbnail, bytes)
|
||||||
|
else:
|
||||||
|
actual_file = location(
|
||||||
|
id=media.id,
|
||||||
|
access_hash=media.access_hash,
|
||||||
|
file_reference=media.file_reference,
|
||||||
|
thumb_size=thumbnail.type
|
||||||
|
)
|
||||||
|
|
||||||
|
body = self.client.iter_download(actual_file)
|
||||||
|
|
||||||
|
r = web.Response(
|
||||||
|
status=200,
|
||||||
|
body=body,
|
||||||
|
headers={
|
||||||
|
"Content-Type": "image/jpeg",
|
||||||
|
"Content-Disposition": 'inline; filename="thumbnail.jpg"'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return r
|
7
app/views/wildcard_view.py
Normal file
7
app/views/wildcard_view.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
|
||||||
|
class WildcardView:
|
||||||
|
|
||||||
|
async def wildcard(self, req):
|
||||||
|
raise web.HTTPFound('/')
|
Loading…
x
Reference in New Issue
Block a user