utils.py 4.6 KB

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