Skip to content

Commit 8b0007f

Browse files
authored
LINE Things scenario execution event (#176)
* Add LINE Things scenario execution event * fix models.things and update test * fix docs/source/linebot.models.rst
1 parent 119b108 commit 8b0007f

File tree

6 files changed

+259
-31
lines changed

6 files changed

+259
-31
lines changed

docs/source/linebot.models.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
linebot.models package
22
======================
33

4+
linebot.models.actions module
5+
--------------------------
6+
7+
.. automodule:: linebot.models.actions
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:
11+
412
linebot.models.base module
513
--------------------------
614

@@ -49,6 +57,14 @@ linebot.models.responses module
4957
:undoc-members:
5058
:show-inheritance:
5159

60+
linebot.models.rich_menu module
61+
-------------------------------
62+
63+
.. automodule:: linebot.models.rich_menu
64+
:members:
65+
:undoc-members:
66+
:show-inheritance:
67+
5268
linebot.models.send_messages module
5369
-----------------------------------
5470

@@ -72,3 +88,11 @@ linebot.models.template module
7288
:members:
7389
:undoc-members:
7490
:show-inheritance:
91+
92+
linebot.models.things module
93+
------------------------------
94+
95+
.. automodule:: linebot.models.things
96+
:members:
97+
:undoc-members:
98+
:show-inheritance:

linebot/models/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,10 @@
136136
ImageCarouselTemplate,
137137
ImageCarouselColumn,
138138
)
139+
from .things import ( # noqa
140+
DeviceLink,
141+
DeviceUnlink,
142+
ScenarioResult,
143+
ActionResult,
144+
Things,
145+
)

linebot/models/events.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
from future.utils import with_metaclass
2222

