utils.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. from uuid import uuid4
  2. from enum import IntEnum
  3. from datetime import datetime
  4. from collections import namedtuple
  5. from telethon.utils import get_display_name
  6. from telethon.tl.types import (
  7. MessageEntityBold,
  8. MessageEntityItalic,
  9. MessageEntityStrike,
  10. MessageEntityCode,
  11. MessageEntityPre,
  12. MessageEntitySpoiler,
  13. MessageEntityUnderline
  14. )
  15. Command = namedtuple(
  16. 'Command',
  17. 'name argc args args_string'
  18. )
  19. Age = namedtuple(
  20. 'Age',
  21. 'days_until age age_now date_string'
  22. )
  23. class Kind(IntEnum):
  24. CANNOT_APPLY_TO_SELF = 0
  25. CAN_APPLY_TO_SELF = 1
  26. NO_TARGET = 2
  27. class WordKind(IntEnum):
  28. FIRST = 0 # «год» / «день».
  29. SECOND = 1 # «лет» / «дней».
  30. THIRD = 2 # «года» / «дня».
  31. WORDS_TABLE = {
  32. 'год': ('год', 'лет', 'года'),
  33. 'день': ('день', 'дней', 'дня')
  34. }
  35. def parse_command(text):
  36. text = text.strip()
  37. if not text.startswith('/'):
  38. raise ValueError
  39. text = text.split(' ')
  40. if len(text) < 1:
  41. raise ValueError
  42. command = text[0][1:].lower()
  43. if '@' in command:
  44. command = command.split('@')
  45. command = command[0]
  46. args = text[1:]
  47. argc = len(args)
  48. args_string = ' '.join(args)
  49. return Command(
  50. name=command,
  51. argc=argc,
  52. args=args,
  53. args_string=args_string
  54. )
  55. def get_user_name(user):
  56. full_name = get_display_name(user)
  57. if not full_name:
  58. full_name = user.username
  59. if not full_name:
  60. full_name = '?'
  61. return full_name
  62. def get_link_to_user(user):
  63. full_name = get_user_name(user)
  64. if user.username:
  65. return f'[{full_name}](@{user.username})'
  66. return f'[{full_name}](tg://user?id={user.id})'
  67. def is_valid_name(name):
  68. return name.isidentifier()
  69. def make_temporary_filename(ext):
  70. uid = uuid4().hex
  71. return f'tmp_{uid}.{ext}'
  72. def parse_kind(kind):
  73. kind = int(kind)
  74. if kind < 0 or kind > 2:
  75. raise ValueError
  76. return kind
  77. # Bruh?
  78. def get_word_kind(number):
  79. if number == 0 or 5 <= number <= 20:
  80. return WordKind.SECOND
  81. last_digit = number % 10
  82. if last_digit == 0:
  83. return WordKind.SECOND
  84. if last_digit == 1:
  85. return WordKind.FIRST
  86. if 5 <= last_digit <= 9:
  87. return WordKind.SECOND
  88. return WordKind.THIRD
  89. def get_word_for(word, number):
  90. return f'{number} {WORDS_TABLE[word][get_word_kind(number)]}'
  91. def calculate_age(date):
  92. now = datetime.now().date()
  93. birthday_date = date.replace(year=now.year)
  94. if now > birthday_date:
  95. birthday_date = date.replace(year=now.year + 1)
  96. delta = birthday_date - now
  97. age = (birthday_date - date).days // 365
  98. age_now = age - 1
  99. return Age(
  100. days_until=delta.days,
  101. age=age,
  102. age_now=age_now,
  103. date_string=date.strftime('%d.%m.%Y')
  104. )
  105. DELIMITERS = {
  106. MessageEntityBold: '**',
  107. MessageEntityItalic: '__',
  108. MessageEntityStrike: '~~',
  109. MessageEntityCode: '`',
  110. MessageEntityPre: '`',
  111. MessageEntityUnderline: '\ue000'
  112. }
  113. class LookupTable:
  114. def __init__(self):
  115. self._start = {}
  116. self._end = {}
  117. self._spoiler = []
  118. def insert(self, entity):
  119. if type(entity) is MessageEntitySpoiler:
  120. self._spoiler.extend(range(entity.offset, entity.offset + entity.length))
  121. return
  122. delimiter = DELIMITERS.get(type(entity))
  123. if not delimiter:
  124. return
  125. start = entity.offset
  126. end = entity.offset + entity.length
  127. if start in self._start:
  128. self._start[start].append(delimiter)
  129. else:
  130. self._start[start] = [delimiter]
  131. if end in self._end:
  132. self._end[end].insert(0, delimiter)
  133. else:
  134. self._end[end] = [delimiter]
  135. def _lookup(self, position):
  136. if position in self._start:
  137. return ''.join(self._start[position])
  138. elif position in self._end:
  139. return ''.join(self._end[position])
  140. def process(self, text):
  141. text = list(text) + ['']
  142. result = ''
  143. for position, character in zip(range(len(text)), text):
  144. delimiter = self._lookup(position)
  145. if delimiter:
  146. result += delimiter
  147. if position in self._spoiler:
  148. result += '█'
  149. else:
  150. result += character
  151. return result
  152. def unparse(text, entities):
  153. table = LookupTable()
  154. for entity in entities:
  155. table.insert(entity)
  156. return table.process(text)