# what Mbin finds for a handle or a URL (search/v2 resolves it): the first of its kind, as JSON
mb_resolve(){ mb GET "search/v2?q=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))'"$1")"| j "print(json.dumps(next((r['$2'] for r in d.get('apResults', []) if r.get('$2')), None)))";}
p_home_has(){ curl -s -H "$PH""$P/api/v1/timelines/home?limit=40"| j "print(next(((s.get('reblog') or s)['id'] for s in d if '$1' in ((s.get('reblog') or s)['content'] or '') or '$1' in (((s.get('reblog') or s).get('privapub') or {}).get('title') or '')), ''))";}
-d "{\"avatarId\":\"$alice_id\",\"title\":\"A PrivaPub community thread $run\",\"text\":\"posted into our own community for Mbin\",\"groupId\":\"$dogs_gid\"}"
until_true 45'[ "$(mb GET "magazine/$dogs_on_mb/entries?sort=newest" | j "print(any(e[\"title\"]==\"A PrivaPub community thread $run\" for e in d.get(\"items\", [])))")" = "True" ]'\
&& ok "a titled post in the PrivaPub community reaches Mbin as a thread"|| ko "PrivaPub community post missing on Mbin"
books_thread=$(mb POST "magazine/$books_on_mb/article""{\"title\":\"Books only $run\",\"body\":\"an Mbin magazine thread\",\"lang\":\"en\"}"| j "print(d['entryId'])")
until_true 45'[ -n "$(p_home_has "Books only $run")" ]'&& ok "the magazine's announce brings its thread to alice's home"|| ko "Mbin magazine thread missing from alice's home"
books_thread_on_p=$(p_home_has "Books only $run")
p_note=$(curl -s -X POST -H "$PH"$P/api/v1/statuses --data-urlencode "status=@$books@mbin.test a PrivaPub note for the magazine $run" -d 'visibility=public')
p_note_id=$(echo"$p_note"| j "print(d['id'])")
mb_post_of(){ mb GET "magazine/$books_on_mb/posts?sort=newest"| j "print(next((p['postId'] for p in d.get('items', []) if '$1' in (p.get('body') or '')), ''))";}
until_true 45'[ -n "$(mb_post_of "a PrivaPub note for the magazine $run")" ]'&& ok "alice's note lands in the magazine as a microblog post"|| ko "PrivaPub note missing from the Mbin magazine"
p_note_on_mb=$(mb_post_of "a PrivaPub note for the magazine $run")
echo" comments"
mb POST "posts/$p_note_on_mb/comments""{\"body\":\"an Mbin comment on PrivaPub $run\",\"lang\":\"en\"}" >/dev/null
until_true 45'[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$p_note_id/context" | j "print(any(\"an Mbin comment on PrivaPub\" in s[\"content\"] for s in d[\"descendants\"]))")" = "True" ]'\
&& ok "Mbin's comment threads under alice's note"|| ko "Mbin's comment missing on PrivaPub"
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses -d "status=a PrivaPub comment on Mbin $run&in_reply_to_id=$books_thread_on_p&visibility=public"
until_true 45'[ "$(mb GET "entry/$books_thread/comments" | j "print(any(\"a PrivaPub comment on Mbin\" in (c.get(\"body\") or \"\") for c in d.get(\"items\", [])))")" = "True" ]'\
&& ok "alice's reply becomes a comment on the Mbin thread"|| ko "PrivaPub reply missing as an Mbin comment"
echo" votes"
p_counts(){ curl -s -H "$PH""$P/api/v1/statuses/$p_note_id"| j "print(d['favourites_count'], d['reblogs_count'], ((d.get('privapub') or {}).get('votes') or {}).get('down', 0))";}
mb PUT "post/$p_note_on_mb/favourite" >/dev/null
until_true 45'[ "$(p_counts | cut -d" " -f1)" = "1" ]'&& ok "Mbin's favourite counts as a like on PrivaPub"|| ko "Mbin's favourite not counted ($(p_counts))"
mb PUT "post/$p_note_on_mb/vote/1" >/dev/null
until_true 45'[ "$(p_counts | cut -d" " -f2)" = "1" ]'&& ok "Mbin's upvote arrives as a boost"|| ko "Mbin's upvote not counted ($(p_counts))"
# (Mbin keeps downvotes to itself, and sends nothing when an upvote is taken back: its vote listener only announces an
# upvote, VoteHandleSubscriber::onVote)
curl -s -o /dev/null -X POST -H "$PH""$P/api/v1/statuses/$books_thread_on_p/favourite"
&& ok "alice's like is a favourite on Mbin"|| ko "alice's like not counted on Mbin ($(mb GET "entry/$books_thread"| j "print(d[\"favourites\"], d[\"uv\"])"))"
curl -s -o /dev/null -X POST -H "$PH""$P/api/v1/statuses/$books_thread_on_p/reblog"
&& ok "alice's boost is an upvote on Mbin"|| ko "alice's boost not counted on Mbin ($(mb GET "entry/$books_thread"| j "print(d[\"favourites\"], d[\"uv\"])"))"
# Mbin reads a report's reason from its summary alone (owner decision 2026-10-06): alice's report of mbuser's thread and
# comment leaves from PrivaPub's reporter, one Flag each with alice's words, and never names her
mb POST "entry/$books_thread/comments""{\"body\":\"an Mbin comment to report $run\",\"lang\":\"en\"}" >/dev/null
mb_comment_on_p(){ curl -s -H "$PH""$P/api/v1/statuses/$books_thread_on_p/context"| j "print(next((s['id'] for s in d['descendants'] if 'an Mbin comment to report $run' in s['content']), ''))";}
until_true 45'[ -n "$(mb_comment_on_p)" ]'&& ok "mbuser's comment to report reaches PrivaPub"|| ko "mbuser's comment never reached PrivaPub"
&& ok "PrivaPub says alice's report was forwarded"|| ko "PrivaPub did not forward alice's report"
mb_reports(){ mb_sql "select count(*) from report r join \"user\" u on u.id = r.reporting_id where r.reason = '$reason' and u.ap_profile_id = '$P_REPORTER' and r.$1";}
until_true 45'[ "$(mb_reports "entry_id is not null and r.entry_comment_id is null")" = "1" ]'&& ok "Mbin keeps the thread's report from PrivaPub's reporter, with alice's words"|| ko "Mbin kept no report of the thread with alice's words"
until_true 45'[ "$(mb_reports "entry_comment_id is not null")" = "1" ]'&& ok "Mbin keeps the comment's report from PrivaPub's reporter, with alice's words"|| ko "Mbin kept no report of the comment with alice's words"
["$(mb_sql "select count(*) from report r join \"user\" u on u.id = r.reporting_id where u.ap_profile_id like '%alice_mbin%'")"="0"]\
&& ok "no report on Mbin names alice"|| ko "a report on Mbin names alice"
&& ok "a moderator's removal reaches PrivaPub"|| ko "a moderator's removal did not reach PrivaPub"
echo" unfollow"
curl -s -o /dev/null -X POST -H "$PH""$P/api/v1/accounts/$books_on_p/unfollow"
until_true 45'[ "$(mb_sql "select count(*) from magazine_subscription s join \"user\" u on u.id = s.user_id where u.ap_profile_id like '"'%alice_mbin%'"' and s.magazine_id = $books_on_mb")" = "0" ]'\
&& ok "alice's unfollow reaches the Mbin magazine"|| ko "the Mbin magazine still counts alice"