Skip to content

Commit 5bd07af

Browse files
committed
Merge remote-tracking branch 'upstream/dev'
2 parents e7a5903 + 6f06d06 commit 5bd07af

1 file changed

Lines changed: 49 additions & 40 deletions

File tree

‎instagram_monitor.py‎

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,32 +3618,32 @@ def send_webhook(title, description, color=0x7289DA, fields=None, image_url=None
36183618
"inline": field.get("inline", False)
36193619
})
36203620

3621-
sanitized_image_url = apply_privacy_substitutions(str(image_url)) if image_url else ""
3621+
webhook_image_url = str(image_url) if image_url else ""
36223622

36233623
# Load all possible items into payload for use in formatting the WEBHOOK_TEMPLATE and WEBHOOK_HEADERS
36243624
payload = {
36253625
"title": title[:WEBHOOK_EMBED_TITLE_LIMIT] if title else "Instagram Monitor", # type: ignore
36263626
"description": description[:WEBHOOK_EMBED_DESCRIPTION_LIMIT] if description else "", # type: ignore
36273627
"version": VERSION,
3628-
"image_url": sanitized_image_url,
3628+
"image_url": webhook_image_url,
36293629
"fields": sanitized_fields,
36303630
"fields_str": "\n".join([f"{f['name']}: {f['value']}" for f in sanitized_fields]) if sanitized_fields else "",
36313631
"color": color,
36323632
"timestamp": datetime.now(timezone.utc).isoformat()
36333633
}
36343634

3635-
if sanitized_image_url:
3636-
payload["image"] = {"url": sanitized_image_url}
3635+
if webhook_image_url:
3636+
payload["image"] = {"url": webhook_image_url}
36373637
elif local_image_file and os.path.isfile(local_image_file):
36383638
# If using local file, use attachment:// syntax
36393639
filename = os.path.basename(local_image_file)
36403640
payload["image"] = {"url": f"attachment://{filename}"}
36413641

36423642
if WEBHOOK_USERNAME:
3643-
payload["username"] = apply_privacy_substitutions(str(WEBHOOK_USERNAME))
3643+
payload["username"] = WEBHOOK_USERNAME
36443644

36453645
if WEBHOOK_AVATAR_URL:
3646-
payload["avatar_url"] = apply_privacy_substitutions(str(WEBHOOK_AVATAR_URL))
3646+
payload["avatar_url"] = WEBHOOK_AVATAR_URL
36473647

36483648
# Apply optional transformations to payload, primarily the title and description
36493649
for transform in WEBHOOK_TRANSFORMS: # type: ignore
@@ -6318,19 +6318,19 @@ def _update_progress_bar(resp):
63186318
thread_wrapper_count = getattr(_thread_local, 'WRAPPER_COUNT', 0)
63196319

63206320
# Determine which config vars to use based on fetch type and advanced fetch flags
6321-
fetch_type = getattr(_thread_local, 'FETCH_TYPE', 'none')
6321+
# If no FETCH_TYPE is set (e.g. a future setup_pbar caller that does not flag a fetch kind),
6322+
# fall back to non-batched defaults so the wrapper still updates the bar via tqdm's rate
6323+
fetch_type = getattr(_thread_local, 'FETCH_TYPE', None)
63226324
if fetch_type == 'followee':
6323-
per_batch = FOLLOWEES_PER_BATCH if ADVANCED_FOLLOWEE_FETCH else 0
6324-
batch_delay = FOLLOWEE_DELAY_PER_BATCH if ADVANCED_FOLLOWEE_FETCH else 0
6325-
limit = FOLLOWEE_LIMIT_TO_FETCH if ADVANCED_FOLLOWEE_FETCH else 0
6325+
per_batch = FOLLOWEES_PER_BATCH if ADVANCED_FOLLOWEE_FETCH else 0
6326+
batch_delay = FOLLOWEE_DELAY_PER_BATCH if ADVANCED_FOLLOWEE_FETCH else 0
63266327
advanced_fetch = ADVANCED_FOLLOWEE_FETCH
63276328
elif fetch_type == 'follower':
6328-
per_batch = FOLLOWERS_PER_BATCH if ADVANCED_FOLLOWER_FETCH else 0
6329-
batch_delay = FOLLOWER_DELAY_PER_BATCH if ADVANCED_FOLLOWER_FETCH else 0
6330-
limit = FOLLOWER_LIMIT_TO_FETCH if ADVANCED_FOLLOWER_FETCH else 0
6329+
per_batch = FOLLOWERS_PER_BATCH if ADVANCED_FOLLOWER_FETCH else 0
6330+
batch_delay = FOLLOWER_DELAY_PER_BATCH if ADVANCED_FOLLOWER_FETCH else 0
63316331
advanced_fetch = ADVANCED_FOLLOWER_FETCH
63326332
else:
6333-
return
6333+
per_batch, batch_delay, advanced_fetch = 0, 0, False
63346334
# Only process and count requests that are likely follower/following related
63356335
# Check if response is successful and JSON before processing
63366336
user_list = []
@@ -6408,10 +6408,7 @@ def _update_progress_bar(resp):
64086408
else:
64096409
elapsed_str = f"{elapsed_m:.1f}"
64106410

