fix: preserve discord unicode channel labels · openclaw/openclaw@d419bcf
steipete
·
2026-05-02
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -37,6 +37,7 @@ Docs: https://docs.openclaw.ai
|
37 | 37 | - Proxy/audio: convert standard `FormData` bodies before proxy-backed undici fetches, so audio transcription and multipart uploads no longer send `[object FormData]` when `HTTP_PROXY` or `HTTPS_PROXY` is configured. Fixes #48554. Thanks @dco5. |
38 | 38 | - Discord: allow explicitly configured ack reactions in tool-only guild channels while keeping automatic lifecycle/status reactions suppressed. Fixes #74922. Thanks @samvilian and @BlueBirdBack. |
39 | 39 | - Discord: preserve attachment and sticker filenames when saving inbound media, so agents can see human-readable file names instead of only UUID-based paths. Fixes #59744. Thanks @xela92 and @rockcent. |
| 40 | +- Discord: preserve non-ASCII channel names in session display labels while keeping allowlist matching on the existing ASCII slug contract. Thanks @swjeong9. |
40 | 41 | - Gateway/diagnostics: include a bounded redacted startup error message in stability bundles, so crash-loop reports identify the failing plugin or contract without exposing secrets. Refs #75797. Thanks @ymebosma. |
41 | 42 | - Gateway/pricing: abort in-flight model pricing catalog fetches when Gateway shutdown stops the refresh loop, and avoid post-stop cache writes or refresh timers. Fixes #72208. Thanks @rzcq. |
42 | 43 | - Codex/app-server: make startup retry cleanup ownership-aware so concurrent Codex lanes cannot close another lane's freshly restarted shared app-server client. Thanks @vincentkoc. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -8,7 +8,7 @@ import {
|
8 | 8 | type ComponentInteractionContext, |
9 | 9 | type DiscordChannelContext, |
10 | 10 | } from "./agent-components.types.js"; |
11 | | -import { normalizeDiscordSlug } from "./allow-list.js"; |
| 11 | +import { normalizeDiscordDisplaySlug, normalizeDiscordSlug } from "./allow-list.js"; |
12 | 12 | import { resolveDiscordChannelInfoSafe } from "./channel-access.js"; |
13 | 13 | |
14 | 14 | function formatUsername(user: { username: string; discriminator?: string | null }): string { |
@@ -72,6 +72,7 @@ export function resolveDiscordChannelContext(
|
72 | 72 | const channelInfo = resolveDiscordChannelInfoSafe(channel); |
73 | 73 | const channelName = channelInfo.name; |
74 | 74 | const channelSlug = channelName ? normalizeDiscordSlug(channelName) : ""; |
| 75 | +const displayChannelSlug = channelName ? normalizeDiscordDisplaySlug(channelName) : ""; |
75 | 76 | const channelType = channelInfo.type; |
76 | 77 | const isThread = isThreadChannelType(channelType); |
77 | 78 | |
@@ -86,7 +87,16 @@ export function resolveDiscordChannelContext(
|
86 | 87 | } |
87 | 88 | } |
88 | 89 | |
89 | | -return { channelName, channelSlug, channelType, isThread, parentId, parentName, parentSlug }; |
| 90 | +return { |
| 91 | + channelName, |
| 92 | + channelSlug, |
| 93 | + displayChannelSlug, |
| 94 | + channelType, |
| 95 | + isThread, |
| 96 | + parentId, |
| 97 | + parentName, |
| 98 | + parentSlug, |
| 99 | +}; |
90 | 100 | } |
91 | 101 | |
92 | 102 | export async function resolveComponentInteractionContext(params: { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -129,8 +129,8 @@ export async function dispatchDiscordComponentEvent(params: {
|
129 | 129 | const senderUsername = interactionCtx.user.username; |
130 | 130 | const senderTag = formatDiscordUserTag(interactionCtx.user); |
131 | 131 | const groupChannel = |
132 | | -!interactionCtx.isDirectMessage && channelCtx.channelSlug |
133 | | - ? `#${channelCtx.channelSlug}` |
| 132 | +!interactionCtx.isDirectMessage && channelCtx.displayChannelSlug |
| 133 | + ? `#${channelCtx.displayChannelSlug}` |
134 | 134 | : undefined; |
135 | 135 | const groupSubject = interactionCtx.isDirectMessage ? undefined : groupChannel; |
136 | 136 | const channelConfig = resolveDiscordChannelConfigWithFallback({ |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -26,6 +26,7 @@ export type AgentComponentInteraction = AgentComponentMessageInteraction | Modal
|
26 | 26 | export type DiscordChannelContext = { |
27 | 27 | channelName: string | undefined; |
28 | 28 | channelSlug: string; |
| 29 | +displayChannelSlug: string; |
29 | 30 | channelType: number | undefined; |
30 | 31 | isThread: boolean; |
31 | 32 | parentId: string | undefined; |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { describe, expect, it } from "vitest"; |
| 2 | +import { normalizeDiscordDisplaySlug, normalizeDiscordSlug } from "./allow-list.js"; |
| 3 | + |
| 4 | +describe("discord slug normalization", () => { |
| 5 | +it("keeps config slugs ASCII-only", () => { |
| 6 | +expect(normalizeDiscordSlug("\uC2E4\uD5D8")).toBe(""); |
| 7 | +expect(normalizeDiscordSlug("baseline-\uAC80\uC99D")).toBe("baseline"); |
| 8 | +}); |
| 9 | + |
| 10 | +it("preserves Unicode in display slugs", () => { |
| 11 | +expect(normalizeDiscordDisplaySlug("\uC2E4\uD5D8")).toBe("\uC2E4\uD5D8"); |
| 12 | +expect(normalizeDiscordDisplaySlug("baseline-\uAC80\uC99D")).toBe("baseline-\uAC80\uC99D"); |
| 13 | +}); |
| 14 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -94,6 +94,16 @@ export function normalizeDiscordSlug(value: string) {
|
94 | 94 | .replace(/^-+|-+$/g, ""); |
95 | 95 | } |
96 | 96 | |
| 97 | +export function normalizeDiscordDisplaySlug(value: string) { |
| 98 | +return normalizeLowercaseStringOrEmpty(value) |
| 99 | +.normalize("NFC") |
| 100 | +.replace(/^#/, "") |
| 101 | +.replace(/[\s_]+/g, "-") |
| 102 | +.replace(/[^\p{L}\p{M}\p{N}-]+/gu, "-") |
| 103 | +.replace(/-{2,}/g, "-") |
| 104 | +.replace(/^-+|-+$/g, ""); |
| 105 | +} |
| 106 | + |
97 | 107 | function resolveDiscordAllowListNameMatch( |
98 | 108 | list: DiscordAllowList, |
99 | 109 | candidate: { name?: string; tag?: string }, |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { describe, expect, it } from "vitest"; |
| 2 | +import { resolveDiscordPreflightChannelContext } from "./message-handler.preflight-channel-context.js"; |
| 3 | + |
| 4 | +describe("resolveDiscordPreflightChannelContext", () => { |
| 5 | +it("uses Unicode channel names for display without changing config matching slugs", () => { |
| 6 | +const context = resolveDiscordPreflightChannelContext({ |
| 7 | +isGuildMessage: true, |
| 8 | +messageChannelId: "channel-1", |
| 9 | +channelName: "\uC2E4\uD5D8", |
| 10 | +guildName: "Guild", |
| 11 | +guildInfo: null, |
| 12 | +threadChannel: undefined, |
| 13 | +}); |
| 14 | + |
| 15 | +expect(context.configChannelSlug).toBe(""); |
| 16 | +expect(context.displayChannelSlug).toBe("\uC2E4\uD5D8"); |
| 17 | +}); |
| 18 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | 1 | import { |
| 2 | +normalizeDiscordDisplaySlug, |
2 | 3 | normalizeDiscordSlug, |
3 | 4 | resolveDiscordChannelConfigWithFallback, |
4 | 5 | type DiscordGuildEntryResolved, |
@@ -19,7 +20,9 @@ export function resolveDiscordPreflightChannelContext(params: {
|
19 | 20 | const configChannelName = params.threadParentName ?? params.channelName; |
20 | 21 | const configChannelSlug = configChannelName ? normalizeDiscordSlug(configChannelName) : ""; |
21 | 22 | const displayChannelName = threadName ?? params.channelName; |
22 | | -const displayChannelSlug = displayChannelName ? normalizeDiscordSlug(displayChannelName) : ""; |
| 23 | +const displayChannelSlug = displayChannelName |
| 24 | + ? normalizeDiscordDisplaySlug(displayChannelName) |
| 25 | + : ""; |
23 | 26 | const guildSlug = |
24 | 27 | params.guildInfo?.slug || (params.guildName ? normalizeDiscordSlug(params.guildName) : ""); |
25 | 28 | |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。