warp-beacon 2.3.15__py3-none-any.whl → 2.3.17__py3-none-any.whl

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.
@@ -1,2 +1,2 @@
1
- __version__ = "2.3.15"
1
+ __version__ = "2.3.17"
2
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
@@ -90,7 +91,9 @@ class Bot(object):
90
91
  self.client.add_handler(MessageHandler(self.handlers.help, filters.command("help")))
91
92
  self.client.add_handler(MessageHandler(self.handlers.random, filters.command("random")))
92
93
  self.client.add_handler(MessageHandler(self.handlers.handler))
93
- self.client.add_handler(CallbackQueryHandler(self.handlers.simple_button_handler))
94
+ self.client.add_handler(self.handlers.simple_button_handler, filters=filters.create(lambda _, q: not q.data.startswith("readmore:")))
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
@@ -213,6 +213,7 @@ class Handlers(object):
213
213
  if chat.type not in (ChatType.GROUP, ChatType.SUPERGROUP) and not urls:
214
214
  await self.bot.send_text(text=reply_text, reply_id=effective_message_id, chat_id=chat.id)
215
215
 
216
+ #TODO refactor to callback router
216
217
  async def simple_button_handler(self, client: Client, query: CallbackQuery) -> None:
217
218
  await client.answer_callback_query(
218
219
  callback_query_id=query.id,
@@ -220,4 +221,28 @@ class Handlers(object):
220
221
  show_alert=True
221
222
  )
222
223
  self.bot.downloader.auth_event.set()
223
- self.bot.downloader.auth_event.clear()
224
+ self.bot.downloader.auth_event.clear()
225
+
226
+ async def read_more_handler(self, client: Client, query: CallbackQuery) -> None:
227
+ origin, uniq_id = '', ''
228
+ #read_more:{job.job_origin}:{job.uniq_id}
229
+ if query.data:
230
+ parts = query.data.split(':')
231
+ if len(parts) == 3:
232
+ _, origin, uniq_id = parts
233
+ db_results = []
234
+ if uniq_id and origin:
235
+ db_results = self.storage.db_find(uniq_id=uniq_id.strip(), origin=origin.strip())
236
+ first_entity = {}
237
+ if db_results:
238
+ first_entity = db_results[0]
239
+
240
+ try:
241
+ await client.answer_callback_query(
242
+ callback_query_id=query.id,
243
+ show_alert=True,
244
+ text=first_entity.get("canonical_name", "Failed to fetch data.")
245
+ )
246
+ except Exception as e:
247
+ logging.warning("read_more_handler: Failed for uniq_id='%s', origin='%s", uniq_id, origin)
248
+ 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.17
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
 
@@ -4,12 +4,12 @@ var/warp_beacon/accounts.json,sha256=OsXdncs6h88xrF_AP6_WDCK1waGBn9SR-uYdIeK37GM
4
4
  var/warp_beacon/placeholder.gif,sha256=cE5CGJVaop4Sx21zx6j4AyoHU0ncmvQuS2o6hJfEH88,6064
5
5
  var/warp_beacon/proxies.json,sha256=VnjlQDXumOEq72ZFjbh6IqHS1TEHqn8HPYAZqWCeSIA,95
6
6
  warp_beacon/__init__.py,sha256=_rThNODmz0nDp_n4mWo_HKaNFE5jk1_7cRhHyYaencI,163
7
- warp_beacon/__version__.py,sha256=JVaWB6HomdBMOznRLcZrplBgUWb-fYoP0lJ0CYpYETM,24
7
+ warp_beacon/__version__.py,sha256=7kEUywY6HMmkf0hezoDB_dwDQMiBqA4wHs6q4S35P6Y,24
8
8
  warp_beacon/warp_beacon.py,sha256=7KEtZDj-pdhtl6m-zFLsSojs1ZR4o7L0xbqtdmYPvfE,342
9
9
  warp_beacon/compress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  warp_beacon/compress/video.py,sha256=_PDMVYCyzLYxHv1uZmmzGcG_8rjaZr7BTXsXTTy_oS4,2846
11
11
  warp_beacon/jobs/__init__.py,sha256=ED8_tPle4iL4kqNW0apAVkgNQtRRTnYfAJwBjO1g0JY,180
12
- warp_beacon/jobs/abstract.py,sha256=LyM4PzOXXlQxll1_tjpD-zjKpkJHDXl1pC4ziEdrLTM,3043
12
+ warp_beacon/jobs/abstract.py,sha256=lYZplMq4Uo9vUB6ziMsC_FPer2NsvhdmFJ_I54L7v7Y,3087
13
13
  warp_beacon/jobs/download_job.py,sha256=5HiPcnJppFMhO14___3eSkoMygM3y-vhpGkMAuNhK7s,854
14
14
  warp_beacon/jobs/types.py,sha256=Ae8zINgbs7cOcYkYoOCOACA7duyhnIGMQAJ_SJB1QRQ,176
15
15
  warp_beacon/jobs/upload_job.py,sha256=_ul4psPej1jLEs-BMcMR80GbXDSmm38jE9yoZtecclY,741
@@ -19,10 +19,10 @@ warp_beacon/mediainfo/audio.py,sha256=ous88kwQj4bDIChN5wnGil5LqTs0IQHH0d-nyrL0-Z
19
19
  warp_beacon/mediainfo/silencer.py,sha256=qxMuViOoVwUYb60uCVvqHiGrqByR1_4_rqMT-XdMkwc,1813
20
20
  warp_beacon/mediainfo/video.py,sha256=UBZrhTN5IDI-aYu6tsJEILo9nFkjHhkldGVFmvV7tEI,2480
