warp-beacon 2.3.15__tar.gz → 2.3.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {warp_beacon-2.3.15/warp_beacon.egg-info → warp_beacon-2.3.16}/PKG-INFO +2 -1
  2. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/pyproject.toml +2 -1
  3. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/setup.py +1 -0
  4. warp_beacon-2.3.16/warp_beacon/__version__.py +2 -0
  5. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/jobs/abstract.py +2 -0
  6. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scheduler/scheduler.py +7 -0
  7. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/account_selector.py +10 -3
  8. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/storage/__init__.py +5 -2
  9. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/telegram/bot.py +20 -4
  10. warp_beacon-2.3.16/warp_beacon/telegram/caption_shortener.py +66 -0
  11. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/telegram/handlers.py +25 -1
  12. {warp_beacon-2.3.15 → warp_beacon-2.3.16/warp_beacon.egg-info}/PKG-INFO +2 -1
  13. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon.egg-info/SOURCES.txt +1 -0
  14. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon.egg-info/requires.txt +1 -0
  15. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon.egg-info/top_level.txt +1 -0
  16. warp_beacon-2.3.15/warp_beacon/__version__.py +0 -2
  17. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/LICENSE +0 -0
  18. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/MANIFEST.in +0 -0
  19. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/README.md +0 -0
  20. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/assets/placeholder.gif +0 -0
  21. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/etc/.gitignore +0 -0
  22. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/etc/accounts.json +0 -0
  23. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/etc/proxies.json +0 -0
  24. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/etc/warp_beacon.conf +0 -0
  25. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/etc/warp_beacon.service +0 -0
  26. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/setup.cfg +0 -0
  27. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/__init__.py +0 -0
  28. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/compress/__init__.py +0 -0
  29. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/compress/video.py +0 -0
  30. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/jobs/__init__.py +0 -0
  31. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/jobs/download_job.py +0 -0
  32. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/jobs/types.py +0 -0
  33. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/jobs/upload_job.py +0 -0
  34. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/mediainfo/__init__.py +0 -0
  35. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/mediainfo/abstract.py +0 -0
  36. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/mediainfo/audio.py +0 -0
  37. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/mediainfo/silencer.py +0 -0
  38. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/mediainfo/video.py +0 -0
  39. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scheduler/__init__.py +0 -0
  40. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/__init__.py +0 -0
  41. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/abstract.py +0 -0
  42. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/exceptions.py +0 -0
  43. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/fail_handler.py +0 -0
  44. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/instagram/__init__.py +0 -0
  45. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/instagram/instagram.py +0 -0
  46. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/link_resolver.py +0 -0
  47. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/youtube/__init__.py +0 -0
  48. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/youtube/abstract.py +0 -0
  49. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/youtube/music.py +0 -0
  50. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/youtube/shorts.py +0 -0
  51. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/scraper/youtube/youtube.py +0 -0
  52. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/storage/mongo.py +0 -0
  53. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/telegram/__init__.py +0 -0
  54. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/telegram/placeholder_message.py +0 -0
  55. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/telegram/utils.py +0 -0
  56. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/uploader/__init__.py +0 -0
  57. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon/warp_beacon.py +0 -0
  58. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon.egg-info/dependency_links.txt +0 -0
  59. {warp_beacon-2.3.15 → warp_beacon-2.3.16}/warp_beacon.egg-info/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: warp_beacon
3
- Version: 2.3.15
3
+ Version: 2.3.16
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -236,6 +236,7 @@ Requires-Dist: urlextract
236
236
  Requires-Dist: pillow
237
237
  Requires-Dist: pymongo
238
238
  Requires-Dist: instagrapi==2.0.0
239
+ Requires-Dist: bs4
239
240
  Dynamic: author
240
241
  Dynamic: home-page
241
242
 
@@ -24,7 +24,8 @@ dependencies = [
24
24
  "urlextract",
25
25
  "pillow",
26
26
  "pymongo",
27
- "instagrapi == 2.0.0"
27
+ "instagrapi == 2.0.0",
28
+ "bs4"
28
29
  ]
29
30
  keywords = ["bot", "telegram", "instagram", "export"]
