mirror of
https://github.com/nexus-stc/hyperboria
synced 2024-12-02 16:02:53 +01:00
51ae1fc5d9
- [nexus] Remove protos GitOrigin-RevId: 1bc308a6c3056b2509952e4b53122f4fd1a9960d
390 lines
13 KiB
Python
390 lines
13 KiB
Python
import datetime
|
||
import math
|
||
import re
|
||
from typing import Optional
|
||
from urllib.parse import quote
|
||
|
||
import numpy as np
|
||
from izihawa_nlptools.utils import (
|
||
despace_full,
|
||
escape_format,
|
||
)
|
||
from izihawa_types.datetime import CustomDatetime
|
||
from library.telegram.common import close_button
|
||
from nexus.translations import t
|
||
from telethon import Button
|
||
|
||
from .common import (
|
||
TooLongQueryError,
|
||
encode_query_to_deep_link,
|
||
)
|
||
|
||
|
||
def highlight_markdown(snippet):
|
||
markdowned = b''
|
||
start_from = 0
|
||
for highlight in snippet.highlights:
|
||
from_ = getattr(highlight, 'from')
|
||
to = highlight.to
|
||
markdowned += escape_format(snippet.fragment[start_from:from_])
|
||
markdowned += b'**'
|
||
markdowned += escape_format(snippet.fragment[from_:to])
|
||
markdowned += b'**'
|
||
start_from = to
|
||
markdowned += escape_format(snippet.fragment[start_from:])
|
||
markdowned = markdowned.decode()
|
||
markdowned = despace_full(re.sub(r'\n+', ' ', markdowned))
|
||
if markdowned[0].islower() or (markdowned[:2] == '**' and markdowned[2].islower()):
|
||
markdowned = '...' + markdowned
|
||
markdowned = markdowned + '...'
|
||
markdowned = f'__{markdowned}__'
|
||
return markdowned
|
||
|
||
|
||
class TextPart:
|
||
def __init__(self, part):
|
||
self.part = str(part)
|
||
self._bold = False
|
||
self._italic = False
|
||
self._brackets = False
|
||
self._escaped = False
|
||
self._clickable = False
|
||
self._unbreakable = False
|
||
|
||
def escaped(self):
|
||
self._escaped = True
|
||
return self
|
||
|
||
def bold(self):
|
||
self._bold = True
|
||
return self
|
||
|
||
def italic(self):
|
||
self._italic = True
|
||
return self
|
||
|
||
def within_brackets(self):
|
||
self._brackets = True
|
||
return self
|
||
|
||
def clickable(self):
|
||
self._clickable = True
|
||
return self
|
||
|
||
def unbreakable(self):
|
||
self._unbreakable = True
|
||
return self
|
||
|
||
def limits(self, limit, with_dots: bool = False):
|
||
if len(self.part) > limit:
|
||
if self._unbreakable:
|
||
self.part = ''
|
||
else:
|
||
self.part = self.part[:limit]
|
||
if with_dots:
|
||
self.part += '...'
|
||
|
||
def __add__(self, other):
|
||
self.part += str(other)
|
||
|
||
def __len__(self):
|
||
return len(self.part)
|
||
|
||
def __str__(self):
|
||
r = self.part
|
||
if not self._escaped:
|
||
r = escape_format(r)
|
||
if self._clickable:
|
||
r = f'`{r}`'
|
||
if self._brackets:
|
||
r = f'({r})'
|
||
if self._bold:
|
||
r = f'**{r}**'
|
||
if self._italic:
|
||
r = f'__{r}__'
|
||
return r
|
||
|
||
|
||
class BaseViewBuilder:
|
||
icon = ''
|
||
|
||
def __init__(self, document_holder, user_language):
|
||
self.document_holder = document_holder
|
||
self.user_language = user_language
|
||
self.last_limit_part = None
|
||
self.last_limit = None
|
||
self.parts = []
|
||
self.has_cover = False
|
||
|
||
def has_field(self, name):
|
||
return self.document_holder.has_field(name)
|
||
|
||
def add(self, el, bold=False, italic=False, escaped=False, clickable=False, lower=False, with_brackets=False, unbreakable=False,
|
||
eol: Optional[str] = ' '):
|
||
if el:
|
||
if not isinstance(el, TextPart):
|
||
el = TextPart(el if not lower else el.lower())
|
||
if bold:
|
||
el.bold()
|
||
if italic:
|
||
el.italic()
|
||
if escaped:
|
||
el.escaped()
|
||
if clickable:
|
||
el.clickable()
|
||
if with_brackets:
|
||
el.within_brackets()
|
||
if unbreakable:
|
||
el.unbreakable()
|
||
self.parts.append(el)
|
||
if eol is not None:
|
||
self.parts.append(TextPart(eol))
|
||
return self
|
||
|
||
def limits(self, limit=None, with_dots: bool = False):
|
||
if limit:
|
||
current_length = 0
|
||
for i, part in enumerate(self.parts):
|
||
current_length += len(part)
|
||
if current_length > limit:
|
||
part.limits(limit + len(part) - current_length, with_dots=with_dots)
|
||
if len(part) > 0:
|
||
self.parts = self.parts[:i + 1]
|
||
else:
|
||
self.parts = self.parts[:i]
|
||
return self
|
||
return self
|
||
|
||
def limited(self, limit):
|
||
self.last_limit_part = len(self.parts)
|
||
self.last_limit = limit
|
||
return self
|
||
|
||
def end_limited(self, with_dots: bool = False):
|
||
if self.last_limit is not None:
|
||
current_length = 0
|
||
for i, part in enumerate(self.parts[self.last_limit_part:]):
|
||
current_length += len(part)
|
||
if current_length > self.last_limit:
|
||
part.limits(current_length - self.last_limit, with_dots=with_dots)
|
||
if len(part) > 0:
|
||
self.parts = self.parts[:self.last_limit_part + i + 1]
|
||
else:
|
||
self.parts = self.parts[:self.last_limit_part + i]
|
||
return self
|
||
self.last_limit = None
|
||
self.last_limit_part = None
|
||
return self
|
||
|
||
def add_icon(self):
|
||
return self.add(self.icon)
|
||
|
||
def add_label(self, label_name, bold=True, lower=False):
|
||
return self.add(t(label_name, self.user_language) + ':', bold=bold, lower=lower)
|
||
|
||
def add_new_line(self, n: int = 1):
|
||
return self.add('\n' * n, eol=None)
|
||
|
||
def add_formatted_datetime(self):
|
||
if self.has_field('issued_at') and self.document_holder.issued_at != -62135596800:
|
||
dt = CustomDatetime(dt=np.datetime64(self.document_holder.issued_at, 's'))
|
||
try:
|
||
ct = datetime.date(dt.year, dt.month, 1)
|
||
if datetime.date.today() - datetime.timedelta(days=365) < ct:
|
||
self.add(TextPart(f'{dt.year}.{dt.month:02d}').within_brackets())
|
||
else:
|
||
self.add(TextPart(str(dt.year)).within_brackets())
|
||
except ValueError:
|
||
pass
|
||
return self
|
||
|
||
def add_downloads_count(self):
|
||
return self.add(f'{math.log1p(self.document_holder.downloads_count):.1f}')
|
||
|
||
def add_links(self, bot_name):
|
||
self.add_label("LINKS")
|
||
self.add(" - ".join(self.document_holder.generate_links(bot_name)), escaped=True)
|
||
return self
|
||
|
||
def add_tags(self, bot_name):
|
||
tag_links = self.document_holder.generate_tags_links(bot_name)
|
||
if not tag_links:
|
||
return self
|
||
self.add(tag_links[0], escaped=True, unbreakable=True)
|
||
for tag_link in tag_links[1:]:
|
||
self.add('- ' + tag_link, escaped=True, unbreakable=True)
|
||
return self
|
||
|
||
def add_authors(self, et_al=True, first_n_authors=1, on_newline=True):
|
||
et_al_suffix = (' et al' if et_al else '')
|
||
if self.document_holder.authors:
|
||
if on_newline:
|
||
self.add_new_line()
|
||
if len(self.document_holder.authors) > first_n_authors:
|
||
self.add('; '.join(self.document_holder.authors[:first_n_authors]) + et_al_suffix)
|
||
elif len(self.document_holder.authors) == 1:
|
||
if self.document_holder.authors[0].count(';') >= 1:
|
||
comma_authors = list(map(str.strip, self.document_holder.authors[0].split(';')))
|
||
if len(comma_authors) > first_n_authors:
|
||
self.add('; '.join(comma_authors[:first_n_authors]) + et_al_suffix)
|
||
else:
|
||
self.add('; '.join(comma_authors))
|
||
elif self.document_holder.authors[0].count(',') >= 1:
|
||
comma_authors = list(map(str.strip, self.document_holder.authors[0].split(',')))
|
||
if len(comma_authors) > first_n_authors:
|
||
self.add('; '.join(comma_authors[:first_n_authors]) + et_al_suffix)
|
||
else:
|
||
self.add('; '.join(comma_authors))
|
||
else:
|
||
self.add(self.document_holder.authors[0])
|
||
else:
|
||
self.add('; '.join(self.document_holder.authors))
|
||
return self
|
||
|
||
def add_doi(self, clickable=True, with_brackets=False, with_leading_pipe=False):
|
||
if self.document_holder.doi:
|
||
if with_leading_pipe:
|
||
self.add('|')
|
||
return self.add(self.document_holder.doi, clickable=clickable, with_brackets=with_brackets)
|
||
|
||
def add_doi_link(self, with_leading_pipe=False, on_newline=False, label=False, text=None, end_newline=False):
|
||
if self.document_holder.doi:
|
||
if on_newline:
|
||
self.add_new_line()
|
||
if with_leading_pipe:
|
||
self.add('|')
|
||
if label:
|
||
self.add('DOI:', bold=True)
|
||
escaped_doi = escape_format(self.document_holder.doi)
|
||
if text is None:
|
||
text = escaped_doi
|
||
self.add(f'[{text}](https://doi.org/{quote(escaped_doi)})', escaped=True)
|
||
if end_newline:
|
||
self.add_new_line()
|
||
return self
|
||
|
||
def add_references_counter(self, bot_name, with_leading_pipe=False):
|
||
if self.has_field('referenced_by_count') and self.document_holder.referenced_by_count:
|
||
if with_leading_pipe:
|
||
self.add('|')
|
||
text = f'🔗 {self.document_holder.referenced_by_count}'
|
||
if self.document_holder.doi:
|
||
try:
|
||
link = encode_query_to_deep_link(f'refs:{self.document_holder.doi}', bot_name=bot_name)
|
||
text = f'[{text}]({link})'
|
||
except TooLongQueryError:
|
||
pass
|
||
self.add(text, escaped=True)
|
||
return self
|
||
|
||
def add_short_description(self):
|
||
return (
|
||
self.add_icon()
|
||
.add_title()
|
||
.limits(250, with_dots=True)
|
||
.add_locator()
|
||
)
|
||
|
||
def add_view(self, bot_name):
|
||
return (
|
||
self.add_cover_url()
|
||
.add_icon()
|
||
.add_title()
|
||
.limits(400, with_dots=True)
|
||
.add_locator(first_n_authors=5)
|
||
.add_new_line(2)
|
||
.add_stats()
|
||
.add_links(bot_name=bot_name)
|
||
.add_new_line(2)
|
||
.add_description()
|
||
.limits(1500, with_dots=True)
|
||
.add_new_line(2)
|
||
.add_tags(bot_name=bot_name)
|
||
.limits(3000)
|
||
)
|
||
|
||
def add_title(self):
|
||
raise NotImplementedError()
|
||
|
||
def add_description(self):
|
||
raise NotImplementedError()
|
||
|
||
def add_locator(self, first_n_authors=1, markup=True):
|
||
raise NotImplementedError()
|
||
|
||
def add_filedata(self, with_leading_pipe=False):
|
||
raise NotImplementedError()
|
||
|
||
def add_stats(self, end_newline=True):
|
||
return self
|
||
|
||
def add_cover_url(self):
|
||
cover_url = self.document_holder.get_cover_url()
|
||
if cover_url:
|
||
self.has_cover = True
|
||
self.add(f'[]({cover_url})', escaped=True, unbreakable=True)
|
||
return self
|
||
|
||
def add_isbns(self, on_newline=False, label=False, end_newline=False):
|
||
if self.document_holder.isbns:
|
||
if on_newline:
|
||
self.add_new_line()
|
||
if label:
|
||
self.add('ISBN:', bold=True)
|
||
self.add(', '.join(self.document_holder.isbns[:2]))
|
||
if end_newline:
|
||
self.add_new_line()
|
||
return self
|
||
|
||
def build(self):
|
||
text = ''.join(map(str, self.parts)).strip()
|
||
text = re.sub('\n\n+', '\n\n', text)
|
||
return text
|
||
|
||
|
||
class BaseButtonsBuilder:
|
||
def __init__(self, document_holder, user_language):
|
||
self.document_holder = document_holder
|
||
self.user_language = user_language
|
||
self.buttons = [[]]
|
||
|
||
def add_back_button(self, back_command):
|
||
self.buttons[-1].append(
|
||
Button.inline(
|
||
text='⬅️',
|
||
data=back_command
|
||
)
|
||
)
|
||
return self
|
||
|
||
def add_download_button(self, session_id, position: int = 0):
|
||
# ⬇️ is a mark, Find+F over sources before replacing
|
||
self.buttons[-1].append(
|
||
Button.inline(
|
||
text=f'⬇️ GET',
|
||
data=self.document_holder.get_download_command(session_id=session_id, position=position),
|
||
)
|
||
)
|
||
return self
|
||
|
||
def add_remote_download_button(self, bot_name):
|
||
# ⬇️ is a mark, Find+F over sources before replacing
|
||
self.buttons[-1].append(
|
||
Button.url('⬇', self.document_holder.get_deep_id_link(bot_name))
|
||
)
|
||
return self
|
||
|
||
def add_close_button(self, session_id):
|
||
self.buttons[-1].append(close_button(session_id))
|
||
return self
|
||
|
||
def add_new_line(self):
|
||
self.buttons.append([])
|
||
return self
|
||
|
||
def add_default_layout(self, bot_name, session_id, position: int = 0):
|
||
raise NotImplementedError()
|
||
|
||
def build(self):
|
||
return self.buttons
|