fix(line): map inbound message types to the correct MessageType
The LINE adapter classified every non-text inbound message as `MessageType.IMAGE`, which doesn't exist on the enum — so any image, video, audio, file, sticker, or location message raised AttributeError the moment it was constructed. Beyond fixing the crash, every non-text message was being collapsed onto a single type. The gateway routes on MessageType (voice → STT, files → document handling, etc.), so misclassification silently mishandled media. Replace the inline ternary with a `_LINE_MESSAGE_TYPES` lookup that maps each LINE webhook type to its proper enum member (audio → VOICE to match how Telegram/WhatsApp treat voice notes), falling back to TEXT for unknown types. Adds regression tests covering the mapping and the old AttributeError. Co-authored-by: Sahibzada Allahyar <94376830+sahibzada-allahyar@users.noreply.github.com>
This commit is contained in:
@ -133,6 +133,21 @@ MEDIA_TOKEN_TTL_SECONDS = 1800 # 30 minutes; LINE caches the URL aggressively
|
||||
LINE_IMAGE_MAX_BYTES = 10 * 1024 * 1024 # 10 MB per LINE docs
|
||||
LINE_AV_MAX_BYTES = 200 * 1024 * 1024 # 200 MB for voice/video
|
||||
|
||||
# Map LINE webhook message types to the normalized MessageType the gateway
|
||||
# routes on. LINE has no separate "voice" type — audio messages are recorded
|
||||
# voice clips, so they map to VOICE (which the gateway sends through STT),
|
||||
# mirroring how Telegram/WhatsApp classify voice notes. Anything unknown
|
||||
# falls back to TEXT.
|
||||
_LINE_MESSAGE_TYPES = {
|
||||
"text": MessageType.TEXT,
|
||||
"image": MessageType.PHOTO,
|
||||
"video": MessageType.VIDEO,
|
||||
"audio": MessageType.VOICE,
|
||||
"file": MessageType.DOCUMENT,
|
||||
"location": MessageType.LOCATION,
|
||||
"sticker": MessageType.STICKER,
|
||||
}
|
||||
|
||||
# A 1×1 transparent PNG used as fallback video preview thumbnail when no
|
||||
# explicit preview is supplied — LINE requires ``previewImageUrl`` for
|
||||
# video messages. Sourced from the Python stdlib (no Pillow dependency).
|
||||
@ -968,7 +983,7 @@ class LineAdapter(BasePlatformAdapter):
|
||||
|
||||
event_obj = MessageEvent(
|
||||
text=text,
|
||||
message_type=MessageType.TEXT if msg_type == "text" else MessageType.IMAGE,
|
||||
message_type=_LINE_MESSAGE_TYPES.get(msg_type, MessageType.TEXT),
|
||||
source=source_obj,
|
||||
raw_message=event,
|
||||
message_id=message_id,
|
||||
|
||||
@ -641,3 +641,36 @@ class TestAdapterInit:
|
||||
assert asyncio.run(ad.get_chat_info("U123"))["type"] == "dm"
|
||||
assert asyncio.run(ad.get_chat_info("C123"))["type"] == "group"
|
||||
assert asyncio.run(ad.get_chat_info("R123"))["type"] == "channel"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Inbound message-type classification
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMessageTypeMapping:
|
||||
"""LINE webhook message types must map to the right normalized
|
||||
MessageType so the gateway routes media correctly (e.g. voice → STT,
|
||||
files → document handling). Regression guard for the old code that
|
||||
referenced the non-existent ``MessageType.IMAGE`` and collapsed every
|
||||
non-text message onto a single type."""
|
||||
|
||||
def test_image_event_not_attributeerror_regression(self):
|
||||
# The bug: MessageType.IMAGE doesn't exist on the enum.
|
||||
MessageType = _line.MessageType
|
||||
assert not hasattr(MessageType, "IMAGE")
|
||||
|
||||
def test_every_line_type_maps_to_correct_enum(self):
|
||||
MessageType = _line.MessageType
|
||||
mapping = _line._LINE_MESSAGE_TYPES
|
||||
assert mapping["text"] == MessageType.TEXT
|
||||
assert mapping["image"] == MessageType.PHOTO
|
||||
assert mapping["video"] == MessageType.VIDEO
|
||||
# LINE has no separate voice type — audio clips are voice notes.
|
||||
assert mapping["audio"] == MessageType.VOICE
|
||||
assert mapping["file"] == MessageType.DOCUMENT
|
||||
assert mapping["location"] == MessageType.LOCATION
|
||||
assert mapping["sticker"] == MessageType.STICKER
|
||||
|
||||
def test_unknown_type_falls_back_to_text(self):
|
||||
MessageType = _line.MessageType
|
||||
assert _line._LINE_MESSAGE_TYPES.get("flex", MessageType.TEXT) == MessageType.TEXT
|
||||
|
||||
Reference in New Issue
Block a user