import logging
from aiohttp import web
import aiohttp_jinja2
from jinja2 import Markup
from telethon.tl import types
from telethon.tl.custom import Message
from .util import get_file_name, get_human_size
from .config import chat_ids, alias_ids
log = logging.getLogger(__name__)
class Views:
def __init__(self, client):
self.client = client
@aiohttp_jinja2.template('home.html')
async def home(self, req):
if len(chat_ids) == 1:
raise web.HTTPFound(f"{alias_ids[0]}")
chats = []
for chat_id, alias_id in zip(chat_ids, alias_ids):
chat = await self.client.get_entity(chat_id)
chats.append({
'id': alias_id,
'name': chat.title
})
return {'chats':chats}
@aiohttp_jinja2.template('index.html')
async def index(self, req):
alias_id = req.rel_url.path.split('/')[1]
chat_id = chat_ids[alias_ids.index(alias_id)]
chat = await self.client.get_entity(chat_id)
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:
if m.file and not isinstance(m.media, types.MessageMediaWebPage):
entry = dict(
file_id=m.id,
media=True,
mime_type=m.file.mime_type,
insight = get_file_name(m)[:55],
date = m.date.isoformat(),
size=get_human_size(m.file.size),
url=req.rel_url.with_path(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[:55],
date = m.date.isoformat(),
size=get_human_size(len(m.raw_text)),
url=req.rel_url.with_path(f"/{alias_id}/{m.id}/view")
)
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': 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': 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': req.rel_url.with_path(f"/{alias_id}/logo")
}
@aiohttp_jinja2.template('info.html')
async def info(self, req):
file_id = int(req.match_info["id"])
alias_id = req.rel_url.path.split('/')[1]
chat_id = chat_ids[alias_ids.index(alias_id)]
message = await self.client.get_messages(entity=chat_id, ids=file_id)
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 = {}
if message.file and not isinstance(message.media, types.MessageMediaWebPage):
file_name = get_file_name(message)
file_size = get_human_size(message.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 = Markup.escape(message.raw_text).__str__().replace('\n', '
')
else:
caption = False
return_val = {
'found': True,
'name': file_name,
'id': file_id,
'size': file_size,
'media': media,
'caption': caption,
'title': f"Download | {file_name} | {file_size}"
}
elif message.message:
text = Markup.escape(message.raw_text).__str__().replace('\n', '
')
return_val = {
'found': True,
'media': False,
'text': text,
}
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 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):
return await self.handle_request(req, thumb=True)
async def thumbnail_head(self, req):
return await self.handle_request(req, head=True, thumb=True)
async def logo(self, req):
alias_id = req.rel_url.path.split('/')[1]
chat_id = chat_ids[alias_ids.index(alias_id)]
photo = await self.client.get_profile_photos(chat_id)
if not photo:
return web.Response(status=404, text="404: Chat has no profile photo")
photo = photo[0]
size = photo.sizes[0]
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,
)
r.enable_chunked_encoding()
return r
async def handle_request(self, req, head=False, thumb=False):
file_id = int(req.match_info["id"])
alias_id = req.rel_url.path.split('/')[1]
chat_id = chat_ids[alias_ids.index(alias_id)]
message = await self.client.get_messages(entity=chat_id, ids=file_id)
if not message or not message.file:
log.info(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 thumb and message.document:
thumbnail = message.document.thumbs
if not thumbnail:
log.info(f"no thumbnail for {file_id} in {chat_id}")
return web.Response(status=404, text="404: Not Found")
thumbnail = thumbnail[-1]
mime_type = 'image/jpeg'
size = thumbnail.size
file_name = f"{file_id}_thumbnail.jpg"
media = types.InputDocumentFileLocation(
id=message.document.id,
access_hash=message.document.access_hash,
file_reference=message.document.file_reference,
thumb_size=thumbnail.type
)
else:
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:
log.info("Range Not Satisfiable", exc_info=True)
return web.Response(
status=416,
text="416: Range Not Satisfiable",
headers = {
"Content-Range": f"bytes */{size}"
}
)
body = None
if not head:
body = self.client.download(media, size, offset, limit)
log.info(f"Serving file {message.id} in {chat_id} ; Range: {offset} - {limit}")
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
)