regex.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # https://github.com/PaulSonOfLars/tgbot/blob/master/tg_bot/modules/sed.py
  2. import asyncio
  3. import multiprocessing as mp
  4. import re
  5. from pyrogram import filters
  6. from wbb import app
  7. from wbb.utils.filter_groups import regex_group
  8. __MODULE__ = "Sed"
  9. __HELP__ = "**Usage:**\ns/foo/bar"
  10. DELIMITERS = ("/", ":", "|", "_")
  11. REGEX_TIMEOUT_SECONDS = 5
  12. def _regex_sub_worker(
  13. pattern: str,
  14. replacement: str,
  15. source_text: str,
  16. ignore_case: bool,
  17. replace_all: bool,
  18. result_queue,
  19. ):
  20. flags = re.I if ignore_case else 0
  21. count = 0 if replace_all else 1
  22. try:
  23. result = re.sub(pattern, replacement, source_text, count=count, flags=flags)
  24. result_queue.put(("ok", result))
  25. except re.error:
  26. result_queue.put(("regex_error", ""))
  27. def run_regex_with_timeout(
  28. pattern: str,
  29. replacement: str,
  30. source_text: str,
  31. ignore_case: bool,
  32. replace_all: bool,
  33. ) -> str:
  34. result_queue = mp.Queue(maxsize=1)
  35. process = mp.Process(
  36. target=_regex_sub_worker,
  37. args=(
  38. pattern,
  39. replacement,
  40. source_text,
  41. ignore_case,
  42. replace_all,
  43. result_queue,
  44. ),
  45. )
  46. process.start()
  47. process.join(REGEX_TIMEOUT_SECONDS)
  48. if process.is_alive():
  49. process.terminate()
  50. process.join()
  51. raise asyncio.TimeoutError
  52. if result_queue.empty():
  53. return ""
  54. status, result = result_queue.get()
  55. if status == "regex_error":
  56. raise re.error("invalid regex")
  57. return result
  58. @app.on_message(
  59. filters.regex(r"s([{}]).*?\1.*".format("".join(DELIMITERS))),
  60. group=regex_group,
  61. )
  62. async def sed(_, message):
  63. if not message.text:
  64. return
  65. text_content = str(message.text)
  66. sed_result = separate_sed(text_content)
  67. if message.reply_to_message:
  68. if message.reply_to_message.text:
  69. to_fix = message.reply_to_message.text
  70. elif message.reply_to_message.caption:
  71. to_fix = message.reply_to_message.caption
  72. else:
  73. return
  74. if not sed_result:
  75. return
  76. repl, repl_with, flags = sed_result
  77. if not repl:
  78. return await message.reply_text(
  79. "You're trying to replace... nothing with something?"
  80. )
  81. try:
  82. if infinite_checker(repl):
  83. return await message.reply_text("Nice try -_-")
  84. text = await asyncio.to_thread(
  85. run_regex_with_timeout,
  86. repl,
  87. repl_with,
  88. to_fix,
  89. "i" in flags,
  90. "g" in flags,
  91. )
  92. text = text.strip()
  93. except asyncio.TimeoutError:
  94. return await message.reply_text("Regex took too long to compute.")
  95. except re.error:
  96. return
  97. # empty string errors -_-
  98. if len(text) >= 4096:
  99. await message.reply_text(
  100. "The result of the sed command was too long for \
  101. telegram!"
  102. )
  103. elif text:
  104. await message.reply_to_message.reply_text(text)
  105. def infinite_checker(repl):
  106. regex = [
  107. r"\((.{1,}[\+\*]){1,}\)[\+\*].",
  108. r"[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}",
  109. r"\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})",
  110. ]
  111. for match in regex:
  112. if re.search(match, repl):
  113. return True
  114. return False
  115. def separate_sed(sed_string):
  116. if not isinstance(sed_string, str):
  117. sed_string = str(sed_string)
  118. if (
  119. len(sed_string) >= 3
  120. and sed_string[1] in DELIMITERS
  121. and sed_string.count(sed_string[1]) >= 2
  122. ):
  123. delim = sed_string[1]
  124. start = counter = 2
  125. while counter < len(sed_string):
  126. if sed_string[counter] == "\\":
  127. counter += 1
  128. elif sed_string[counter] == delim:
  129. replace = sed_string[start:counter]
  130. counter += 1
  131. start = counter
  132. break
  133. counter += 1
  134. else:
  135. return None
  136. while counter < len(sed_string):
  137. if (
  138. sed_string[counter] == "\\"
  139. and counter + 1 < len(sed_string)
  140. and sed_string[counter + 1] == delim
  141. ):
  142. sed_string = sed_string[:counter] + sed_string[counter + 1 :]
  143. elif sed_string[counter] == delim:
  144. replace_with = sed_string[start:counter]
  145. counter += 1
  146. break
  147. counter += 1
  148. else:
  149. return replace, sed_string[start:], ""
  150. flags = ""
  151. if counter < len(sed_string):
  152. flags = sed_string[counter:]
  153. return replace, replace_with, flags.lower()