Skip to content

feat(reply): 表情回应支持 Emoji#130

Merged
xiaocaoooo merged 1 commit into
mainfrom
feature/enhance-reply-expressions
Jun 13, 2026
Merged

feat(reply): 表情回应支持 Emoji#130
xiaocaoooo merged 1 commit into
mainfrom
feature/enhance-reply-expressions

Conversation

@xiaocaoooo

@xiaocaoooo xiaocaoooo commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced reply commands to support multiple emojis and face responses in a single operation
    • Batch configuration of emoji-based reply mappings
  • Improvements

    • Improved response display with automatic emoji rendering and fallback formatting when unavailable

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR enhances the reply feature to accept multiple emoji and face ID targets in a single command. It introduces two emoji conversion utilities—emojiToFaceId and faceIdToEmoji—and integrates them into the reply command handler to extract, normalize, and batch-configure reply mappings.

Changes

Emoji Reply Feature Enhancement

Layer / File(s) Summary
Feature specification and plan
.narrafork/plan-flickering-foraging-matsumoto.md
Specification document defines the emoji/faceId bidirectional conversion algorithm, batch command parsing with multi-emoji detection, list output rendering with fallback display, and compatibility/robustness requirements.
Emoji and FaceId conversion utilities
src/utils/index.ts
emojiToFaceId strips variation selectors, converts code points to a concatenated 32-bit BigInt, and returns the decimal string. faceIdToEmoji reconstructs code points from the BigInt and returns the emoji string, or a bracketed fallback [ID: ...] on error. Replaces removed extractXmlTag utility.
Reply command with emoji batch support
src/features/reply/feature.ts
Updated /回应 on/off parsing to extract both explicit face message IDs and Emoji_Presentation characters, convert emojis to e:-prefixed IDs, de-duplicate, and batch add/remove mappings. Updated /回应 list rendering to convert e:-prefixed stored IDs back to emojis; non-prefixed values remain numeric face IDs. Updated confirmation messages and logging to enumerate all selected targets instead of the first target only.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • Parallel-SEKAI/AmiaBot#82: Earlier implementation of the reply feature module and DB-backed user reply faces, which this PR extends with emoji support and batch operations.

Poem

🐰 Emojis hop into the reply fold,
From glyph to ID, a story told,
Batch by batch, they find their place,
Each smile wears a numbered face,
The feature glows—my fur stands bold! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(reply): 表情回应支持 Emoji' directly and specifically describes the main change: adding emoji support to the reply/response feature, which aligns with all three modified files implementing emoji-to-faceId conversion and batch emoji handling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/enhance-reply-expressions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/reply/feature.ts`:
- Around line 66-68: The loop that calls addReplyFace per item can leave partial
state on failure; change to perform the work atomically by adding a batch API
and using a transaction: implement a new bulkAddReplyFaces(userId, faceIds) (and
bulkRemoveReplyFaces for the removal case) that performs the inserts/deletes
inside a single DB transaction/atomic operation, then replace the per-item
awaited loops in feature.ts (the for...await loops that call addReplyFace) with
a single call to the new bulk function; ensure the bulk functions return a clear
error on failure and roll back the transaction so no partial writes remain.
- Around line 58-60: The emoji extraction using
message.content.match(/\p{Emoji_Presentation}/gu) is missing sequences with
variation selectors and ZWJ joins; update the regex used where emojis and
emojiIds are built (the emojis variable and its mapping to emojiIds that call
emojiToFaceId) to match full emoji sequences instead, e.g. use a regex based on
\p{Extended_Pictographic} that captures optional U+FE0F and
ZWJ+Extended_Pictographic runs (conceptually:
/\p{Extended_Pictographic}(?:\uFE0F|\u200D\p{Extended_Pictographic})*/gu), and
apply the same replacement at the other site that builds emojis/emojiIds so that
emojiToFaceId receives whole emoji graphemes.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6c0a524-cb69-4199-8833-f327a71f4b4e

📥 Commits

Reviewing files that changed from the base of the PR and between c490136 and 1a78fe1.

📒 Files selected for processing (3)
  • .narrafork/plan-flickering-foraging-matsumoto.md
  • src/features/reply/feature.ts
  • src/utils/index.ts

