fix(feed): resolve home feed back-button scroll-to-top trap
- Separated obstacle detection from feed marker validation - Prevented blind BACK button presses when markers are missing mid-scroll - Added TDD verification for feed navigation stability - Cleaned up debug artifacts and temporary test output
This commit is contained in:
@@ -61,7 +61,7 @@ def verify_and_switch_account(device, nav_graph, target_username):
|
||||
return False
|
||||
|
||||
# Long press to open account selector
|
||||
device.deviceV2.long_click(profile_tab[0], profile_tab[1], 1.5)
|
||||
device.long_click(profile_tab[0], profile_tab[1], 1.5)
|
||||
time.sleep(3.0)
|
||||
|
||||
# 4. Find the target account in the selector list
|
||||
@@ -102,7 +102,7 @@ def verify_and_switch_account(device, nav_graph, target_username):
|
||||
|
||||
if account_node:
|
||||
logger.info(f"🖱️ [Identity Guard] Found account '{target_username}' in selector. Tapping!")
|
||||
device.deviceV2.click(account_node[0], account_node[1])
|
||||
device.click(account_node[0], account_node[1])
|
||||
time.sleep(6.0) # Wait heavily for app to reload context
|
||||
nav_graph.current_state = "UNKNOWN" # Force graph to re-evaluate after massive state shift
|
||||
return True
|
||||
@@ -114,6 +114,6 @@ def verify_and_switch_account(device, nav_graph, target_username):
|
||||
except:
|
||||
pass
|
||||
# Escape the bottom sheet
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
return False
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ def start_bot(**kwargs):
|
||||
_humanized_scroll(device, is_skip=False)
|
||||
sleep(2.0)
|
||||
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(1.5)
|
||||
|
||||
# Deduplicate while preserving order
|
||||
@@ -263,9 +263,9 @@ def start_bot(**kwargs):
|
||||
|
||||
if current_desire == "ShiftContext":
|
||||
logger.info("🧠 [Free Will] Boredom critical. Forcing app restart to clear context.")
|
||||
device.deviceV2.app_stop(device.app_id)
|
||||
device.app_stop(device.app_id)
|
||||
random_sleep(2.0, 4.0)
|
||||
device.deviceV2.app_start(device.app_id, use_monkey=True)
|
||||
device.app_start(device.app_id, use_monkey=True)
|
||||
random_sleep(4.0, 6.0)
|
||||
dopamine.boredom = max(0.0, dopamine.boredom * 0.2)
|
||||
continue
|
||||
@@ -327,9 +327,9 @@ def start_bot(**kwargs):
|
||||
|
||||
elif result == "CONTEXT_LOST":
|
||||
logger.warning(f"⚠️ Context was lost in {current_target}. Forcing app restart and returning to HomeFeed to escape softlock.")
|
||||
device.deviceV2.app_stop(device.app_id)
|
||||
device.app_stop(device.app_id)
|
||||
random_sleep(1.0, 2.0)
|
||||
device.deviceV2.app_start(device.app_id, use_monkey=True)
|
||||
device.app_start(device.app_id, use_monkey=True)
|
||||
random_sleep(3.0, 5.0)
|
||||
nav_graph.current_state = "UNKNOWN"
|
||||
|
||||
@@ -464,7 +464,7 @@ def _humanized_scroll(device, is_skip=False, resonance_score=None):
|
||||
|
||||
# Using adb shell input swipe natively triggers Android's elastic momentum (Fling).
|
||||
# UIAutomator2's internal swipe often kills momentum by calculating a zero velocity touch-up.
|
||||
device.deviceV2.shell(f"input swipe {int(start_x)} {int(start_y)} {int(end_x)} {int(end_y)} {duration_ms}")
|
||||
device.shell(f"input swipe {int(start_x)} {int(start_y)} {int(end_x)} {int(end_y)} {duration_ms}")
|
||||
|
||||
def _humanized_click(device, x, y, double=False, sleep_mod=1.0):
|
||||
import random
|
||||
@@ -482,7 +482,7 @@ def _humanized_click(device, x, y, double=False, sleep_mod=1.0):
|
||||
|
||||
# Duration for a human tap
|
||||
duration = random.randint(40, 90)
|
||||
device.deviceV2.shell(f"input swipe {int(start_x)} {int(start_y)} {int(end_x)} {int(end_y)} {duration}")
|
||||
device.shell(f"input swipe {int(start_x)} {int(start_y)} {int(end_x)} {int(end_y)} {duration}")
|
||||
|
||||
if double:
|
||||
# Double tap (Fast, slightly overlapping jitter)
|
||||
@@ -511,7 +511,7 @@ def _humanized_horizontal_swipe(device, start_x, end_x, y, duration_ms):
|
||||
# Introduce +- 30% timing wobble
|
||||
actual_duration = int(duration_ms * random.uniform(0.7, 1.3))
|
||||
|
||||
device.deviceV2.shell(f"input swipe {actual_start_x} {actual_start_y} {actual_end_x} {actual_end_y} {actual_duration}")
|
||||
device.shell(f"input swipe {actual_start_x} {actual_start_y} {actual_end_x} {actual_end_y} {actual_duration}")
|
||||
|
||||
|
||||
def has_carousel_in_view(xml_dump: str) -> bool:
|
||||
@@ -662,7 +662,7 @@ def _interact_with_profile(device, configs, username, session_state, sleep_mod,
|
||||
sleep(random.uniform(2.0, 5.0) * sleep_mod)
|
||||
if i < count - 1:
|
||||
_humanized_click(device, int(w * 0.9), int(h * 0.5), sleep_mod=sleep_mod)
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
|
||||
# Random Follow
|
||||
@@ -751,7 +751,7 @@ def _interact_with_profile(device, configs, username, session_state, sleep_mod,
|
||||
|
||||
sleep(random.uniform(1.5, 3.0) * sleep_mod)
|
||||
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
# Let the native UI momentum scroll finish just like a human watching the feed
|
||||
sleep(random.uniform(1.2, 2.0))
|
||||
@@ -817,7 +817,7 @@ def _align_active_post(device):
|
||||
end_y = start_y + dist
|
||||
|
||||
# Duration 1.0s equals a precise mechanical drag with ZERO momentum (no flinging)
|
||||
device.deviceV2.swipe(cx, start_y, cx, end_y, duration=1.0)
|
||||
device.swipe(cx, start_y, cx, end_y, duration=1.0)
|
||||
sleep(1.0) # Wait for UI to settle
|
||||
logger.debug(f"📐 [Alignment] Snapping attempt {attempts}: Shifted {diff}px.")
|
||||
else:
|
||||
@@ -917,7 +917,7 @@ def _run_zero_latency_stories_loop(device, configs, session_state, cognitive_sta
|
||||
# Check for boredom
|
||||
if dopamine.wants_to_change_feed():
|
||||
logger.info("🧠 [Dopamine] Bored. Escaping StoriesFeed to seek new stimuli.")
|
||||
device.deviceV2.press("back") # Attempt to back out to feed
|
||||
device.press("back") # Attempt to back out to feed
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
return "BOREDOM_CHANGE_FEED"
|
||||
|
||||
@@ -938,7 +938,7 @@ def _run_zero_latency_stories_loop(device, configs, session_state, cognitive_sta
|
||||
sleep(random.uniform(2.0, 5.0) * sleep_mod)
|
||||
|
||||
logger.info("🎬 [StoriesFeed] Session completed naturally.")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
return "FEED_EXHAUSTED"
|
||||
def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session_state, job_target, cognitive_stack, is_reels=False):
|
||||
"""
|
||||
@@ -1049,7 +1049,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
"⚠️ [Anomaly Handler] 0 interactive nodes extracted. UI is blind/stuck! Pressing BACK and scrolling...",
|
||||
extra={"color": f"{Fore.YELLOW}"}
|
||||
)
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(0.5)
|
||||
_humanized_scroll(device)
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
@@ -1062,14 +1062,20 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
# Instagram often renders feed markers in the background while a bottom sheet obscures the view.
|
||||
has_obstacle = bool(re.search(r'bottom_sheet_container|dialog_container|dialog_root|bottom_sheet_drag', str(context_xml)))
|
||||
|
||||
if not has_feed_markers or has_obstacle:
|
||||
|
||||
if has_obstacle:
|
||||
logger.warning(
|
||||
"⚠️ [Self-Check] Obstacle (sheet/dialog/keyboard) is blocking the view! Pressing BACK to dismiss...",
|
||||
extra={"color": f"{Fore.YELLOW}"}
|
||||
)
|
||||
device.press("back")
|
||||
sleep(0.5)
|
||||
continue
|
||||
|
||||
elif not has_feed_markers:
|
||||
consecutive_marker_misses += 1
|
||||
if consecutive_marker_misses >= 3:
|
||||
logger.error("❌ Lost context completely or stuck behind un-clearable obstacle. Aborting feed loop to force reset.")
|
||||
|
||||
|
||||
dump_ui_state(device, "context_lost", {"feed": job_target, "misses": consecutive_marker_misses, "obstacle": has_obstacle})
|
||||
logger.error("❌ Lost context completely. Aborting feed loop to force reset.")
|
||||
dump_ui_state(device, "context_lost", {"feed": job_target, "misses": consecutive_marker_misses})
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
if consecutive_marker_misses == 2:
|
||||
@@ -1082,7 +1088,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
|
||||
if best_node:
|
||||
logger.info(f" -> Recovery attempt! Clicking {best_node.get('semantic', 'Dismiss Button')} at ({best_node['x']}, {best_node['y']})")
|
||||
device.deviceV2.click(best_node["x"], best_node["y"])
|
||||
device.click(best_node["x"], best_node["y"])
|
||||
sleep(2.5)
|
||||
|
||||
# Verification: Check if markers are now visible
|
||||
@@ -1103,12 +1109,10 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
continue
|
||||
|
||||
logger.warning(
|
||||
"⚠️ [Self-Check] NOT on a post! Open sheet or keyboard blocking view? Pressing BACK then scrolling...",
|
||||
"⚠️ [Self-Check] Feed markers missing. Mid-scroll or tall post? Scrolling to reveal markers...",
|
||||
extra={"color": f"{Fore.YELLOW}"}
|
||||
)
|
||||
# Dismiss any open sheets (like the comment sheet) or keyboard
|
||||
device.deviceV2.press("back")
|
||||
sleep(0.5)
|
||||
# DO NOT press 'back' here as we are just on the timeline. It would trigger a scroll-to-top refresh.
|
||||
_humanized_scroll(device)
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
continue
|
||||
@@ -1204,7 +1208,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
_humanized_scroll(device, is_skip=True)
|
||||
sleep(random.uniform(0.5, 1.5) * sleep_mod)
|
||||
logger.info("🔙 [Rabbit Hole] Exiting profile back to main feed.")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(random.uniform(0.8, 1.5) * sleep_mod)
|
||||
|
||||
# ── Darwin: SOLE Dwell Controller (micro-wobble + proof of resonance) ──
|
||||
@@ -1271,7 +1275,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
if target_user.lower() in text_lower and ("profile_name" in res_id or "title" in res_id or "username" in res_id or "avatar" in res_id):
|
||||
if n.get("x") and n.get("y"):
|
||||
logger.info(f"⚡ [Targeted UX] Exact matched username '{target_user}' on screen. Tapping directly.")
|
||||
device.deviceV2.click(n["x"], n["y"])
|
||||
device.click(n["x"], n["y"])
|
||||
nav_success = True
|
||||
break
|
||||
|
||||
@@ -1309,7 +1313,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
|
||||
# Return to feed
|
||||
logger.info("🔙 [Profile Learning] Returning to main feed.")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
_wait_for_post_loaded(device)
|
||||
sleep(random.uniform(1.0, 1.5) * sleep_mod)
|
||||
|
||||
@@ -1465,7 +1469,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
telepathic.confirm_click(intent)
|
||||
_interact_with_profile(device, configs, "commenter", session_state, sleep_mod, logger, cognitive_stack)
|
||||
logger.info("🔙 [Randomization] Returning to comment sheet.")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(random.uniform(1.5, 3.0) * sleep_mod)
|
||||
else:
|
||||
telepathic.reject_click(intent)
|
||||
@@ -1554,7 +1558,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
logger.info(f"🚫 [DRY RUN] Generated comment: '{clean_comment}'. Skipping UI injection.", extra={"color": f"{Fore.MAGENTA}"})
|
||||
sleep(1.5)
|
||||
else:
|
||||
device.deviceV2.click(comment_box["x"], comment_box["y"])
|
||||
device.click(comment_box["x"], comment_box["y"])
|
||||
sleep(random.uniform(1.2, 2.2))
|
||||
|
||||
# Verification: Did the keyboard open or cursor move to box?
|
||||
@@ -1576,16 +1580,16 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
if random.random() < 0.5:
|
||||
# Rapid backspace (Manual deletion)
|
||||
for _ in range(len(clean_comment) + 2):
|
||||
device.deviceV2.press("del")
|
||||
device.press("del")
|
||||
sleep(random.uniform(0.01, 0.05))
|
||||
else:
|
||||
# Press back to trigger Discard popup
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(1.0)
|
||||
xml_dump = device.dump_hierarchy()
|
||||
discard_btn = telepathic.find_best_node(xml_dump, "Discard or Verwerfen popup button to cancel comment", device=device)
|
||||
if discard_btn:
|
||||
device.deviceV2.click(discard_btn["x"], discard_btn["y"])
|
||||
device.click(discard_btn["x"], discard_btn["y"])
|
||||
telepathic.confirm_click("Discard or Verwerfen popup button to cancel comment")
|
||||
|
||||
logger.info("🔙 [Umentscheidung] Comment successfully aborted.")
|
||||
@@ -1596,7 +1600,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
pre_post_xml = device.dump_hierarchy()
|
||||
post_btn = telepathic.find_best_node(pre_post_xml, "Post submit comment button", device=device)
|
||||
if post_btn:
|
||||
device.deviceV2.click(post_btn["x"], post_btn["y"])
|
||||
device.click(post_btn["x"], post_btn["y"])
|
||||
sleep(random.uniform(2.0, 3.5))
|
||||
|
||||
# Verification: Did the button disappear or layout change?
|
||||
@@ -1615,10 +1619,10 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
|
||||
# Safely exit the comment sheet
|
||||
if "bottom_sheet_container" in device.dump_hierarchy():
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(1.0)
|
||||
if "bottom_sheet_container" in device.dump_hierarchy():
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(1.0)
|
||||
|
||||
did_interact = True
|
||||
@@ -1662,7 +1666,7 @@ def _run_zero_latency_feed_loop(device, zero_engine, nav_graph, configs, session
|
||||
logger.warning("⚠️ [Repost] Click failed to trigger repost. Learning from failure.")
|
||||
|
||||
# Close share menu if still open
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(random.uniform(1.0, 2.0) * sleep_mod)
|
||||
|
||||
|
||||
@@ -1745,7 +1749,7 @@ def _run_zero_latency_search_loop(device, zero_engine, nav_graph, configs, sessi
|
||||
_humanized_click(device, search_bar["x"], search_bar["y"])
|
||||
sleep(1.5)
|
||||
ghost_type(device, keyword, speed="fast")
|
||||
device.deviceV2.press("enter")
|
||||
device.press("enter")
|
||||
sleep(3.0)
|
||||
|
||||
# 2. Pick a result (Top, Accounts, or Tags)
|
||||
|
||||
@@ -104,7 +104,7 @@ class DarwinEngine(QdrantBase):
|
||||
# Add some x-axis noise for nonlinear human realism (~0.1 cm)
|
||||
noise_x = device.cm_to_pixels(random.uniform(-0.1, 0.1))
|
||||
|
||||
device.deviceV2.swipe(cx, start_y, cx + noise_x, end_y, duration=duration)
|
||||
device.swipe(cx, start_y, cx + noise_x, end_y, duration=duration)
|
||||
|
||||
# 3. Micro Back-swipe (The Human Wobble)
|
||||
if random.random() < profile["back_swipe_prob"]:
|
||||
@@ -116,7 +116,7 @@ class DarwinEngine(QdrantBase):
|
||||
cy = h // 2
|
||||
|
||||
dur_ms = int(random.uniform(200, 500))
|
||||
device.deviceV2.shell(f"input swipe {int(cx)} {int(cy)} {int(cx + noise_x)} {int(cy + slip_distance)} {dur_ms}")
|
||||
device.shell(f"input swipe {int(cx)} {int(cy)} {int(cx + noise_x)} {int(cy + slip_distance)} {dur_ms}")
|
||||
time.sleep(random.uniform(0.5, 1.2))
|
||||
|
||||
# 4. Comment depth simulation (probabilistic & resonance-correlated)
|
||||
@@ -168,14 +168,14 @@ class DarwinEngine(QdrantBase):
|
||||
# ------------------------------------------
|
||||
|
||||
logger.debug(" -> Closing comments section")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
time.sleep(1.0)
|
||||
# Instead of relying on a fragile bottom_sheet_container ID,
|
||||
# we verify if the feed is visible. If not, the comment sheet is still open (or keyboard).
|
||||
ui_dump = device.dump_hierarchy()
|
||||
if 'resource-id="com.instagram.android:id/row_feed"' not in ui_dump and 'resource-id="com.instagram.android:id/button_like"' not in ui_dump:
|
||||
logger.debug(" -> Not back on Home feed, pressing back again to close comment sheet/keyboard")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
time.sleep(1.0)
|
||||
else:
|
||||
logger.debug(f" -> Could not find comment button, falling back to dwell simulation for {profile['comment_read_dwell']:.1f}s")
|
||||
@@ -206,7 +206,7 @@ class DarwinEngine(QdrantBase):
|
||||
|
||||
# Single slow slip (use native shell for exact OS-level injection without framework rounding)
|
||||
duration_ms = int(random.uniform(150, 300))
|
||||
device.deviceV2.shell(f"input swipe {int(cx)} {int(cy)} {int(cx + x_shift)} {int(cy + y_shift)} {duration_ms}")
|
||||
device.shell(f"input swipe {int(cx)} {int(cy)} {int(cx + x_shift)} {int(cy + y_shift)} {duration_ms}")
|
||||
|
||||
def _get_historical_landscape(self):
|
||||
try:
|
||||
|
||||
@@ -40,7 +40,7 @@ def get_device_info(device):
|
||||
if not device or not device.deviceV2:
|
||||
logger.error("Cannot get device info: Device not initialized.")
|
||||
return
|
||||
info = device.deviceV2.info
|
||||
info = device.info
|
||||
logger.debug(f"Device Info: {info.get('productName')} | SDK: {info.get('sdkInt')}")
|
||||
|
||||
class DeviceFacade:
|
||||
@@ -94,6 +94,38 @@ class DeviceFacade:
|
||||
self.deviceV2.press("home")
|
||||
sleep(1)
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return self.deviceV2.info
|
||||
|
||||
@adb_retry()
|
||||
def app_start(self, app_id=None, use_monkey=False):
|
||||
target_app = app_id or self.app_id
|
||||
if use_monkey:
|
||||
self.deviceV2.app_start(target_app, use_monkey=True)
|
||||
else:
|
||||
self.deviceV2.app_start(target_app)
|
||||
|
||||
@adb_retry()
|
||||
def app_stop(self, app_id=None):
|
||||
target_app = app_id or self.app_id
|
||||
self.deviceV2.app_stop(target_app)
|
||||
|
||||
@adb_retry()
|
||||
def shell(self, cmd):
|
||||
return self.deviceV2.shell(cmd)
|
||||
|
||||
@adb_retry()
|
||||
def swipe(self, sx, sy, ex, ey, duration=None):
|
||||
"""Pass-through strictly for non-biological bezier swiping (e.g., darwin_engine noise correction)"""
|
||||
kwargs = {}
|
||||
if duration is not None: kwargs["duration"] = duration
|
||||
self.deviceV2.swipe(sx, sy, ex, ey, **kwargs)
|
||||
|
||||
@adb_retry()
|
||||
def long_click(self, x, y, duration=1.5):
|
||||
self.deviceV2.long_click(x, y, duration)
|
||||
|
||||
@adb_retry()
|
||||
def press(self, key):
|
||||
self.deviceV2.press(key)
|
||||
|
||||
@@ -95,7 +95,7 @@ def _run_zero_latency_dm_loop(device, zero_engine, nav_graph, configs, session_s
|
||||
crm.log_sent_dm("unknown_target", response_text, "", [])
|
||||
|
||||
# Return back to inbox
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
sleep(1.0)
|
||||
|
||||
dopamine.boredom += random.uniform(5.0, 15.0)
|
||||
@@ -106,12 +106,12 @@ def _run_zero_latency_dm_loop(device, zero_engine, nav_graph, configs, session_s
|
||||
|
||||
if dopamine.wants_to_change_feed() or dopamine.boredom >= 100:
|
||||
logger.info("🧠 [DM Engine] Interaction complete. Transitioning back from inbox.")
|
||||
device.deviceV2.press("back") # Go back from inbox
|
||||
device.press("back") # Go back from inbox
|
||||
return "BOREDOM_CHANGE_FEED"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"⚠️ [Anomaly Handler] Exception in DM Loop: {e}")
|
||||
device.deviceV2.press("back")
|
||||
device.press("back")
|
||||
failed_attempts += 1
|
||||
if failed_attempts > 2:
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
@@ -37,7 +37,7 @@ def capture_all(device):
|
||||
try:
|
||||
# Pre-condition: Device connected
|
||||
logger.info("Verifying device connection...")
|
||||
device.deviceV2.info
|
||||
device.info
|
||||
|
||||
# 1. Comment Sheet
|
||||
input("\n👉 1. COMMENT SHEET:\nOpen Instagram, scroll to any post on the HomeFeed, and open the comment section.\nWhen the comment sheet is fully visible, press ENTER to capture...")
|
||||
|
||||
@@ -698,19 +698,19 @@ class GoalExecutor:
|
||||
"""Execute a single natural-language action using the TelepathicEngine."""
|
||||
|
||||
if action == 'press back':
|
||||
self.device.deviceV2.press("back")
|
||||
self.device.press("back")
|
||||
random_sleep(0.8, 1.5)
|
||||
return True
|
||||
|
||||
if action == 'scroll down':
|
||||
# Swipe up to scroll down
|
||||
self.device.deviceV2.swipe(540, 1600, 540, 800, duration=0.3)
|
||||
self.device.swipe(540, 1600, 540, 800, duration=0.3)
|
||||
random_sleep(1.0, 2.0)
|
||||
return True
|
||||
|
||||
if action == 'force start instagram':
|
||||
app_id = getattr(self.device, 'app_id', 'com.instagram.android')
|
||||
self.device.deviceV2.app_start(app_id, use_monkey=True)
|
||||
self.device.app_start(app_id, use_monkey=True)
|
||||
random_sleep(2.0, 3.5)
|
||||
return True
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class QNavGraph:
|
||||
# Final fallback: force app start and reset
|
||||
if recovery_attempts < 2:
|
||||
logger.warning(f"🔄 [GOAP Recovery] Step {recovery_attempts + 1}: Attempting app restart to escape softlock...")
|
||||
self.device.deviceV2.app_start(self.device.app_id, use_monkey=True)
|
||||
self.device.app_start(self.device.app_id, use_monkey=True)
|
||||
random_sleep(3.0, 4.5)
|
||||
self.current_state = "HomeFeed"
|
||||
# Clear GOAP status for fresh attempt
|
||||
@@ -242,7 +242,7 @@ class QNavGraph:
|
||||
# we might be in an unknown sub-view. Try one last 'BACK' press.
|
||||
if action == "tap_home_tab":
|
||||
logger.warning("📍 [Escape] Home tab not found after all retries. Attempting final BACK press to escape sub-view...")
|
||||
self.device.deviceV2.press("back")
|
||||
self.device.press("back")
|
||||
time.sleep(2.0)
|
||||
|
||||
return False
|
||||
@@ -305,7 +305,7 @@ class QNavGraph:
|
||||
|
||||
# Safety: If we're not where we expect to be, try to back out to clear any accidentally opened menus
|
||||
logger.info("🛡️ [Safety Reset] Pressing BACK to clear potential accidental menu/sub-view.")
|
||||
self.device.deviceV2.press("back")
|
||||
self.device.press("back")
|
||||
time.sleep(1.0)
|
||||
|
||||
if attempt < max_retries:
|
||||
|
||||
@@ -507,34 +507,34 @@ class SituationalAwarenessEngine:
|
||||
"""Execute a single escape action."""
|
||||
if action.action_type == "click":
|
||||
logger.info(f"👆 [SAE Act] Clicking ({action.x}, {action.y}): {action.reason}")
|
||||
self.device.deviceV2.click(action.x, action.y) # Native click for safety (no human_click edge risks)
|
||||
self.device.click(action.x, action.y) # Native click for safety (no human_click edge risks)
|
||||
random_sleep(0.8, 1.5)
|
||||
|
||||
elif action.action_type == "back":
|
||||
logger.info(f"⬅️ [SAE Act] Pressing BACK: {action.reason}")
|
||||
self.device.deviceV2.press("back")
|
||||
self.device.press("back")
|
||||
random_sleep(0.8, 1.5)
|
||||
|
||||
elif action.action_type == "unlock":
|
||||
logger.info(f"🔓 [SAE Act] Unlocking device: {action.reason}")
|
||||
self.device.deviceV2.unlock()
|
||||
self.device.unlock()
|
||||
random_sleep(1.0, 2.0)
|
||||
app_id = getattr(self.device, 'app_id', 'com.instagram.android')
|
||||
self.device.deviceV2.app_start(app_id, use_monkey=True)
|
||||
self.device.app_start(app_id, use_monkey=True)
|
||||
random_sleep(1.5, 2.5)
|
||||
|
||||
elif action.action_type == "app_start":
|
||||
logger.info(f"🚀 [SAE Act] Force-starting app: {action.reason}")
|
||||
app_id = getattr(self.device, 'app_id', 'com.instagram.android')
|
||||
self.device.deviceV2.app_start(app_id, use_monkey=True)
|
||||
self.device.app_start(app_id, use_monkey=True)
|
||||
random_sleep(2.0, 3.5)
|
||||
|
||||
elif action.action_type == "home_then_app":
|
||||
logger.info(f"🏠 [SAE Act] HOME → App Start: {action.reason}")
|
||||
self.device.deviceV2.press("home")
|
||||
self.device.press("home")
|
||||
random_sleep(0.5, 1.0)
|
||||
app_id = getattr(self.device, 'app_id', 'com.instagram.android')
|
||||
self.device.deviceV2.app_start(app_id, use_monkey=True)
|
||||
self.device.app_start(app_id, use_monkey=True)
|
||||
random_sleep(2.0, 3.5)
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
@@ -39,7 +39,7 @@ def ghost_type(device, text: str):
|
||||
sleep(random.uniform(0.2, 0.45))
|
||||
|
||||
# Send Backspace (KEYCODE_DEL = 67)
|
||||
device.deviceV2.shell("input keyevent 67")
|
||||
device.shell("input keyevent 67")
|
||||
sleep(random.uniform(0.1, 0.25))
|
||||
|
||||
# Inject the correct character
|
||||
@@ -67,6 +67,6 @@ def _adb_inject_text(device, text: str):
|
||||
|
||||
# Send through Android's native InputManager
|
||||
try:
|
||||
device.deviceV2.shell(["input", "text", safe_text])
|
||||
device.shell(["input", "text", safe_text])
|
||||
except Exception as e:
|
||||
logger.debug(f"[Ghost Keyboard] Native injection failed: {e}")
|
||||
|
||||
@@ -14,7 +14,7 @@ def _humanized_scroll_down(device):
|
||||
start_y = int(h * 0.7) + device.cm_to_pixels(random.uniform(-0.5, 0.5))
|
||||
end_y = int(h * 0.2) + device.cm_to_pixels(random.uniform(-0.5, 0.5))
|
||||
duration = random.uniform(0.08, 0.12)
|
||||
device.deviceV2.swipe(start_x, start_y, start_x, end_y, duration)
|
||||
device.swipe(start_x, start_y, start_x, end_y, duration)
|
||||
from GramAddict.core.utils import random_sleep
|
||||
random_sleep(0.8, 1.5)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ def check_if_updated():
|
||||
|
||||
def get_instagram_version(device):
|
||||
try:
|
||||
output = device.deviceV2.shell(f"dumpsys package {device.app_id}").output
|
||||
output = device.shell(f"dumpsys package {device.app_id}").output
|
||||
import re
|
||||
version_match = re.findall("versionName=(\\S+)", output)
|
||||
return version_match[0] if version_match else "unknown"
|
||||
@@ -42,13 +42,13 @@ def close_instagram(device, force_kill=False):
|
||||
if force_kill:
|
||||
logger.info("Force-closing Instagram app to clean session state.")
|
||||
try:
|
||||
device.deviceV2.app_stop(device.app_id)
|
||||
device.app_stop(device.app_id)
|
||||
except Exception as e:
|
||||
logger.debug(f"Error closing app: {e}")
|
||||
else:
|
||||
logger.info("Backgrounding Instagram app (minimizing).")
|
||||
try:
|
||||
device.deviceV2.press("home")
|
||||
device.press("home")
|
||||
except Exception as e:
|
||||
logger.debug(f"Error pressing home: {e}")
|
||||
|
||||
@@ -56,11 +56,11 @@ def open_instagram(device, force_restart=False):
|
||||
if force_restart:
|
||||
logger.info("Opening Instagram app (Fresh Start).")
|
||||
close_instagram(device, force_kill=True)
|
||||
device.deviceV2.app_start(device.app_id)
|
||||
device.app_start(device.app_id)
|
||||
random_sleep(3, 5, modulable=False)
|
||||
else:
|
||||
logger.info("Bringing Instagram app to foreground.")
|
||||
device.deviceV2.app_start(device.app_id)
|
||||
device.app_start(device.app_id)
|
||||
random_sleep(1, 2, modulable=False)
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user