diff --git a/linebot/models/__init__.py b/linebot/models/__init__.py index cb7e941e..e7233e80 100644 --- a/linebot/models/__init__.py +++ b/linebot/models/__init__.py @@ -48,6 +48,7 @@ MemberJoinedEvent, MemberLeftEvent, BeaconEvent, + ThingsEvent, Postback, Beacon, Link, diff --git a/linebot/models/events.py b/linebot/models/events.py index 2f570b9c..47832517 100644 --- a/linebot/models/events.py +++ b/linebot/models/events.py @@ -356,6 +356,37 @@ def __init__(self, timestamp=None, source=None, reply_token=None, link=None, **k ) +class ThingsEvent(Event): + """Webhook ThingsEvent. + + https://developers.line.biz/en/reference/messaging-api/#device-link-event + + Indicates that a LINE Things-compatible device has been linked with LINE by + a user operation. + """ + + def __init__(self, timestamp=None, source=None, reply_token=None, things=None, **kwargs): + """__init__ method. + + :param long timestamp: Time of the event in milliseconds + :param source: Source object + :type source: T <= :py:class:`linebot.models.sources.Source` + :param str reply_token: Reply token + :param things: Things object + :type things: :py:class:`linebot.models.events.Things` + :param kwargs: + """ + super(ThingsEvent, self).__init__( + timestamp=timestamp, source=source, **kwargs + ) + + self.type = 'things' + self.reply_token = reply_token + self.things = self.get_or_new_from_json_dict( + things, Things + ) + + class Postback(Base): """Postback. @@ -467,3 +498,21 @@ def __init__(self, result=None, nonce=None, **kwargs): self.result = result self.nonce = nonce + + +class Things(Base): + """Things. + + https://developers.line.biz/en/docs/line-things/develop-bot/#link-event + """ + + def __init__(self, device_id=None, type=None, **kwargs): + """__init__ method. + + :param str device_id: Device ID of the device that was linked with LINE. + :param str type: link or unlink + """ + super(Things, self).__init__(**kwargs) + + self.device_id = device_id + self.type = type diff --git a/linebot/webhook.py b/linebot/webhook.py index 415ceb1e..1fed867a 100644 --- a/linebot/webhook.py +++ b/linebot/webhook.py @@ -34,6 +34,7 @@ AccountLinkEvent, MemberJoinedEvent, MemberLeftEvent, + ThingsEvent, ) from .utils import LOGGER, PY3, safe_compare_digest @@ -150,6 +151,8 @@ def parse(self, body, signature): events.append(MemberJoinedEvent.new_from_json_dict(event)) elif event_type == 'memberLeft': events.append(MemberLeftEvent.new_from_json_dict(event)) + elif event_type == 'things': + events.append(ThingsEvent.new_from_json_dict(event)) else: LOGGER.warn('Unknown event type. type=' + event_type) diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 980b5a51..a6e3b5f5 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -24,7 +24,7 @@ from linebot.models import ( MessageEvent, FollowEvent, UnfollowEvent, JoinEvent, LeaveEvent, PostbackEvent, BeaconEvent, AccountLinkEvent, - MemberJoinedEvent, MemberLeftEvent, + MemberJoinedEvent, MemberLeftEvent, ThingsEvent, TextMessage, ImageMessage, VideoMessage, AudioMessage, LocationMessage, StickerMessage, FileMessage, SourceUser, SourceRoom, SourceGroup @@ -304,20 +304,16 @@ def test_parse(self): self.assertEqual(events[18].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[18].postback.params['datetime'], '2013-04-01T10:00') - # MessageEvent, SourceUser, FileMessage - self.assertIsInstance(events[19], MessageEvent) + # ThingsEvent, SourceUser, link + self.assertIsInstance(events[19], ThingsEvent) self.assertEqual(events[19].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') - self.assertEqual(events[19].type, 'message') + self.assertEqual(events[19].type, 'things') self.assertEqual(events[19].timestamp, 1462629479859) self.assertIsInstance(events[19].source, SourceUser) self.assertEqual(events[19].source.type, 'user') self.assertEqual(events[19].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') - self.assertEqual(events[19].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') - self.assertIsInstance(events[19].message, FileMessage) - self.assertEqual(events[19].message.id, '325708') - self.assertEqual(events[19].message.type, 'file') - self.assertEqual(events[19].message.file_name, "file.txt") - self.assertEqual(events[19].message.file_size, 2138) + self.assertEqual(events[19].things.device_id, 't2c449c9d1') + self.assertEqual(events[19].things.type, 'link') # MemberJoinedEvent self.assertIsInstance(events[20], MemberJoinedEvent) @@ -344,6 +340,32 @@ def test_parse(self): self.assertEqual(events[21].left.members[0].user_id, 'U4af4980629...') self.assertEqual(events[21].left.members[1].user_id, 'U91eeaf62d9...') + # ThingsEvent, SourceUser, unlink + self.assertIsInstance(events[22], ThingsEvent) + self.assertEqual(events[22].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') + self.assertEqual(events[22].type, 'things') + self.assertEqual(events[22].timestamp, 1462629479859) + self.assertIsInstance(events[22].source, SourceUser) + self.assertEqual(events[22].source.type, 'user') + self.assertEqual(events[22].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') + self.assertEqual(events[22].things.device_id, 't2c449c9d1') + self.assertEqual(events[22].things.type, 'unlink') + + # MessageEvent, SourceUser, FileMessage + self.assertIsInstance(events[23], MessageEvent) + self.assertEqual(events[23].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') + self.assertEqual(events[23].type, 'message') + self.assertEqual(events[23].timestamp, 1462629479859) + self.assertIsInstance(events[23].source, SourceUser) + self.assertEqual(events[23].source.type, 'user') + self.assertEqual(events[23].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') + self.assertEqual(events[23].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') + self.assertIsInstance(events[23].message, FileMessage) + self.assertEqual(events[23].message.id, '325708') + self.assertEqual(events[23].message.type, 'file') + self.assertEqual(events[23].message.file_name, "file.txt") + self.assertEqual(events[23].message.file_size, 2138) + class TestWebhookHandler(unittest.TestCase): def setUp(self): diff --git a/tests/text/webhook.json b/tests/text/webhook.json index 09ab5a96..bfa54ec0 100644 --- a/tests/text/webhook.json +++ b/tests/text/webhook.json @@ -248,17 +248,15 @@ }, { "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", - "type": "message", + "type": "things", "timestamp": 1462629479859, "source": { "type": "user", "userId": "U206d25c2ea6bd87c17655609a1c37cb8" }, - "message": { - "id": "325708", - "type": "file", - "fileName": "file.txt", - "fileSize": 2138 + "things": { + "deviceId": "t2c449c9d1", + "type": "link" } }, { @@ -301,6 +299,34 @@ } ] } + }, + { + "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", + "type": "things", + "timestamp": 1462629479859, + "source": { + "type": "user", + "userId": "U206d25c2ea6bd87c17655609a1c37cb8" + }, + "things": { + "deviceId": "t2c449c9d1", + "type": "unlink" + } + }, + { + "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", + "type": "message", + "timestamp": 1462629479859, + "source": { + "type": "user", + "userId": "U206d25c2ea6bd87c17655609a1c37cb8" + }, + "message": { + "id": "325708", + "type": "file", + "fileName": "file.txt", + "fileSize": 2138 + } } ] }