CliRemote 1.7.14__py3-none-any.whl → 1.8.3__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.
- {cliremote-1.7.14.dist-info → cliremote-1.8.3.dist-info}/METADATA +1 -1
- {cliremote-1.7.14.dist-info → cliremote-1.8.3.dist-info}/RECORD +7 -7
- remote/precise_engine.py +7 -33
- remote/spammer.py +5 -32
- {cliremote-1.7.14.dist-info → cliremote-1.8.3.dist-info}/WHEEL +0 -0
- {cliremote-1.7.14.dist-info → cliremote-1.8.3.dist-info}/licenses/LICENSE +0 -0
- {cliremote-1.7.14.dist-info → cliremote-1.8.3.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
cliremote-1.
|
1
|
+
cliremote-1.8.3.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
|
2
2
|
remote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
remote/account_manager.py,sha256=TepnGIoE2hU-j_NmU5ByoS-JrboE0w3A1bYmQoQX5h8,15176
|
4
4
|
remote/account_viewer.py,sha256=j46KSjbgrBmBi7UxFeJ5tCwHIe0QvCvphkirGIbB2oo,5192
|
@@ -22,18 +22,18 @@ remote/joiner.py,sha256=CXTQ-l6CysqZSkYP1GUV1LM1qlh5wD4jBj_og_FVW1I,7773
|
|
22
22
|
remote/leave_controller.py,sha256=TIShgxCMiKyBn9ZdQZYm6Kf89GgDaoPCRH7pxqq9FC8,1167
|
23
23
|
remote/lefter.py,sha256=jxZmkl3NIe1c-WeVES8WBX21hW-eocfxfePvxddHdi0,6090
|
24
24
|
remote/mention_manager.py,sha256=I75ezmVZM0lYrgk8OQ2p-VETLLwM5FiW83tiBgbQ1CY,3257
|
25
|
-
remote/precise_engine.py,sha256=
|
25
|
+
remote/precise_engine.py,sha256=3LhyDcITIfscCgYTwWyJ-CTAWylrufqHeURR1FAwKvA,641
|
26
26
|
remote/profile_info.py,sha256=Us5mz6sdV_5-5wRx6F3S6rtj2m6vaP_HOrCBLgHXpD4,1992
|
27
27
|
remote/profile_media.py,sha256=lD_uIb0U3jeH_dpa4SNbF0wr1DQiQ5OfbTCOubcm9Us,2264
|
28
28
|
remote/profile_privacy.py,sha256=LrRZbbqhaYdl4JzBMJrl-D-N8y1AG2LR1DDzv-tmlV0,2298
|
29
|
-
remote/spammer.py,sha256=
|
29
|
+
remote/spammer.py,sha256=lKALyfX3hInsJmR6qVb5xxAdM-GkW2s42e6gqTyIo1k,11820
|
30
30
|
remote/speed_manager.py,sha256=fIWSQAP9qW8AHZtMZq0MrC4_nvxcTFU1SBU75kpRzB8,1115
|
31
31
|
remote/stop_manager.py,sha256=UXzKJTblEyQqCjp7fenvQ51Q96Unx05WeOiuFMdj25M,1151
|
32
32
|
remote/text_manager.py,sha256=C2wNSXPSCDu8NSD3RsfbKmUQMWOYd1B5N4tzy-Jsriw,2195
|
33
33
|
remote/username_manager.py,sha256=nMNdke-2FIv86xR1Y6rR-43oUoQu_3Khw8wEo54noXI,3388
|
34
34
|
remote/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
35
|
remote/utils/sqlite_utils.py,sha256=5i0oUXsBgKC_8qHZPJ-Gyhp9D1TwqKHVvuZRIhKpS6w,1260
|
36
|
-
cliremote-1.
|
37
|
-
cliremote-1.
|
38
|
-
cliremote-1.
|
39
|
-
cliremote-1.
|
36
|
+
cliremote-1.8.3.dist-info/METADATA,sha256=OsHKIAwSjhPU2NGE8vUv8FuKc9xNe4tUwTadfrJk_z4,1202
|
37
|
+
cliremote-1.8.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
38
|
+
cliremote-1.8.3.dist-info/top_level.txt,sha256=yBZidJ6zCix_a2ubGlYaewvlzBFXWbckQt20dudxJ1E,7
|
39
|
+
cliremote-1.8.3.dist-info/RECORD,,
|
remote/precise_engine.py
CHANGED
@@ -1,45 +1,19 @@
|
|
1
1
|
# antispam_core/precise_engine.py
|
2
|
-
import asyncio
|
3
|
-
import time
|
2
|
+
import asyncio, time
|
4
3
|
|
5
4
|
class PreciseTicker:
|
6
|
-
"""
|
7
|
-
A drift-free ticker.
|
8
|
-
Usage:
|
9
|
-
ticker = PreciseTicker(interval=1.0)
|
10
|
-
while running:
|
11
|
-
# do work
|
12
|
-
await ticker.sleep()
|
13
|
-
Behavior:
|
14
|
-
- Ensures interval spacing between wakes without accumulating drift.
|
15
|
-
- If the work took longer than the interval, it schedules next wake as now + interval
|
16
|
-
(so it doesn't try to "catch up" by sleeping negative amounts).
|
17
|
-
- On first use, it sets the baseline relative to time.perf_counter().
|
18
|
-
"""
|
19
5
|
def __init__(self, interval: float):
|
20
|
-
if interval <= 0:
|
21
|
-
raise ValueError("interval must be > 0")
|
22
6
|
self.interval = float(interval)
|
23
|
-
self.next_tick =
|
7
|
+
self.next_tick = time.perf_counter()
|
24
8
|
|
25
9
|
async def sleep(self):
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
self.next_tick = now + self.interval
|
30
|
-
|
31
|
-
delay = self.next_tick - now
|
32
|
-
|
10
|
+
"""خواب دقیق بدون drift در زمان"""
|
11
|
+
self.next_tick += self.interval
|
12
|
+
delay = self.next_tick - time.perf_counter()
|
33
13
|
if delay > 0:
|
34
|
-
# normal case: wait remaining time
|
35
14
|
await asyncio.sleep(delay)
|
36
|
-
# advance to next slot
|
37
|
-
self.next_tick += self.interval
|
38
15
|
else:
|
39
|
-
|
40
|
-
self.next_tick = now + self.interval
|
41
|
-
# don't sleep now (we are already late)
|
16
|
+
self.next_tick = time.perf_counter() # ریست در صورت عقب افتادن
|
42
17
|
|
43
18
|
def reset(self):
|
44
|
-
|
45
|
-
self.next_tick = None
|
19
|
+
self.next_tick = time.perf_counter()
|
remote/spammer.py
CHANGED
@@ -30,7 +30,7 @@ if not any(
|
|
30
30
|
# ============================================================
|
31
31
|
# ✅ پیکربندی ایمن و پارامترهای پیشفرض
|
32
32
|
# ============================================================
|
33
|
-
MAX_RETRIES =
|
33
|
+
MAX_RETRIES = 3 # تلاش مجدد حداکثر برای هر ارسال
|
34
34
|
BASE_BACKOFF = 1.5 # ضریب بکآف برای retries (s)
|
35
35
|
FLOOD_COOLDOWN_CAP = 3600 # حداکثر زمان cooldown برای FloodWait (s)
|
36
36
|
|
@@ -45,23 +45,6 @@ if not hasattr(client_manager, "client_locks"):
|
|
45
45
|
# ============================================================
|
46
46
|
# 🧰 توابع کمکی
|
47
47
|
# ============================================================
|
48
|
-
def is_in_cooldown(phone: str) -> bool:
|
49
|
-
ts = _account_cooldowns.get(phone)
|
50
|
-
if not ts:
|
51
|
-
return False
|
52
|
-
return ts > asyncio.get_event_loop().time()
|
53
|
-
|
54
|
-
def set_cooldown(phone: str, seconds: float):
|
55
|
-
resume = asyncio.get_event_loop().time() + seconds
|
56
|
-
_account_cooldowns[phone] = resume
|
57
|
-
logger.info(f"{phone}: ⏳ Put on cooldown for {seconds:.1f}s (resume @ {resume:.1f})")
|
58
|
-
|
59
|
-
def clear_expired_cooldowns():
|
60
|
-
now = asyncio.get_event_loop().time()
|
61
|
-
expired = [p for p, t in _account_cooldowns.items() if t <= now]
|
62
|
-
for p in expired:
|
63
|
-
_account_cooldowns.pop(p, None)
|
64
|
-
logger.info(f"{p}: ✅ Cooldown expired")
|
65
48
|
|
66
49
|
async def _attempt_send(cli, acc_phone: str, target: str, text: str):
|
67
50
|
"""Try to send once, raising exceptions up to caller to handle specialized behavior."""
|
@@ -82,10 +65,6 @@ async def safe_send(acc_phone: str, spam_config: dict, text: str, remove_client_
|
|
82
65
|
بازگشت: True اگر ارسال موفق باشد، False در غیر این صورت.
|
83
66
|
"""
|
84
67
|
try:
|
85
|
-
# اگر اکانت در cooldown است، زود بازگردان False (بدون تلاش)
|
86
|
-
if is_in_cooldown(acc_phone):
|
87
|
-
logger.info(f"{acc_phone}: ⚠️ Skipping send because account is in cooldown.")
|
88
|
-
return False
|
89
68
|
|
90
69
|
cli = await client_manager.get_or_start_client(acc_phone)
|
91
70
|
if not cli:
|
@@ -122,8 +101,7 @@ async def safe_send(acc_phone: str, spam_config: dict, text: str, remove_client_
|
|
122
101
|
wait = e.value if hasattr(e, "value") else getattr(e, "x", 0)
|
123
102
|
# حداقل و حداکثر را منطقی حفظ کن
|
124
103
|
wait = max(1, min(wait, FLOOD_COOLDOWN_CAP))
|
125
|
-
logger.warning(f"{acc_phone}: ⏰ FloodWait {wait}s (code:{getattr(e, 'value', 'n/a')})")
|
126
|
-
set_cooldown(acc_phone, wait)
|
104
|
+
logger.warning(f"{acc_phone}: ⏰ FloodWait {wait}s (code:{getattr(e, 'value', 'n/a')})")
|
127
105
|
# صبر در این تابع به اندازهٔ wait ضروری نیست — cooldown کافی است.
|
128
106
|
return False
|
129
107
|
|
@@ -189,8 +167,7 @@ async def run_spammer(spam_config: dict, get_spam_texts, make_mention_html, remo
|
|
189
167
|
|
190
168
|
try:
|
191
169
|
while spam_config.get("run", False):
|
192
|
-
# cleanup expired cooldowns
|
193
|
-
clear_expired_cooldowns()
|
170
|
+
# cleanup expired cooldowns
|
194
171
|
|
195
172
|
# refresh active accounts (live snapshot)
|
196
173
|
active_accounts: Set[str] = set(client_manager.get_active_accounts())
|
@@ -200,12 +177,8 @@ async def run_spammer(spam_config: dict, get_spam_texts, make_mention_html, remo
|
|
200
177
|
continue
|
201
178
|
|
202
179
|
# filter out accounts currently in cooldown
|
203
|
-
acc_list = sorted([a for a in active_accounts
|
204
|
-
|
205
|
-
# همه در cooldown هستند — کمی صبر کن و ادامه بده
|
206
|
-
logger.info("تمام اکانتها در cooldown هستند؛ کمی صبر میکنیم...")
|
207
|
-
await asyncio.sleep(min(5, base_delay))
|
208
|
-
continue
|
180
|
+
acc_list = sorted([a for a in active_accounts])
|
181
|
+
|
209
182
|
|
210
183
|
texts = get_spam_texts()
|
211
184
|
if not texts:
|
File without changes
|
File without changes
|
File without changes
|