feat(like_post): add unlike support via remove.0 array dot-notation (v1.2.0)

Unlike is now implemented: add=\$empty, remove.0=STAR (verified via Network Interceptor).
Adds optional mood parameter (default STAR). Removes the early-return error path for like=False.
SPEC.md, CLAUDE.md, CHANGELOG.md updated; Unlike offene Punkte entry removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 22:57:47 +02:00
parent 4c1e4e2c23
commit 70c2f61f05
4 changed files with 50 additions and 48 deletions
+31 -38
View File
@@ -1901,43 +1901,33 @@ def delete_circle(circle_id: str) -> str:
@mcp.tool()
def like_post(post_id: str, like: bool = True) -> str:
"""Like a wall post/activity with a STAR mood.
def like_post(post_id: str, like: bool = True, mood: str = "STAR") -> str:
"""Like or unlike a wall post/activity.
IMPORTANT: Ask the user for confirmation before calling this tool.
Note: Unlike (like=False) is not yet supported. The Family Wall API offers
no discoverable endpoint or parameter to remove a like. Passing like=False
returns an error without making any API call.
Args:
post_id: Wall post ID from get_activities (e.g. ``wall/23431854_31119189``).
like: Must be ``True``. ``False`` is reserved for future unlike support.
like: ``True`` to add a like (default), ``False`` to remove it.
mood: Mood type to set or remove. Default is ``"STAR"``.
Returns:
JSON success indicator or an error message.
JSON with the resulting like state or an error message.
"""
# Unlike is not yet supported: extensive FW_DEBUG=1 testing showed that
# wallmood with moodType="STAR" is an idempotent SET operation (not a toggle).
# Tested and ruled out: moodType variations ("NONE", "REMOVE", "DELETE", ""),
# moodStarShortcut parameter, and alternative endpoints (all return 502).
# See SPEC.md for full investigation notes.
if not like:
return json.dumps(
{"error": "Unlike is not yet supported. The unlike mechanism is unknown."},
ensure_ascii=False,
indent=2,
)
# Verified via FW_DEBUG=1:
# - Parameter 'wall_message_id': post ID as returned by get_activities
# - Parameter 'moodType': "STAR" (Family Wall's internal like type; "LIKE" is silently
# mapped to "STAR" server-side — use "STAR" directly)
# - Response a00.r.r: full wall message object with moodMap showing resulting state
params: dict[str, Any] = {
"wall_message_id": post_id,
"moodType": "STAR",
}
# Like: add=STAR, remove=$empty (idempotent set)
# Unlike: add=$empty, remove.0=STAR (array dot-notation, verified via Network Interceptor)
if like:
params: dict[str, Any] = {
"wall_message_id": post_id,
"add": mood,
"remove": "$empty",
}
else:
params = {
"wall_message_id": post_id,
"add": "$empty",
"remove.0": mood,
}
try:
data = _authenticated_call("wallmood", params)
@@ -1968,15 +1958,18 @@ def like_post(post_id: str, like: bool = True) -> str:
result: dict[str, Any] = {"liked": now_liked, "id": post_id, "author": account_id}
# Surface a warning when the like call apparently had no effect, so the
# caller can distinguish a successful like from a silent API rejection
# (e.g. rate limit, unsupported post type, or self-like restriction).
if not now_liked:
result["warning"] = (
"Like may not have been applied. "
"Possible causes: rate limit, unsupported post type (e.g. FAMILY_CREATED), "
"or self-like restriction."
)
if now_liked != like:
if like:
result["warning"] = (
"Like may not have been applied. "
"Possible causes: rate limit, unsupported post type (e.g. FAMILY_CREATED), "
"or self-like restriction."
)
else:
result["warning"] = (
"Unlike may not have been applied. "
"Possible causes: post was not liked, rate limit, or API restriction."
)
return json.dumps(result, ensure_ascii=False, indent=2)