Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion plugins/platforms/line/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@
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).
Expand Down Expand Up @@ -968,7 +983,7 @@ async def _handle_message_event(self, event: Dict[str, Any]) -> None:

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,
Expand Down
33 changes: 33 additions & 0 deletions tests/gateway/test_line_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,3 +641,36 @@ def test_get_chat_info_infers_type_from_prefix(self, monkeypatch):
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
Loading