6411-
# batch_info = f", fetch={limit}/{per_batch}/{batch_delay}s" if advanced_fetch else ""
6412-
# stats_string = f"{names_per_req:.1f} names/req, reqs={thread_wrapper_count:d}, mins={elapsed_m:.1f}, remain={rem_m:.1f}{batch_info}"
64136411
stats_string = f"{names_per_req:.1f} names/req, reqs={thread_wrapper_count:d}, mins={elapsed_str}, remain={remain_str}"
6414-
# debug_print(f"{fetched_so_far}/{d['total']} [{stats_string}]")
64156412
thread_pbar.unit = stats_string
64166413
thread_pbar.update(increment)
64176414

@@ -6818,6 +6815,7 @@ def simulate_human_actions(bot: instaloader.Instaloader, sleep_seconds: int) ->
68186815
print("* BeHuman: simulation stop")
68196816

68206817

6818+
# Formats an advanced follower/followee fetch config as a human-readable description
68216819
def build_follow_string(enabled, limit, batch, delay, alt_format=False):
68226820
if enabled:
68236821
if limit and not batch and not delay:
@@ -6831,7 +6829,7 @@ def build_follow_string(enabled, limit, batch, delay, alt_format=False):
68316829
return follow_str
68326830

68336831

6834-
def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit, fetch_delay, advanced_fetch, estimated_limit, user):
6832+
def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit, fetch_delay, advanced_fetch, estimated_limit, user, stop_event=None):
68356833
"""Fetch usernames in batches using a fresh generator per call.
68366834

68376835
Args:
@@ -6840,11 +6838,13 @@ def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit,
68406838
total_limit: Stop after this many total accounts (FOLLOWER_LIMIT_TO_FETCH / FOLLOWEE_LIMIT_TO_FETCH). 0 = no limit.
68416839
fetch_delay: Seconds to sleep between batches.
68426840
advanced_fetch: Indicates if advanced_fetch is enabled (valid configuration of above 3 items)
6843-
estimated_limit: Estimated number of items to fetch. Used for messaging.
6841+
estimated_limit: Estimated number of itesm to fetch. Used for messaging.
68446842
user: Instagram username, forwarded to log_activity.
6843+
stop_event: Optional threading.Event from the caller. If set, the inter-batch wait is
6844+
aborted and the function returns whatever has been fetched so far.
68456845

68466846
Returns:
6847-
List of username strings.
6847+
List of username strings (may be partial if stop_event fired).
68486848
"""
68496849
results = []
68506850
gen = get_generator_fn() # single generator — keeps cursor position across batches
@@ -6854,15 +6854,24 @@ def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit,
68546854
msg = f"Fetching {build_follow_string(advanced_fetch, estimated_limit, max_per_batch, fetch_delay, alt_format=True)}"
68556855
if thread_pbar:
68566856
thread_pbar.write(f"* {msg}", file=thread_pbar.fp)
6857-
print(f"* {msg}") # if pbar, this will go to log, while the thread_pbar.write only goes to screen
6857+
print(f"* {msg}") # if pbar, this will go to log, while the thread_pbar.write only goes to screen
68586858
log_activity(msg, user=user)
68596859