30
31
  classifiers = [
@@ -64,6 +64,7 @@ setup(
64
64
  "warp_beacon/telegram/placeholder_message",
65
65
  "warp_beacon/telegram/handlers",
66
66
  "warp_beacon/telegram/utils",
67
+ "warp_beacon/telegram/caption_shortener",
67
68
  "warp_beacon/jobs/abstract",
68
69
  "warp_beacon/jobs/download_job",
69
70
  "warp_beacon/jobs/upload_job",
@@ -0,0 +1,2 @@
1
+ __version__ = "2.3.16"
2
+
@@ -44,6 +44,7 @@ class JobSettings(TypedDict):
44
44
  job_postponed_until: int
45
45
  message_leftover: str
46
46
  replay: bool
47
+ short_text: bool
47
48
 
48
49
  class AbstractJob(ABC):
49
50
  job_id: uuid.UUID = None
@@ -80,6 +81,7 @@ class AbstractJob(ABC):
80
81
  job_postponed_until: int = -1
81
82
  message_leftover: str = ""
82
83
  replay: bool = False
84
+ short_text: bool = False
83
85
 
84
86
  def __init__(self, **kwargs: Unpack[JobSettings]) -> None:
85
87
  if kwargs:
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import time
3
3
  from random import randrange
4
+ import datetime
4
5
  import threading
5
6
  import json
6
7
 
@@ -81,6 +82,12 @@ class IGScheduler(object):
81
82
  self.load_state()
82
83
  while self.running:
83
84
  try:
85
+ now = datetime.datetime.now()
86
+ if 4 <= now.hour < 7:
87
+ logging.info("Scheduler is paused due to night hours (4:00 - 7:00)")
88
+ self.event.wait(timeout=10800)
89
+ continue
90
+
84
91
  if self.state["remaining"] <= 0:
85
92
  self.state["remaining"] = randrange(8400, 26200)
86
93
  logging.info("Next scheduler activity in '%s' seconds", self.state["remaining"])
@@ -56,8 +56,15 @@ class AccountSelector(object):
56
56
  logging.info("Account proxy matched '%s'", proxy)
57
57
  matched_proxy.append(proxy)
58
58
  if matched_proxy:
59
- random.seed(random.seed(time.time_ns() ^ int.from_bytes(os.urandom(len(matched_proxy)), "big")))
59
+ if len(matched_proxy) > 1:
60
+ random.seed(random.seed(time.time_ns() ^ int.from_bytes(os.urandom(len(matched_proxy)), "big")))
61
+ # ensure new proxy in case if previous account required captcha
62
+ last_proxy = self.accounts.get("last_proxy", None)
63
+ if last_proxy and last_proxy in matched_proxy:
64
+ matched_proxy.remove(last_proxy)
60
65
  prox_choice = random.choice(matched_proxy)
66
+ # saving chosen proxy for history
67
+ self.accounts["last_proxy"] = prox_choice
61
68
  logging.info("Chosen proxy: '%s'", prox_choice)
62
69
  return prox_choice
63
70
  except Exception as e:
@@ -103,8 +110,8 @@ class AccountSelector(object):
103
110
  def bump_acc_fail(self, key: str, amount: int = 1) -> int:
104
111
  try:
105
112
  idx = self.account_index[self.current_module_name].value
106
- self.accounts_meta_data[idx][key] += amount
107
- return self.accounts_meta_data[idx][key]
113
+ self.accounts_meta_data[self.current_module_name][idx][key] += amount
114
+ return self.accounts_meta_data[self.current_module_name][idx][key]
108
115
  except Exception as e:
109
116
  logging.warning("Failed to record fail stats")
110
117
  logging.exception(e)
@@ -52,12 +52,15 @@ class Storage(object):
52
52
  path = urlparse(url).path.strip('/')
53
53
  return path
54
54
 
55
- def db_find(self, uniq_id: str) -> list[dict]:
55
+ def db_find(self, uniq_id: str, origin: str = "") -> list[dict]:
56
56
  document = None
57
57
  ret = []
58
58
  try:
59
59
  logging.debug("uniq_id to search is '%s'", uniq_id)
60
- cursor = self.db.find({"uniq_id": uniq_id})
60
+ find_opts = {"uniq_id": uniq_id}
61
+ if origin:
62
+ find_opts["origin"] = origin
63
+ cursor = self.db.find(find_opts)
61
64
  for document in cursor:
62
65
  ret.append(
63
66
  {
@@ -21,6 +21,7 @@ from warp_beacon.uploader import AsyncUploader
21
21
  from warp_beacon.jobs.upload_job import UploadJob
22
22
  from warp_beacon.jobs.types import JobType
23
23
  from warp_beacon.telegram.utils import Utils
24
+ from warp_beacon.telegram.caption_shortener import CaptionShortner
24
25
  from warp_beacon.scheduler.scheduler import IGScheduler
25
26
 
26
27
  import logging
@@ -91,6 +92,8 @@ class Bot(object):
91
92
  self.client.add_handler(MessageHandler(self.handlers.random, filters.command("random")))
92
93
  self.client.add_handler(MessageHandler(self.handlers.handler))
93
94
  self.client.add_handler(CallbackQueryHandler(self.handlers.simple_button_handler))
95
+ self.client.add_handler(CallbackQueryHandler(self.handlers.read_more_handler, filters=filters.create(lambda _, q: q.data.startswith("readmore:"))))
96
+
94
97
 
95
98
  self.placeholder = PlaceholderMessage(self)
96
99
 
@@ -158,9 +161,14 @@ class Bot(object):
158
161
 
159
162
  def build_signature_caption(self, job: UploadJob) -> str:
160
163
  caption = ""
164
+ is_group = job.chat_type in (ChatType.GROUP, ChatType.SUPERGROUP)
161
165
  if job.canonical_name:
162
- caption = f"<b>{html.escape(job.canonical_name)}</b>"
163
- if job.chat_type in (ChatType.GROUP, ChatType.SUPERGROUP):
166
+ if is_group and CaptionShortner.need_short(job.canonical_name):
167
+ caption = f"{html.escape(CaptionShortner.smart_truncate_html(job.canonical_name))} ..."
168
+ job.short_text = True
169
+ else:
170
+ caption = f"<b>{html.escape(job.canonical_name)}</b>"
171
+ if is_group:
164
172
  if job.canonical_name:
165
173
  caption += "\n—\n"
166
174
  if job.message_leftover:
@@ -336,8 +344,16 @@ class Bot(object):
336
344
  args["disable_notification"] = True
337
345
  args["reply_to_message_id"] = job.message_id
338
346
 
339
- if os.environ.get("ENABLE_DONATES", None) == "true" and job.media_type is not JobType.COLLECTION:
340
- args["reply_markup"] = InlineKeyboardMarkup([[InlineKeyboardButton("❤ Donate", url=os.environ.get("DONATE_LINK", "https://pay.cryptocloud.plus/pos/W5BMtNQt5bJFoW2E"))]])
347
+ if job.media_type is not JobType.COLLECTION:
348
+ render_donates = os.environ.get("ENABLE_DONATES", None) == "true"
349
+ keyboard_buttons = [[]]
350
+ if job.short_text:
351
+ keyboard_buttons[0].append(InlineKeyboardButton("📖 Read more", callback_data=f"read_more:{job.job_origin}:{job.uniq_id}"))
352
+ if render_donates:
353
+ keyboard_buttons[0].append(InlineKeyboardButton("❤ Donate", url=os.environ.get("DONATE_LINK", "https://pay.cryptocloud.plus/pos/W5BMtNQt5bJFoW2E")))
354
+
355
+ if keyboard_buttons[0]: #job.short_text or render_donates:
356
+ args["reply_markup"] = InlineKeyboardMarkup(keyboard_buttons)
341
357
 
342
358
  return args
343
359
 
@@ -0,0 +1,66 @@
1
+ import logging
2
+ from typing import Union
3
+ from bs4 import NavigableString, Tag, Comment
4
+ from bs4 import BeautifulSoup
5
+
6
+ CAPTION_LENGTH_LIMIT = 85
7
+
8
+ class CaptionShortner(object):
9
+ @staticmethod
10
+ def strip_html(text: str) -> str:
11
+ try:
12
+ soup = BeautifulSoup(text, "html.parser")
13
+ return soup.get_text()
14
+ except Exception as e:
15
+ logging.warning("Failed to stript HTML tags!")
16
+ logging.exception(e)
17
+
18
+ return text
19
+
20
+ @staticmethod
21
+ def need_short(text: str) -> bool:
22
+ wo_html = CaptionShortner.strip_html(text)
23
+ if len(wo_html) > CAPTION_LENGTH_LIMIT:
24
+ return True
25
+ return False
26
+
27
+ @staticmethod
28
+ def smart_truncate_html(html: str, limit: int = CAPTION_LENGTH_LIMIT) -> str:
29
+ result = ""
30
+ try:
31
+ soup = BeautifulSoup(html, "html.parser")
32
+ length = 0
33
+
34
+ def walk(node: Union[NavigableString, Tag, Comment]) -> None:
35
+ nonlocal result, length
36
+ if length >= limit:
37
+ return
38
+
39
+ if isinstance(node, str):
40
+ words = node.split()
41
+ for word in words:
42
+ if length + len(word) + 1 > limit:
43
+ return
44
+ if result and not result.endswith(" "):
45
+ result += " "
46
+ length += 1
47
+ result += word
48
+ length += len(word)
49
+ elif isinstance(node, Tag):
50
+ if node.name == '[document]':
51
+ for child in node.children:
52
+ walk(child)
53
+ return
54
+
55
+ for child in node.children:
56
+ walk(child)
57
+ if length >= limit:
58
+ break
59
+
60
+ result += f"</{node.name}>"
61
+
62
+ walk(soup)
63
+ except Exception as e:
64
+ logging.warning("Fail in smart_truncate_html!")
65
+ logging.exception(e)
66
+ return result
@@ -220,4 +220,28 @@ class Handlers(object):
220
220
  show_alert=True
221
221
  )
222
222
  self.bot.downloader.auth_event.set()
223
- self.bot.downloader.auth_event.clear()
223
+ self.bot.downloader.auth_event.clear()
224
+
225
+ async def read_more_handler(self, client: Client, query: CallbackQuery) -> None:
226
+ origin, uniq_id = '', ''
227
+ #read_more:{job.job_origin}:{job.uniq_id}
228
+ if query.data:
229
+ parts = query.data.split(':')
230
+ if len(parts) == 3:
231
+ _, origin, uniq_id = parts
232
+ db_results = []
233
+ if uniq_id and origin:
234
+ db_results = self.storage.db_find(uniq_id=uniq_id.strip(), origin=origin.strip())
235
+ first_entity = {}
236
+ if db_results:
237
+ first_entity = db_results[0]
238
+
239
+ try:
240
+ await client.answer_callback_query(
241
+ callback_query_id=query.id,
242
+ show_alert=True,
243
+ text=first_entity.get("canonical_name", "Failed to fetch data.")
244
+ )
245
+ except Exception as e:
246
+ logging.warning("read_more_handler: Failed for uniq_id='%s', origin='%s", uniq_id, origin)
247
+ logging.exception(e)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: warp_beacon
3
- Version: 2.3.15
3
+ Version: 2.3.16
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -236,6 +236,7 @@ Requires-Dist: urlextract
236
236
  Requires-Dist: pillow
237
237
  Requires-Dist: pymongo
238
238
  Requires-Dist: instagrapi==2.0.0
239
+ Requires-Dist: bs4
239
240
  Dynamic: author
240
241
  Dynamic: home-page
241
242
 
@@ -50,6 +50,7 @@ warp_beacon/storage/__init__.py
50
50
  warp_beacon/storage/mongo.py
51
51
  warp_beacon/telegram/__init__.py
52
52
  warp_beacon/telegram/bot.py
53
+ warp_beacon/telegram/caption_shortener.py
53
54
  warp_beacon/telegram/handlers.py
54
55
  warp_beacon/telegram/placeholder_message.py
55
56
  warp_beacon/telegram/utils.py
@@ -10,3 +10,4 @@ urlextract
10
10
  pillow
11
11
  pymongo
12
12
  instagrapi==2.0.0
13
+ bs4
@@ -31,6 +31,7 @@ warp_beacon/storage
31
31
  warp_beacon/storage/mongo
32
32
  warp_beacon/telegram
33
33
  warp_beacon/telegram/bot
34
+ warp_beacon/telegram/caption_shortener
34
35
  warp_beacon/telegram/handlers
35
36
  warp_beacon/telegram/placeholder_message
36
37
  warp_beacon/telegram/utils
@@ -1,2 +0,0 @@
1
- __version__ = "2.3.15"
2
-
File without changes
File without changes
File without changes
File without changes