23-
from .base import Base
24-
from .messages import (
23+
from linebot.models.base import Base
24+
from linebot.models.messages import (
2525
TextMessage,
2626
ImageMessage,
2727
VideoMessage,
@@ -30,7 +30,13 @@
3030
StickerMessage,
3131
FileMessage
3232
)
33-
from .sources import SourceUser, SourceGroup, SourceRoom
33+
from linebot.models.sources import SourceUser, SourceGroup, SourceRoom
34+
from linebot.models.things import (
35+
DeviceUnlink,
36+
DeviceLink,
37+
ScenarioResult,
38+
)
39+
from linebot.models.things import Things # noqa
3440

3541

3642
class Event(with_metaclass(ABCMeta, Base)):
@@ -360,20 +366,20 @@ class ThingsEvent(Event):
360366
"""Webhook ThingsEvent.
361367
362368
https://developers.line.biz/en/reference/messaging-api/#device-link-event
369+
https://developers.line.biz/en/reference/messaging-api/#device-unlink-event
370+
https://developers.line.biz/en/reference/messaging-api/#scenario-result-event
363371
364-
Indicates that a LINE Things-compatible device has been linked with LINE by
365-
a user operation.
372+
Event sent from LINE Things Webhook service.
366373
"""
367374

368375
def __init__(self, timestamp=None, source=None, reply_token=None, things=None, **kwargs):
369376
"""__init__ method.
370377
371-
:param long timestamp: Time of the event in milliseconds
372378
:param source: Source object
373379
:type source: T <= :py:class:`linebot.models.sources.Source`
374380
:param str reply_token: Reply token
375381
:param things: Things object
376-
:type things: :py:class:`linebot.models.events.Things`
382+
:type things: T <= :py:class:`linebot.models.things.Things`
377383
:param kwargs:
378384
"""
379385
super(ThingsEvent, self).__init__(
@@ -382,8 +388,12 @@ def __init__(self, timestamp=None, source=None, reply_token=None, things=None, *
382388

383389
self.type = 'things'
384390
self.reply_token = reply_token
385-
self.things = self.get_or_new_from_json_dict(
386-
things, Things
391+
self.things = self.get_or_new_from_json_dict_with_types(
392+
things, {
393+
'link': DeviceLink,
394+
'unlink': DeviceUnlink,
395+
'scenarioResult': ScenarioResult,
396+
}
387397
)
388398

389399

@@ -498,21 +508,3 @@ def __init__(self, result=None, nonce=None, **kwargs):
498508

499509
self.result = result
500510
self.nonce = nonce
501-
502-
503-
class Things(Base):
504-
"""Things.
505-
506-
https://developers.line.biz/en/docs/line-things/develop-bot/#link-event
507-
"""
508-
509-
def __init__(self, device_id=None, type=None, **kwargs):
510-
"""__init__ method.
511-
512-
:param str device_id: Device ID of the device that was linked with LINE.
513-
:param str type: link or unlink
514-
"""
515-
super(Things, self).__init__(**kwargs)
516-
517-
self.device_id = device_id
518-
self.type = type

linebot/models/things.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
"""linebot.models.things module."""
16+
17+
from __future__ import unicode_literals
18+
19+
from abc import ABCMeta
20+
21+
from future.utils import with_metaclass
22+
23+
from .base import Base
24+
25+
26+
class Things(with_metaclass(ABCMeta, Base)):
27+
"""Abstract Base Class of Things."""
28+
29+
def __init__(self, device_id=None, **kwargs):
30+
"""__init__ method.
31+
32+
:param str device_id: Device ID.
33+
:param kwargs:
34+
"""
35+
super(Things, self).__init__(**kwargs)
36+
self.device_id = device_id
37+
38+
39+
class DeviceLink(Things):
40+
"""DeviceLink.
41+
42+
https://developers.line.biz/en/reference/messaging-api/#device-link-event
43+
44+
Indicates that a user linked a device with LINE.
45+
"""
46+
47+
def __init__(self, device_id=None, **kwargs):
48+
"""__init__ method.
49+
50+
:param str device_id: Device ID of the device that has been linked with LINE.
51+
:param kwargs:
52+
"""
53+
super(DeviceLink, self).__init__(device_id=device_id, **kwargs)
54+
55+
self.type = 'link'
56+
57+
58+
class DeviceUnlink(Things):
59+
"""DeviceUnlink.
60+
61+
https://developers.line.biz/en/reference/messaging-api/#device-unlink-event
62+
63+
Indicates that the user unlinked a device from LINE.
64+
"""
65+
66+
def __init__(self, device_id=None, **kwargs):
67+
"""__init__ method.
68+
69+
:param str device_id: Device ID of the device that was unlinked from LINE.
70+
:param kwargs:
71+
"""
72+
super(DeviceUnlink, self).__init__(device_id=device_id, **kwargs)
73+
74+
self.type = 'unlink'
75+
76+
77+
class ScenarioResult(Things):
78+
"""ScenarioResult.
79+
80+
https://developers.line.biz/en/reference/messaging-api/#scenario-result-event
81+
82+
Indicates that an automatic communication scenario has been executed.
83+
"""
84+
85+
def __init__(self, device_id=None, result=None, **kwargs):
86+
"""__init__ method.
87+
88+
:param str device_id: Device ID of the device that executed the scenario.
89+
:param str result: ScenarioResultPayload object.
90+
:param kwargs:
91+
"""
92+
super(ScenarioResult, self).__init__(device_id=device_id, **kwargs)
93+
94+
self.type = 'scenarioResult'
95+
self.result = self.get_or_new_from_json_dict(
96+
result, ScenarioResultPayload
97+
)
98+
99+
100+
class ScenarioResultPayload(Base):
101+
"""ScenarioResultPayload."""
102+
103+
def __init__(self, scenario_id=None, revision=None, start_time=None,
104+
result_code=None, end_time=None, action_results=None,
105+
ble_notification_payload=None, error_reason=None, **kwargs):
106+
"""__init__ method.
107+
108+
:param str scenario_id: Scenario ID executed.
109+
:param long revision: Revision number.
110+
:param long start_time: Timestamp for when execution of scenario
111+
action started (milliseconds).
112+
:param long end_time: Timestamp for when execution of scenario
113+
was completed (milliseconds).
114+
:param str result_code: Scenario execution completion status.
115+
:param action_results: Array of actions specified in a scenario.
116+
:type action_results: list[T <= :py:class:`linebot.models.things.ActionResult`]
117+
:param str ble_notification_payload: Data contained in notification.
118+
:param str error_reason: Error response.
119+
:param kwargs:
120+
"""
121+
super(ScenarioResultPayload, self).__init__(**kwargs)
122+
123+
self.scenario_id = scenario_id
124+
self.revision = revision
125+
self.start_time = start_time
126+
self.end_time = end_time
127+
self.result_code = result_code
128+
self.action_results = [self.get_or_new_from_json_dict(it, ActionResult)
129+
for it in action_results]
130+
self.ble_notification_payload = ble_notification_payload
131+
self.error_reason = error_reason
132+
133+
134+
class ActionResult(Base):
135+
"""ActionResult.
136+
137+
Execution result of individual operations specified in action
138+
"""
139+
140+
def __init__(self, type=None, data=None, **kwargs):
141+
"""__init__ method.
142+
143+
:param str type: Type of the executed action.
144+
:param str data: Base64-encoded binary data.
145+
:param kwargs:
146+
"""
147+
super(ActionResult, self).__init__(**kwargs)
148+
149+
self.type = type
150+
self.data = data

tests/test_webhook.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
MemberJoinedEvent, MemberLeftEvent, ThingsEvent,
2828
TextMessage, ImageMessage, VideoMessage, AudioMessage,
2929
LocationMessage, StickerMessage, FileMessage,
30-
SourceUser, SourceRoom, SourceGroup
31-
)
30+
SourceUser, SourceRoom, SourceGroup,
31+
DeviceLink, DeviceUnlink, ScenarioResult, ActionResult)
3232

3333

3434
class TestSignatureValidator(unittest.TestCase):
@@ -308,8 +308,9 @@ def test_parse(self):
308308
self.assertIsInstance(events[19].source, SourceUser)
309309
self.assertEqual(events[19].source.type, 'user')
310310
self.assertEqual(events[19].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
311-
self.assertEqual(events[19].things.device_id, 't2c449c9d1')
311+
self.assertIsInstance(events[19].things, DeviceLink)
312312
self.assertEqual(events[19].things.type, 'link')
313+
self.assertEqual(events[19].things.device_id, 't2c449c9d1')
313314

314315
# MemberJoinedEvent
315316
self.assertIsInstance(events[20], MemberJoinedEvent)
@@ -344,8 +345,9 @@ def test_parse(self):
344345
self.assertIsInstance(events[22].source, SourceUser)
345346
self.assertEqual(events[22].source.type, 'user')
346347
self.assertEqual(events[22].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
347-
self.assertEqual(events[22].things.device_id, 't2c449c9d1')
348+
self.assertIsInstance(events[22].things, DeviceUnlink)
348349
self.assertEqual(events[22].things.type, 'unlink')
350+
self.assertEqual(events[22].things.device_id, 't2c449c9d1')
349351

350352
# MessageEvent, SourceUser, FileMessage
351353
self.assertIsInstance(events[23], MessageEvent)
@@ -362,6 +364,29 @@ def test_parse(self):
362364
self.assertEqual(events[23].message.file_name, "file.txt")
363365
self.assertEqual(events[23].message.file_size, 2138)
364366

367+
# ThingsEvent, SourceUser, scenarioResult
368+
self.assertIsInstance(events[24], ThingsEvent)
369+
self.assertEqual(events[24].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
370+
self.assertEqual(events[24].type, 'things')
371+
self.assertEqual(events[24].timestamp, 1547817848122)
372+
self.assertIsInstance(events[24].source, SourceUser)
373+
self.assertEqual(events[24].source.type, 'user')
374+
self.assertEqual(events[24].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
375+
self.assertIsInstance(events[24].things, ScenarioResult)
376+
self.assertEqual(events[24].things.type, 'scenarioResult')
377+
self.assertEqual(events[24].things.device_id, 't2c449c9d1')
378+
self.assertEqual(events[24].things.result.scenario_id, 'XXX')
379+
self.assertEqual(events[24].things.result.revision, 2)
380+
self.assertEqual(events[24].things.result.start_time, 1547817845950)
381+
self.assertEqual(events[24].things.result.end_time, 1547817845952)
382+
self.assertEqual(events[24].things.result.result_code, 'success')
383+
self.assertEqual(events[24].things.result.ble_notification_payload, 'AQ==')
384+
self.assertIsInstance(events[24].things.result.action_results[0], ActionResult)
385+
self.assertEqual(events[24].things.result.action_results[0].type, 'binary')
386+
self.assertEqual(events[24].things.result.action_results[0].data, '/w==')
387+
self.assertIsInstance(events[24].things.result.action_results[1], ActionResult)
388+
self.assertEqual(events[24].things.result.action_results[1].type, 'void')
389+
365390

366391
class TestWebhookHandler(unittest.TestCase):
367392
def setUp(self):

tests/text/webhook.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,36 @@
343343
"fileName": "file.txt",
344344
"fileSize": 2138
345345
}
346+
},
347+
{
348+
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
349+
"type": "things",
350+
"source": {
351+
"type": "user",
352+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
353+
},
354+
"timestamp": 1547817848122,
355+
"things": {
356+
"type": "scenarioResult",
357+
"deviceId": "t2c449c9d1",
358+
"result": {
359+
"scenarioId": "XXX",
360+
"revision": 2,
361+
"startTime": 1547817845950,
362+
"endTime": 1547817845952,
363+
"resultCode": "success",
364+
"bleNotificationPayload": "AQ==",
365+
"actionResults": [
366+
{
367+
"type": "binary",
368+
"data": "/w=="
369+
},
370+
{
371+
"type": "void"
372+
}
373+
]
374+
}
375+
}
346376
}
347377
]
348378
}

0 commit comments

Comments
 (0)