21
21
  warp_beacon/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- warp_beacon/scheduler/scheduler.py,sha256=F9jF6myIHOI0TmC0-rBt2Un8gVvuhBW9zL-nEAEj0Bc,2585
22
+ warp_beacon/scheduler/scheduler.py,sha256=gcV-e-9MjL8JEfu7T3Gy1imUrXz266ub9Ub4O27W5Hw,2786
23
23
  warp_beacon/scraper/__init__.py,sha256=V_C5SmAmRtjFfVBtTotOHCW3ILMQ8m_ulrBF6ry59_A,16944
24
24
  warp_beacon/scraper/abstract.py,sha256=6A6KuBUHZhu8VAyBwLgmnxMPHJcLpgwLapmULy8hpoA,2726
25
- warp_beacon/scraper/account_selector.py,sha256=zYFn9qd5x_BOox5g1iG7QjqaLe_5ezKD_2xFy06fPrk,4731
25
+ warp_beacon/scraper/account_selector.py,sha256=zv5ci_Y7W-tzKeFklffpLts19a1wFg69fqefK-_i_pk,5122
26
26
  warp_beacon/scraper/exceptions.py,sha256=Qkz76yo-X5kucEZIP9tWaK-oYO-kvsPEl8Y0W63oDhU,1300
27
27
  warp_beacon/scraper/fail_handler.py,sha256=_blvckfTZ4xWVancQKVRXH5ClKGwfrBxMwvXIFZh1qA,975
28
28
  warp_beacon/scraper/link_resolver.py,sha256=Rc9ZuMyOo3iPywDHwjngy-WRQ2SXhJwxcg-5ripx7tM,2447
@@ -33,17 +33,18 @@ warp_beacon/scraper/youtube/abstract.py,sha256=cBtExei2Cb3o4YBtHqi8If_FdmE6NyJqN
33
33
  warp_beacon/scraper/youtube/music.py,sha256=qbijpSv54fsrIYHeY-nfmw4vo6oBmedQHsVG8pXNfrc,1380
34
34
  warp_beacon/scraper/youtube/shorts.py,sha256=ujGEV7ILXHqBRa99SyITsnR7ulAHJDtumAh51kVX880,1231
35
35
  warp_beacon/scraper/youtube/youtube.py,sha256=fGrbjBngvvNdpzhb1yZVedNW0_tCrLc31VSYMSHzcQY,2135
36
- warp_beacon/storage/__init__.py,sha256=2uvyIR0APIW6gOxwJRvCji7wS2q6I7dghvLyWsRqRxo,3312
36
+ warp_beacon/storage/__init__.py,sha256=xEzexwWZTMtjVesQ73BLhS0VkWoqRXSUmuXS4h5HOvY,3402
37
37
  warp_beacon/storage/mongo.py,sha256=qC4ZiO8XXvPnP0rJwz4CJx42pqFsyAjCiW10W5QdT6E,527
38
38
  warp_beacon/telegram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- warp_beacon/telegram/bot.py,sha256=WDaUphlF7p6pQkVajIM_F07LvswZXrzOUV5oiAzbb-Y,15625
40
- warp_beacon/telegram/handlers.py,sha256=7gldKrX-CTDQNfD1NQ08cC31uPR-95mhN59OujAEqZg,7704
39
+ warp_beacon/telegram/bot.py,sha256=YgHTCZuPTC04Pkl18A9b6eA7pZBbDeQn_iEBtnRoVxs,16438
40
+ warp_beacon/telegram/caption_shortener.py,sha256=6J4Dp35mOmJEyctiuN8WXM39bkQ13m78HoghsYX3fbU,1548
41
+ warp_beacon/telegram/handlers.py,sha256=P9eR8DzFBSEQVaU0Fp0ABu8qeqFowu0GSU2SdZDfEdc,8511
41
42
  warp_beacon/telegram/placeholder_message.py,sha256=wN9-BRiyrtHG-EvXtZkGJHt2CX71munQ57ITttjt0mw,6400
42
43
  warp_beacon/telegram/utils.py,sha256=9uebX53G16mV7ER7WgfdWBLFHHw14S8HBt9URrIskg0,4440
43
44
  warp_beacon/uploader/__init__.py,sha256=5KRWsxPRGuQ56YhCEnJsXnb-yQp8dpvWEsPDf0dD-fw,4964
44
- warp_beacon-2.3.15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
45
- warp_beacon-2.3.15.dist-info/METADATA,sha256=jpCB5tW5_C8ybCWMY4Z1CgZi_FLS-bNtAMCB7HzTZGs,21704
46
- warp_beacon-2.3.15.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
47
- warp_beacon-2.3.15.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
48
- warp_beacon-2.3.15.dist-info/top_level.txt,sha256=XusFB1mGUStI2gqbtRuJzQrfrAXmdMaT3QTyfvdLEXI,1064
49
- warp_beacon-2.3.15.dist-info/RECORD,,
45
+ warp_beacon-2.3.17.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
46
+ warp_beacon-2.3.17.dist-info/METADATA,sha256=NeHzbOuWLvTkhwo8d4UPZ7j3J4HyEKSYSn_HL5qD3KE,21723
47
+ warp_beacon-2.3.17.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
48
+ warp_beacon-2.3.17.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
49
+ warp_beacon-2.3.17.dist-info/top_level.txt,sha256=2iKFlYwJ-meO9sCX4OGEP1hhQN17t2KFksQ5dXMhXUA,1103
50
+ warp_beacon-2.3.17.dist-info/RECORD,,
@@ -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