Comment on lines +58 to +60
const emojis =
message.content.match(/\p{Emoji_Presentation}/gu) || [];
const emojiIds = emojis.map((emoji) => `e:${emojiToFaceId(emoji)}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In JavaScript Unicode property escapes, what does \p{Emoji_Presentation} exclude compared with \p{Extended_Pictographic}, and does it match emojis like "❤️" and ZWJ sequences such as "👨‍👩‍👧‍👦"?

💡 Result:

The \p{Emoji_Presentation} property and \p{Extended_Pictographic} property serve different purposes within the Unicode Standard, with \p{Extended_Pictographic} being a significantly broader superset [1]. \p{Emoji_Presentation} matches only those characters that are rendered as colorful emoji by default, without needing a variation selector (like U+FE0F) [1][2]. In contrast, \p{Extended_Pictographic} includes \p{Emoji_Presentation} and adds several other categories [1]: 1. Characters that are pictographic in nature but default to text presentation (and require a variation selector to render as emoji, such as many symbol characters) [3][1][4]. 2. Reserved code points within emoji blocks, intended for future emoji, which allows regex patterns using this property to be more future-proof [1]. 3. Other miscellaneous pictographic symbols that do not have the Emoji_Presentation property [3][1]. Regarding your specific examples: "❤️" (U+2764 U+FE0F) The sequence "❤️" generally matches \p{Emoji_Presentation} only if the engine considers the base character "❤" (U+2764) to have the property. However, U+2764 actually has Emoji_Presentation=No, meaning it defaults to text presentation [5][4]. When combined with the variation selector U+FE0F, it becomes an emoji presentation sequence [2][4]. Because \p{Emoji_Presentation} is a binary property of individual code points, it does not match this sequence as a whole; it matches the individual base character only if that character is marked as Emoji_Presentation=Yes. Consequently, \p{Emoji_Presentation} is typically insufficient for matching full emoji sequences [6][7]. ZWJ sequences (e.g., "👨‍👩‍👧‍👦") These sequences are composed of multiple code points joined by the Zero Width Joiner (U+200D) [7][8]. \p{Emoji_Presentation} is a property of individual code points, not sequences [1][6]. Therefore, it will not match a ZWJ sequence as a single match; it will only match the individual code points within the sequence that happen to have the Emoji_Presentation property [7]. To match complex ZWJ sequences or emoji presentation sequences correctly, standard regex property escapes are generally inadequate, and dedicated libraries like emoji-regex are recommended [6][7][9].

Citations:


Fix emoji extraction to handle variation-selector and ZWJ emoji sequences
The current /\p{Emoji_Presentation}/gu match is limited to code points with default emoji presentation, so it can miss emojis like "❤️" (❤ + FE0F) and won’t treat ZWJ emojis (e.g., "👨‍👩‍👧‍👦") as single units—causing missed/fragmented emoji IDs in the on/off logic (lines 58-60; also 106-108).

💡 Suggested fix
-const emojis = message.content.match(/\p{Emoji_Presentation}/gu) || [];
+const emojis =
+  message.content.match(
+    /\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?)*/gu
+  ) ?? [];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const emojis =
message.content.match(/\p{Emoji_Presentation}/gu) || [];
const emojiIds = emojis.map((emoji) => `e:${emojiToFaceId(emoji)}`);
const emojis =
message.content.match(
/\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?)*/gu
) ?? [];
const emojiIds = emojis.map((emoji) => `e:${emojiToFaceId(emoji)}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/reply/feature.ts` around lines 58 - 60, The emoji extraction
using message.content.match(/\p{Emoji_Presentation}/gu) is missing sequences
with variation selectors and ZWJ joins; update the regex used where emojis and
emojiIds are built (the emojis variable and its mapping to emojiIds that call
emojiToFaceId) to match full emoji sequences instead, e.g. use a regex based on
\p{Extended_Pictographic} that captures optional U+FE0F and
ZWJ+Extended_Pictographic runs (conceptually:
/\p{Extended_Pictographic}(?:\uFE0F|\u200D\p{Extended_Pictographic})*/gu), and
apply the same replacement at the other site that builds emojis/emojiIds so that
emojiToFaceId receives whole emoji graphemes.

Comment on lines +66 to +68
for (const faceId of finalFaceIds) {
await addReplyFace(message.userId, faceId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Batch on/off can leave partial persisted state on mid-loop failure.

These per-item awaited writes are non-atomic; if one write fails, earlier inserts/deletes stay committed, producing inconsistent final state versus the failure response.

💡 Suggested direction
- for (const faceId of finalFaceIds) {
-   await addReplyFace(message.userId, faceId);
- }
+ await addReplyFacesBatch(message.userId, finalFaceIds);

- for (const faceId of finalFaceIds) {
-   await removeReplyFace(message.userId, faceId);
- }
+ await removeReplyFacesBatch(message.userId, finalFaceIds);

Also applies to: 114-116

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/reply/feature.ts` around lines 66 - 68, The loop that calls
addReplyFace per item can leave partial state on failure; change to perform the
work atomically by adding a batch API and using a transaction: implement a new
bulkAddReplyFaces(userId, faceIds) (and bulkRemoveReplyFaces for the removal
case) that performs the inserts/deletes inside a single DB transaction/atomic
operation, then replace the per-item awaited loops in feature.ts (the
for...await loops that call addReplyFace) with a single call to the new bulk
function; ensure the bulk functions return a clear error on failure and roll
back the transaction so no partial writes remain.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 3 file(s) based on 2 unresolved review comments.

Files modified:

  • .npmrc
  • src/features/reply/db.ts
  • src/features/reply/feature.ts

Commit: 41b34daa07f201972307b6e88a35de8835cfbb73

The changes have been pushed to the feature/enhance-reply-expressions branch.

Time taken: 7m 22s

@xiaocaoooo xiaocaoooo merged commit bbbb1d4 into main Jun 13, 2026
8 of 10 checks passed
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