Runtime environment
LangBot v4.10.x / latest, Docker deployment, Community Edition, QQ Official Bot API (qqofficial).
Observed when receiving QQ Official group-at messages containing QQ emoji / face tags in the raw inbound content field.
Deployment version
Community Edition
Exception
QQ Official inbound messages may include QQ emoji / face markup in the raw text content, for example:
@justbot 欢迎我<faceType=1,faceId="300",ext="eyJ0ZXh0Ijoi6IOW5LiJ5pakIn0=">
The ext payload is base64-encoded JSON. Decoding this example gives:
Expected behavior:
The QQ Official adapter should normalize this platform-specific face tag before the message enters LangBot's generic message chain / model context. For example:
or, at minimum, remove or replace the raw tag with a safe placeholder such as:
Actual behavior:
The current legacy QQ Official converter appears to pass the raw inbound text directly into Plain:
yiri_msg_list.append(platform_message.Plain(text=message))
As a result, the model can receive the raw platform tag as ordinary user text:
@HENU_Bot 欢迎我<faceType=1,faceId="300",ext="...">
This can cause the model to interpret or discuss the implementation detail (faceType, faceId, ext, base64 payload) instead of responding naturally to the user message.
Related location:
src/langbot/pkg/platform/sources/qqofficial.py
Relevant path:
QQOfficialEventConverter.target2yiri() passes event.content to QQOfficialMessageConverter.target2yiri().
QQOfficialMessageConverter.target2yiri() appends the whole message as Plain(text=message).
- For
GROUP_AT_MESSAGE_CREATE / AT_MESSAGE_CREATE, LangBot inserts a synthetic At(target='justbot'), but the original leading textual mention can remain in the raw content.
Suggested fix direction:
Normalize QQ Official inbound text in the adapter-level converter before creating Plain text.
Example approach:
import base64
import json
import re
_QQ_FACE_RE = re.compile(r'<faceType=\d+,faceId="[^"]+",ext="([^"]*)">')
_LEADING_BOT_MENTION_RE = re.compile(r'^\s*@\S+\s*')
def _decode_qq_face_ext(ext: str) -> str:
try:
padded = ext + "=" * ((4 - len(ext) % 4) % 4)
payload = json.loads(base64.b64decode(padded).decode("utf-8"))
text = str(payload.get("text") or "").strip()
return f"[{text}]" if text else "[表情]"
except Exception:
return "[表情]"
def _normalize_qq_official_text(message: str, strip_leading_mention: bool = False) -> str:
text = str(message or "")
text = _QQ_FACE_RE.sub(lambda m: _decode_qq_face_ext(m.group(1)), text)
if strip_leading_mention:
text = _LEADING_BOT_MENTION_RE.sub("", text, count=1)
return text.strip()
Then use the normalized text in QQOfficialMessageConverter.target2yiri() before appending Plain.
For group-at events, it may also be helpful to strip the leading textual bot mention because LangBot already inserts a synthetic At(target='justbot') for those events.
Reproduction steps
- Configure a QQ Official bot in LangBot.
- Send a group-at message containing a QQ emoji / face tag, such as:
@Bot 欢迎我<faceType=1,faceId="300",ext="eyJ0ZXh0Ijoi6IOW5LiJ5pakIn0=">
- Observe the message that enters the model / conversation context.
- The raw
<faceType=...> tag can appear as plain text instead of a normalized emoji label such as [胖三斤] or [表情].
Enabled plugins
This issue is adapter-level and can be observed before plugin business logic. A plugin-assisted chat made it visible because the model responded to the raw faceType / ext text, but the normalization should happen in the QQ Official adapter rather than in individual plugins.
Runtime environment
LangBot v4.10.x / latest, Docker deployment, Community Edition, QQ Official Bot API (
qqofficial).Observed when receiving QQ Official group-at messages containing QQ emoji / face tags in the raw inbound
contentfield.Deployment version
Community Edition
Exception
QQ Official inbound messages may include QQ emoji / face markup in the raw text content, for example:
The
extpayload is base64-encoded JSON. Decoding this example gives:{"text":"胖三斤"}Expected behavior:
The QQ Official adapter should normalize this platform-specific face tag before the message enters LangBot's generic message chain / model context. For example:
or, at minimum, remove or replace the raw tag with a safe placeholder such as:
Actual behavior:
The current legacy QQ Official converter appears to pass the raw inbound text directly into
Plain:As a result, the model can receive the raw platform tag as ordinary user text:
This can cause the model to interpret or discuss the implementation detail (
faceType,faceId,ext, base64 payload) instead of responding naturally to the user message.Related location:
Relevant path:
QQOfficialEventConverter.target2yiri()passesevent.contenttoQQOfficialMessageConverter.target2yiri().QQOfficialMessageConverter.target2yiri()appends the whole message asPlain(text=message).GROUP_AT_MESSAGE_CREATE/AT_MESSAGE_CREATE, LangBot inserts a syntheticAt(target='justbot'), but the original leading textual mention can remain in the rawcontent.Suggested fix direction:
Normalize QQ Official inbound text in the adapter-level converter before creating
Plaintext.Example approach:
Then use the normalized text in
QQOfficialMessageConverter.target2yiri()before appendingPlain.For group-at events, it may also be helpful to strip the leading textual bot mention because LangBot already inserts a synthetic
At(target='justbot')for those events.Reproduction steps
<faceType=...>tag can appear as plain text instead of a normalized emoji label such as[胖三斤]or[表情].Enabled plugins
This issue is adapter-level and can be observed before plugin business logic. A plugin-assisted chat made it visible because the model responded to the raw
faceType/exttext, but the normalization should happen in the QQ Official adapter rather than in individual plugins.