68606860
while True:
6861+
# Abort between batches if the caller (e.g. Ctrl-C, web-dashboard stop) requested shutdown
6862+
# Returns whatever has been fetched so far rather than None so the caller's len()/iteration is safe
6863+
if stop_event is not None and stop_event.is_set():
6864+
return results
6865+
68616866
batch = []
68626867
for f in gen:
68636868
batch.append(f.username)
68646869
if advanced_fetch and max_per_batch and (len(batch) >= max_per_batch):
68656870
break # pause; generator retains its position
6871+
# Stop mid-batch if the overall total_limit would be exceeded, otherwise a per_batch
6872+
# larger than total_limit (e.g. limit=30, per_batch=50) over-fetches by a full batch
6873+
if advanced_fetch and total_limit and (len(results) + len(batch) >= total_limit):
6874+
break
68666875

68676876
if not batch:
68686877
break # generator fully exhausted
@@ -6874,16 +6883,13 @@ def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit,
68746883

68756884
# advanced fetching feature disabled or generator ran out mid-batch
68766885
if not advanced_fetch or (max_per_batch and len(batch) < max_per_batch):
6877-
break
6886+
break
68786887

68796888
# advanced fetching feature enabled if here
68806889
if fetch_delay:
6881-
# Use thread-local storage for multi-target safety
6882-
stop_event = threading.Event()
68836890
# Interruptible wait (stop/recheck aware) similar to the main sleep loop
68846891
sleep_remaining = fetch_delay
6885-
if thread_pbar:
6886-
batch_info_orig = thread_pbar.unit
6892+
batch_info_orig = thread_pbar.unit if thread_pbar else ""
68876893
while sleep_remaining > 0:
68886894
if thread_pbar:
68896895
# need to remove part of string to make room, since entire PBAR needs to fit within HORIZONTAL_LINE width (safe_ncols)
@@ -6892,24 +6898,23 @@ def fetch_usernames_paginated(bot, get_generator_fn, max_per_batch, total_limit,
68926898
batch_info = re.sub(r" - PAUSED.*$", "", batch_info) + f" - PAUSED for {sleep_remaining}s"
68936899
thread_pbar.unit = batch_info
68946900
thread_pbar.refresh()
6895-
if stop_event and stop_event.is_set():
6896-
return
6897-
# Allow Web Dashboard "recheck" to break the wait early (still hour-gated later)
6901+
if stop_event is not None and stop_event.is_set():
6902+
return results
6903+
# If a Web Dashboard "recheck" is pending, shorten the current inter-batch wait so the
6904+
# in-progress fetch completes sooner. Do not consume the event here: the recheck applies
6905+
# to the next check cycle and is handled by the main loop after this fetch returns
6906+
recheck_pending = False
68986907
if WEB_DASHBOARD_ENABLED:
68996908
with WEB_DASHBOARD_DATA_LOCK: # type: ignore
69006909
if user in WEB_DASHBOARD_RECHECK_EVENTS and WEB_DASHBOARD_RECHECK_EVENTS[user].is_set():
6901-
WEB_DASHBOARD_RECHECK_EVENTS[user].clear()
6902-
manual_recheck_active = True
6903-
manual_override_active = True
6904-
log_activity("Manual recheck requested", user=user, level='system')
6905-
update_ui_data(targets={user: {'status': 'Recheck requested'}})
6906-
break
6910+
recheck_pending = True
69076911
# Check for proxy changes from web dashboard
69086912
refresh_proxy_if_needed(bot, user)
6913+
if recheck_pending:
6914+
break
69096915

69106916
wait_chunk = min(1, sleep_remaining)
6911-
# debug_print(wait_chunk)
6912-
if stop_event:
6917+
if stop_event is not None:
69136918
stop_event.wait(wait_chunk)
69146919
else:
69156920
time.sleep(wait_chunk)
@@ -7411,6 +7416,7 @@ def _get_iphone_json(path, params, **kwargs):
74117416
advanced_fetch=ADVANCED_FOLLOWER_FETCH,
74127417
estimated_limit=follower_limit,
74137418
user=user,
7419+
stop_event=stop_event,
74147420
)
74157421
_thread_local.FETCH_TYPE = None
74167422
end_time_dl = time.time()
@@ -7558,6 +7564,7 @@ def _get_iphone_json(path, params, **kwargs):
75587564
advanced_fetch=ADVANCED_FOLLOWEE_FETCH,
75597565
estimated_limit=followee_limit,
75607566
user=user,
7567+
stop_event=stop_event,
75617568
)
75627569
_thread_local.FETCH_TYPE = None
75637570
end_time_dl = time.time()
@@ -8448,6 +8455,7 @@ def _get_iphone_json(path, params, **kwargs):
84488455
advanced_fetch=ADVANCED_FOLLOWEE_FETCH,
84498456
estimated_limit=followee_limit,
84508457
user=user,
8458+
stop_event=stop_event,
84518459
)
84528460
_thread_local.FETCH_TYPE = None
84538461
followings_to_save = []
@@ -8607,6 +8615,7 @@ def _get_iphone_json(path, params, **kwargs):
86078615
advanced_fetch=ADVANCED_FOLLOWER_FETCH,
86088616
estimated_limit=follower_limit,
86098617
user=user,
8618+
stop_event=stop_event,
86108619
)
86118620
_thread_local.FETCH_TYPE = None
86128621
followers_to_save = []
@@ -10199,17 +10208,17 @@ def run_main():
1019910208

