@@ -51,7 +51,9 @@ class Post:
5151 like_count : int = 0
5252 is_reply : bool = False
5353 parent_uri : str | None = None
54+ parent_cid : str | None = None
5455 root_uri : str | None = None
56+ root_cid : str | None = None
5557
5658 # Scoring
5759 base_score : float = 1.0
@@ -318,24 +320,33 @@ def get_replies_to_our_posts(pds: str, jwt: str, our_did: str, conversations: di
318320 continue
319321
320322 thread = r .json ().get ("thread" , {})
323+ # Get root post CID for threading
324+ root_post = thread .get ("post" , {})
325+ root_cid = root_post .get ("cid" , "" )
326+
321327 for reply in thread .get ("replies" , []):
322328 post_data = reply .get ("post" , {})
323329 if post_data .get ("author" , {}).get ("did" ) == our_did :
324330 continue # Skip our own replies
325331
332+ record = post_data .get ("record" , {})
333+ reply_ref = record .get ("reply" , {})
334+
326335 replies .append (Post (
327336 uri = post_data .get ("uri" , "" ),
328337 cid = post_data .get ("cid" , "" ),
329338 author_did = post_data .get ("author" , {}).get ("did" , "" ),
330339 author_handle = post_data .get ("author" , {}).get ("handle" , "" ),
331- text = post_data . get ( " record" , {}) .get ("text" , "" )[:500 ],
332- created_at = post_data . get ( " record" , {}) .get ("createdAt" , "" ),
340+ text = record .get ("text" , "" )[:500 ],
341+ created_at = record .get ("createdAt" , "" ),
333342 reply_count = post_data .get ("replyCount" , 0 ),
334343 like_count = post_data .get ("likeCount" , 0 ),
335344 repost_count = post_data .get ("repostCount" , 0 ),
336345 is_reply = True ,
337- parent_uri = our_post_uri ,
338- root_uri = thread_key
346+ parent_uri = reply_ref .get ("parent" , {}).get ("uri" ) or our_post_uri ,
347+ parent_cid = reply_ref .get ("parent" , {}).get ("cid" ),
348+ root_uri = reply_ref .get ("root" , {}).get ("uri" ) or thread_key ,
349+ root_cid = reply_ref .get ("root" , {}).get ("cid" ) or root_cid
339350 ))
340351 except Exception :
341352 continue
@@ -443,7 +454,9 @@ def filter_recent_posts(posts: list[dict], hours: int = 12) -> list[Post]:
443454 repost_count = post_data .get ("repostCount" , 0 ),
444455 is_reply = is_reply ,
445456 parent_uri = reply_ref .get ("parent" , {}).get ("uri" ) if is_reply else None ,
446- root_uri = reply_ref .get ("root" , {}).get ("uri" ) if is_reply else None
457+ parent_cid = reply_ref .get ("parent" , {}).get ("cid" ) if is_reply else None ,
458+ root_uri = reply_ref .get ("root" , {}).get ("uri" ) if is_reply else None ,
459+ root_cid = reply_ref .get ("root" , {}).get ("cid" ) if is_reply else None
447460 ))
448461
449462 return recent
@@ -549,16 +562,33 @@ def select_posts_with_llm(candidates: list[Post], state: dict, dry_run: bool = F
549562 return []
550563
551564
552- def post_reply (pds : str , jwt : str , did : str , parent_uri : str , parent_cid : str , text : str ) -> dict | None :
553- """Post a reply to a post. Returns the created post data or None."""
565+ def post_reply (
566+ pds : str , jwt : str , did : str ,
567+ parent_uri : str , parent_cid : str ,
568+ text : str ,
569+ root_uri : str | None = None ,
570+ root_cid : str | None = None
571+ ) -> dict | None :
572+ """Post a reply to a post. Returns the created post data or None.
573+
574+ Args:
575+ parent_uri/parent_cid: The post we're directly replying to
576+ root_uri/root_cid: The thread root (if different from parent)
577+
578+ If root is not provided, parent is used as root (for top-level replies).
579+ """
554580 now = dt .datetime .now (dt .timezone .utc ).replace (microsecond = 0 ).isoformat ().replace ("+00:00" , "Z" )
555581
582+ # Use parent as root if root not specified (replying to a non-reply post)
583+ actual_root_uri = root_uri or parent_uri
584+ actual_root_cid = root_cid or parent_cid
585+
556586 record = {
557587 "$type" : "app.bsky.feed.post" ,
558588 "text" : text ,
559589 "createdAt" : now ,
560590 "reply" : {
561- "root" : {"uri" : parent_uri , "cid" : parent_cid },
591+ "root" : {"uri" : actual_root_uri , "cid" : actual_root_cid },
562592 "parent" : {"uri" : parent_uri , "cid" : parent_cid }
563593 }
564594 }
@@ -667,15 +697,24 @@ def run(args) -> int:
667697 print ()
668698
669699 if not dry_run :
670- result = post_reply (pds , jwt , did , sel ["uri" ], sel ["cid" ], sel ["reply" ])
700+ # Look up original Post to get root info for proper threading
701+ original_post = next ((p for p in candidates if p .uri == sel ["uri" ]), None )
702+ root_uri = original_post .root_uri if original_post else None
703+ root_cid = original_post .root_cid if original_post else None
704+
705+ result = post_reply (
706+ pds , jwt , did ,
707+ sel ["uri" ], sel ["cid" ], sel ["reply" ],
708+ root_uri = root_uri , root_cid = root_cid
709+ )
671710 if result :
672711 print (f" ✓ Posted!" )
673712 state ["replied_posts" ].append (sel ["uri" ])
674713 state .setdefault ("replied_accounts_today" , []).append (
675- next (( p .author_did for p in candidates if p . uri == sel [ "uri" ]), "" )
714+ original_post .author_did if original_post else ""
676715 )
677716 # Track for conversation continuation
678- track_reply (conversations , result .get ("uri" , "" ), sel ["uri" ], sel . get ( " root_uri" ) )
717+ track_reply (conversations , result .get ("uri" , "" ), sel ["uri" ], root_uri )
679718 else :
680719 print (f" ✗ Failed to post" )
681720
0 commit comments