diff --git a/GramAddict/core/perception/intent_resolver.py b/GramAddict/core/perception/intent_resolver.py index 281ea54..e862914 100644 --- a/GramAddict/core/perception/intent_resolver.py +++ b/GramAddict/core/perception/intent_resolver.py @@ -278,6 +278,35 @@ class IntentResolver: logger.info(f"🎯 [Structural Fast-Path] Found first post/item: {rid}") return node + if "story ring" in intent_lower or "story tray" in intent_lower: + story_nodes = [] + for node in candidates: + rid = (node.resource_id or "").lower() + desc = (node.content_desc or "").lower() + text = (node.text or "").lower() + # Instagram story tray avatars usually have this resource id and 'story' in the content description + if ("avatar_image_view" in rid or "row_profile_header_imageview" in rid) and "story" in desc: + # Ignore the user's explicit "Add to story" ring + if "add to story" not in desc and "your story" not in text: + story_nodes.append(node) + + if story_nodes: + # Sort horizontally (left-to-right) + story_nodes.sort(key=lambda n: n.x1) + + # If we are looking at the home feed story tray (avatar_image_view) + if "avatar_image_view" in (story_nodes[0].resource_id or "").lower(): + if len(story_nodes) > 1: + logger.info(f"🎯 [Structural Fast-Path] Found {len(story_nodes)} story rings. Skipping own profile. Picking second: '{story_nodes[1].content_desc}'") + return story_nodes[1] + else: + logger.warning("🎯 [Structural Fast-Path] Only 1 story ring found on feed (likely own profile). Skipping to avoid modal trap.") + return None + else: + # Profile header or other single-story views + logger.info(f"🎯 [Structural Fast-Path] Found story ring avatar: {story_nodes[0].resource_id} (desc: '{story_nodes[0].content_desc}')") + return story_nodes[0] + # --- Navigation Tab Fast-Paths --- # Deterministically identify bottom navigation tabs to prevent VLM confusion tab_map = {