1020010209
# Advanced Follower/Followee Fetching Settings
1020110210
if any([FOLLOWERS_PER_BATCH, FOLLOWER_LIMIT_TO_FETCH, FOLLOWER_DELAY_PER_BATCH]):
10202-
ADVANCED_FOLLOWER_FETCH = (FOLLOWERS_PER_BATCH and FOLLOWER_DELAY_PER_BATCH) or (FOLLOWER_LIMIT_TO_FETCH and not FOLLOWERS_PER_BATCH and not FOLLOWER_DELAY_PER_BATCH)
10211+
ADVANCED_FOLLOWER_FETCH = bool((FOLLOWERS_PER_BATCH and FOLLOWER_DELAY_PER_BATCH) or (FOLLOWER_LIMIT_TO_FETCH and not FOLLOWERS_PER_BATCH and not FOLLOWER_DELAY_PER_BATCH))
1020310212
if not ADVANCED_FOLLOWER_FETCH:
1020410213
print(f"* Error: Invalid configuration for advanced follower fetching: FOLLOWER_LIMIT_TO_FETCH: {FOLLOWER_LIMIT_TO_FETCH}, FOLLOWERS_PER_BATCH: {FOLLOWERS_PER_BATCH}, FOLLOWER_DELAY_PER_BATCH: {FOLLOWER_DELAY_PER_BATCH}")
1020510214
sys.exit(1)
1020610215

1020710216
if any([FOLLOWEES_PER_BATCH, FOLLOWEE_LIMIT_TO_FETCH, FOLLOWEE_DELAY_PER_BATCH]):
10208-
ADVANCED_FOLLOWEE_FETCH = (FOLLOWEES_PER_BATCH and FOLLOWEE_DELAY_PER_BATCH) or (FOLLOWEE_LIMIT_TO_FETCH and not FOLLOWEES_PER_BATCH and not FOLLOWEE_DELAY_PER_BATCH)
10217+
ADVANCED_FOLLOWEE_FETCH = bool((FOLLOWEES_PER_BATCH and FOLLOWEE_DELAY_PER_BATCH) or (FOLLOWEE_LIMIT_TO_FETCH and not FOLLOWEES_PER_BATCH and not FOLLOWEE_DELAY_PER_BATCH))
1020910218
if not ADVANCED_FOLLOWEE_FETCH:
1021010219
print(f"* Error: Invalid configuration for advanced followee fetching: FOLLOWEE_LIMIT_TO_FETCH: {FOLLOWEE_LIMIT_TO_FETCH}, FOLLOWEES_PER_BATCH: {FOLLOWEES_PER_BATCH}, FOLLOWEE_DELAY_PER_BATCH: {FOLLOWEE_DELAY_PER_BATCH}")
1021110220
sys.exit(1)
10212-
10221+
1021310222
# Handle new debug, dashboard, and webhook arguments
1021410223
if args.debug_mode is True:
1021510224
DEBUG_MODE = True

0 commit comments

Comments
 (0)