Skip to content

Add /chat/pin endpoint to pin and unpin messages#336

Open
rona-sh wants to merge 1 commit into
asternic:mainfrom
rona-sh:feat/chat-pin-message
Open

Add /chat/pin endpoint to pin and unpin messages#336
rona-sh wants to merge 1 commit into
asternic:mainfrom
rona-sh:feat/chat-pin-message

Conversation

@rona-sh

@rona-sh rona-sh commented Jun 18, 2026

Copy link
Copy Markdown

Adds an authenticated POST /chat/pin endpoint that pins and unpins an existing message in individual chats and groups, mapping to whatsmeow's PinInChatMessage.
This PR also bumps go.mau.fi/whatsmeow to latest as it is required for correct PinInChatMessage send behavior (older versions send the stanza without the edit attribute and fail silently).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new /chat/pin endpoint to pin or unpin messages in individual or group chats, complete with documentation in API.md, route registration, handler implementation in handlers.go, and integration tests in pin_test.go. The reviewer recommended supporting the me: prefix convention on message IDs to allow users to pin or unpin their own messages, utilizing the idiomatic !senderJID.IsEmpty() check, and updating the response payload, logging, and documentation to reflect this prefix change.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread handlers.go
Comment on lines +4287 to +4294
key := &waCommon.MessageKey{
RemoteJID: proto.String(chatJID.String()),
FromMe: proto.Bool(false),
ID: proto.String(t.Id),
}
if senderJID.String() != "" {
key.Participant = proto.String(senderJID.String())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To allow users to pin or unpin their own messages, we should support the me: prefix convention on the message ID (similar to how it is handled in the React endpoint). Additionally, we should use the idiomatic !senderJID.IsEmpty() check instead of senderJID.String() != "".

		fromMe := false
		msgID := t.Id
		if strings.HasPrefix(msgID, "me:") {
			fromMe = true
			msgID = msgID[len("me:"):]
		}

		key := &waCommon.MessageKey{
			RemoteJID: proto.String(chatJID.String()),
			FromMe:    proto.Bool(fromMe),
			ID:        proto.String(msgID),
		}
		if !senderJID.IsEmpty() {
			key.Participant = proto.String(senderJID.String())
		}

Comment thread handlers.go
Comment on lines +4325 to +4337
response := map[string]interface{}{
"Chat": chatJID.String(),
"Id": t.Id,
"Timestamp": resp.Timestamp.UTC().Format(time.RFC3339),
}
if pin {
response["Details"] = "Message pinned"
response["DurationSeconds"] = duration
log.Info().Str("id", t.Id).Str("chat", chatJID.String()).Uint32("duration", duration).Msg("Message pinned")
} else {
response["Details"] = "Message unpinned"
log.Info().Str("id", t.Id).Str("chat", chatJID.String()).Msg("Message unpinned")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the response payload and log messages to use the stripped msgID instead of t.Id to ensure consistency with other endpoints (like React).

Suggested change
response := map[string]interface{}{
"Chat": chatJID.String(),
"Id": t.Id,
"Timestamp": resp.Timestamp.UTC().Format(time.RFC3339),
}
if pin {
response["Details"] = "Message pinned"
response["DurationSeconds"] = duration
log.Info().Str("id", t.Id).Str("chat", chatJID.String()).Uint32("duration", duration).Msg("Message pinned")
} else {
response["Details"] = "Message unpinned"
log.Info().Str("id", t.Id).Str("chat", chatJID.String()).Msg("Message unpinned")
}
response := map[string]interface{}{
"Chat": chatJID.String(),
"Id": msgID,
"Timestamp": resp.Timestamp.UTC().Format(time.RFC3339),
}
if pin {
response["Details"] = "Message pinned"
response["DurationSeconds"] = duration
log.Info().Str("id", msgID).Str("chat", chatJID.String()).Uint32("duration", duration).Msg("Message pinned")
} else {
response["Details"] = "Message unpinned"
log.Info().Str("id", msgID).Str("chat", chatJID.String()).Msg("Message unpinned")
}

Comment thread API.md
|---|---|---|---|
| `Chat` | Yes | string | Target chat JID. For groups, the group JID ending in `@g.us`. |
| `Sender` | Required for groups | string | JID of the original message sender. Required for group messages because the message key includes the participant. Optional for one-to-one chats. |
| `Id` | Yes | string | WhatsApp message ID of the message to pin or unpin. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To document the ability to pin/unpin your own messages using the me: prefix, we should update the description of the Id field.

Suggested change
| `Id` | Yes | string | WhatsApp message ID of the message to pin or unpin. |
| `Id` | Yes | string | WhatsApp message ID of the message to pin or unpin. Prefix with `me:` to pin/unpin your own messages (e.g., `me:3EB0ABCDEF1234567890`). |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant