diff --git a/line-openapi b/line-openapi index 6e4c2a2a1..99df56b76 160000 --- a/line-openapi +++ b/line-openapi @@ -1 +1 @@ -Subproject commit 6e4c2a2a1b7b059ea3920456d4f88d03fd8ef0bb +Subproject commit 99df56b76bf1b8a8268456fa36c2d1538b4c086c diff --git a/linebot/v3/webhooks/__init__.py b/linebot/v3/webhooks/__init__.py index 2e5710d97..ba22c3c4b 100644 --- a/linebot/v3/webhooks/__init__.py +++ b/linebot/v3/webhooks/__init__.py @@ -72,6 +72,7 @@ from linebot.v3.webhooks.models.mention import Mention from linebot.v3.webhooks.models.mentionee import Mentionee from linebot.v3.webhooks.models.message_content import MessageContent +from linebot.v3.webhooks.models.message_edited_event import MessageEditedEvent from linebot.v3.webhooks.models.message_event import MessageEvent from linebot.v3.webhooks.models.module_content import ModuleContent from linebot.v3.webhooks.models.module_event import ModuleEvent diff --git a/linebot/v3/webhooks/models/__init__.py b/linebot/v3/webhooks/models/__init__.py index 5fd92f106..dd5a98bb6 100644 --- a/linebot/v3/webhooks/models/__init__.py +++ b/linebot/v3/webhooks/models/__init__.py @@ -53,6 +53,7 @@ from linebot.v3.webhooks.models.mention import Mention from linebot.v3.webhooks.models.mentionee import Mentionee from linebot.v3.webhooks.models.message_content import MessageContent +from linebot.v3.webhooks.models.message_edited_event import MessageEditedEvent from linebot.v3.webhooks.models.message_event import MessageEvent from linebot.v3.webhooks.models.module_content import ModuleContent from linebot.v3.webhooks.models.module_event import ModuleEvent diff --git a/linebot/v3/webhooks/models/event.py b/linebot/v3/webhooks/models/event.py index 91db398c5..dbe5a29c7 100644 --- a/linebot/v3/webhooks/models/event.py +++ b/linebot/v3/webhooks/models/event.py @@ -62,6 +62,7 @@ class Config: 'memberLeft': 'MemberLeftEvent', 'membership': 'MembershipEvent', 'message': 'MessageEvent', + 'messageEdited': 'MessageEditedEvent', 'module': 'ModuleEvent', 'postback': 'PostbackEvent', 'unfollow': 'UnfollowEvent', @@ -87,7 +88,7 @@ def to_json(self) -> str: return json.dumps(self.to_dict()) @classmethod - def from_json(cls, json_str: str) -> Union(AccountLinkEvent, ActivatedEvent, BeaconEvent, BotResumedEvent, BotSuspendedEvent, DeactivatedEvent, FollowEvent, JoinEvent, LeaveEvent, MemberJoinedEvent, MemberLeftEvent, MembershipEvent, MessageEvent, ModuleEvent, PnpDeliveryCompletionEvent, PostbackEvent, UnfollowEvent, UnsendEvent, VideoPlayCompleteEvent): + def from_json(cls, json_str: str) -> Union(AccountLinkEvent, ActivatedEvent, BeaconEvent, BotResumedEvent, BotSuspendedEvent, DeactivatedEvent, FollowEvent, JoinEvent, LeaveEvent, MemberJoinedEvent, MemberLeftEvent, MembershipEvent, MessageEditedEvent, MessageEvent, ModuleEvent, PnpDeliveryCompletionEvent, PostbackEvent, UnfollowEvent, UnsendEvent, VideoPlayCompleteEvent): """Create an instance of Event from a JSON string""" return cls.from_dict(json.loads(json_str)) @@ -106,7 +107,7 @@ def to_dict(self): return _dict @classmethod - def from_dict(cls, obj: dict) -> Union(AccountLinkEvent, ActivatedEvent, BeaconEvent, BotResumedEvent, BotSuspendedEvent, DeactivatedEvent, FollowEvent, JoinEvent, LeaveEvent, MemberJoinedEvent, MemberLeftEvent, MembershipEvent, MessageEvent, ModuleEvent, PnpDeliveryCompletionEvent, PostbackEvent, UnfollowEvent, UnsendEvent, VideoPlayCompleteEvent): + def from_dict(cls, obj: dict) -> Union(AccountLinkEvent, ActivatedEvent, BeaconEvent, BotResumedEvent, BotSuspendedEvent, DeactivatedEvent, FollowEvent, JoinEvent, LeaveEvent, MemberJoinedEvent, MemberLeftEvent, MembershipEvent, MessageEditedEvent, MessageEvent, ModuleEvent, PnpDeliveryCompletionEvent, PostbackEvent, UnfollowEvent, UnsendEvent, VideoPlayCompleteEvent): """Create an instance of Event from a dict""" # look up the object type based on discriminator mapping object_type = cls.get_discriminator_value(obj) diff --git a/linebot/v3/webhooks/models/message_edited_event.py b/linebot/v3/webhooks/models/message_edited_event.py new file mode 100644 index 000000000..7d6b9efd2 --- /dev/null +++ b/linebot/v3/webhooks/models/message_edited_event.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + Webhook Type Definition + + Webhook event definition of the LINE Messaging API # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic.v1 import Field, StrictStr +from linebot.v3.webhooks.models.delivery_context import DeliveryContext +from linebot.v3.webhooks.models.event import Event +from linebot.v3.webhooks.models.event_mode import EventMode +from linebot.v3.webhooks.models.message_content import MessageContent +from linebot.v3.webhooks.models.source import Source + +class MessageEditedEvent(Event): + """ + Webhook event object which contains the edited message. This event is triggered when a user edits a text message. Currently, message editing is not available in one-on-one chats with LINE Official Accounts, so this event is only triggered in group chats. Multiple messageEdited webhook events may arrive out of order. The event with the largest timestamp represents the latest edit state. + https://developers.line.biz/en/reference/messaging-api/#edit-event + """ + reply_token: Optional[StrictStr] = Field(None, alias="replyToken") + message: MessageContent = Field(...) + type: str = "messageEdited" + + __properties = ["type", "source", "timestamp", "mode", "webhookEventId", "deliveryContext", "replyToken", "message"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> MessageEditedEvent: + """Create an instance of MessageEditedEvent from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic.v1 by calling `to_dict()` of source + if self.source: + _dict['source'] = self.source.to_dict() + # override the default output from pydantic.v1 by calling `to_dict()` of delivery_context + if self.delivery_context: + _dict['deliveryContext'] = self.delivery_context.to_dict() + # override the default output from pydantic.v1 by calling `to_dict()` of message + if self.message: + _dict['message'] = self.message.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> MessageEditedEvent: + """Create an instance of MessageEditedEvent from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return MessageEditedEvent.parse_obj(obj) + + _obj = MessageEditedEvent.parse_obj({ + "type": obj.get("type"), + "source": Source.from_dict(obj.get("source")) if obj.get("source") is not None else None, + "timestamp": obj.get("timestamp"), + "mode": obj.get("mode"), + "webhook_event_id": obj.get("webhookEventId"), + "delivery_context": DeliveryContext.from_dict(obj.get("deliveryContext")) if obj.get("deliveryContext") is not None else None, + "reply_token": obj.get("replyToken"), + "message": MessageContent.from_dict(obj.get("message")) if obj.get("message") is not None else None + }) + return _obj + diff --git a/tests/v3/test_webhook.py b/tests/v3/test_webhook.py index 3b2288e6e..515ec3fee 100644 --- a/tests/v3/test_webhook.py +++ b/tests/v3/test_webhook.py @@ -25,7 +25,7 @@ from linebot.v3.webhooks.models import ( MessageEvent, FollowEvent, UnfollowEvent, JoinEvent, LeaveEvent, PostbackEvent, BeaconEvent, AccountLinkEvent, - MemberJoinedEvent, MemberLeftEvent, + MemberJoinedEvent, MemberLeftEvent, MessageEditedEvent, VideoPlayCompleteEvent, VideoPlayComplete, UnsendEvent, UnsendDetail, TextMessageContent, ImageMessageContent, VideoMessageContent, AudioMessageContent, LocationMessageContent, StickerMessageContent, FileMessageContent, @@ -70,7 +70,7 @@ def test_parse(self): events = self.parser.parse(body, 'channel_secret') # events count - self.assertEqual(len(events), 27) + self.assertEqual(len(events), 28) # MessageEvent, UserSource, TextMessageContent self.assertIsInstance(events[0], MessageEvent) @@ -508,8 +508,25 @@ def test_parse(self): self.assertEqual(events[25].message.type, 'text') self.assertEqual(events[25].message.text, 'Hello, world') + # MessageEditedEvent, UserSource, TextMessageContent + self.assertIsInstance(events[26], MessageEditedEvent) + self.assertEqual(events[26].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') + self.assertEqual(events[26].type, 'messageEdited') + self.assertEqual(events[26].mode, 'active') + self.assertEqual(events[26].timestamp, 1462629479859) + self.assertIsInstance(events[26].source, UserSource) + self.assertEqual(events[26].source.type, 'user') + self.assertEqual(events[26].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') + self.assertEqual(events[26].webhook_event_id, 'testwebhookeventid') + self.assertIsInstance(events[26].delivery_context, DeliveryContext) + self.assertFalse(events[26].delivery_context.is_redelivery) + self.assertIsInstance(events[26].message, TextMessageContent) + self.assertEqual(events[26].message.id, '325708') + self.assertEqual(events[26].message.type, 'text') + self.assertEqual(events[26].message.text, 'Hello, edited world') + # UnknownEvent - self.assertIsInstance(events[26], UnknownEvent) + self.assertIsInstance(events[27], UnknownEvent) # TODO: richmenu switch diff --git a/tests/v3/text/webhook.json b/tests/v3/text/webhook.json index b12799dfe..ce1f42435 100644 --- a/tests/v3/text/webhook.json +++ b/tests/v3/text/webhook.json @@ -526,6 +526,26 @@ "quoteToken": "quoteToken" } }, + { + "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", + "type": "messageEdited", + "mode": "active", + "timestamp": 1462629479859, + "source": { + "type": "user", + "userId": "U206d25c2ea6bd87c17655609a1c37cb8" + }, + "webhookEventId": "testwebhookeventid", + "deliveryContext": { + "isRedelivery": false + }, + "message": { + "id": "325708", + "type": "text", + "text": "Hello, edited world", + "quoteToken": "quoteToken" + } + }, { "type": "undefined", "mode": "active",