Skip to content

Commit 1d0653e

Browse files
authored
Support Unsend & Video play complete event (#284)
#283 - Unsend event - Video play complete event
1 parent 6b0aba4 commit 1d0653e

File tree

9 files changed

+216
-4
lines changed

9 files changed

+216
-4
lines changed

linebot/models/events.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
ScenarioResult,
3838
)
3939
from linebot.models.things import Things # noqa, backward compatibility
40+
from linebot.models.unsend import Unsend
41+
from linebot.models.video_play_complete import VideoPlayComplete
4042

4143

4244
class Event(with_metaclass(ABCMeta, Base)):
@@ -414,6 +416,68 @@ def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, thi
414416
)
415417

416418

419+
class UnsendEvent(Event):
420+
"""Webhook UnsendEvent.
421+
422+
https://developers.line.biz/en/reference/messaging-api/#unsend-event
423+
424+
Event object for when the user unsends a message in a group or room.
425+
"""
426+
427+
def __init__(self, mode=None, timestamp=None, source=None, unsend=None, **kwargs):
428+
"""__init__ method.
429+
430+
:param str mode: Channel state
431+
:param long timestamp: Time of the event in milliseconds
432+
:param source: Source object
433+
:type source: T <= :py:class:`linebot.models.sources.Source`
434+
:param unsend: Unsend object
435+
:type unsend: T <= :py:class:`linebot.models.unsend.Unsend`
436+
:param kwargs:
437+
"""
438+
super(UnsendEvent, self).__init__(
439+
mode=mode, timestamp=timestamp, source=source, **kwargs
440+
)
441+
442+
self.type = 'unsend'
443+
self.unsend = self.get_or_new_from_json_dict(
444+
unsend, Unsend
445+
)
446+
447+
448+
class VideoPlayCompleteEvent(Event):
449+
"""Webhook VideoCompleteEvent.
450+
451+
https://developers.line.biz/en/reference/messaging-api/#video-viewing-complete
452+
453+
Event object Event for when a user finishes viewing a video at least once.
454+
"""
455+
456+
def __init__(self, mode=None, timestamp=None, source=None, reply_token=None,
457+
video_play_complete=None, **kwargs):
458+
"""__init__ method.
459+
460+
:param str mode: Channel state
461+
:param long timestamp: Time of the event in milliseconds
462+
:param source: Source object
463+
:type source: T <= :py:class:`linebot.models.sources.Source`
464+
:param str reply_token: Reply token
465+
:param video_play_complete: VideoPlayComplete object
466+
:type video_play_complete:
467+
T <= :py:class:`linebot.models.video_play_complete.VideoPlayComplete`
468+
:param kwargs:
469+
"""
470+
super(VideoPlayCompleteEvent, self).__init__(
471+
mode=mode, timestamp=timestamp, source=source, **kwargs
472+
)
473+
474+
self.type = 'videoPlayComplete'
475+
self.reply_token = reply_token
476+
self.video_play_complete = self.get_or_new_from_json_dict(
477+
video_play_complete, VideoPlayComplete
478+
)
479+
480+
417481
class Postback(Base):
418482
"""Postback.
419483

linebot/models/send_messages.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,16 @@ class VideoSendMessage(SendMessage):
101101
"""
102102

103103
def __init__(self, original_content_url=None, preview_image_url=None,
104-
quick_reply=None, **kwargs):
104+
tracking_id=None, quick_reply=None, **kwargs):
105105
"""__init__ method.
106106
107107
:param str original_content_url: URL of video file.
108108
HTTPS. mp4. Less than 1 minute. Max: 10 MB.
109109
:param str preview_image_url: URL of preview image.
110110
HTTPS. JPEG. Max: 240 x 240. Max: 1 MB.
111+
:param str tracking_id: the video viewing complete event occurs
112+
when the user finishes watching the video.
113+
Max character limit: 100.
111114
:param quick_reply: QuickReply object
112115
:type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
113116
:param kwargs:
@@ -117,6 +120,7 @@ def __init__(self, original_content_url=None, preview_image_url=None,
117120
self.type = 'video'
118121
self.original_content_url = original_content_url
119122
self.preview_image_url = preview_image_url
123+
self.tracking_id = tracking_id
120124

121125

122126
class AudioSendMessage(SendMessage):

linebot/models/unsend.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.unsend 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 Unsend(with_metaclass(ABCMeta, Base)):
27+
"""Abstract Base Class of Unsend."""
28+
29+
def __init__(self, message_id=None, **kwargs):
30+
"""__init__ method.
31+
32+
:param str message_id: The message ID of the unsent message.
33+
:param kwargs:
34+
"""
35+
super(Unsend, self).__init__(**kwargs)
36+
self.message_id = message_id

linebot/models/video_play_complete.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.video_play_complete 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 VideoPlayComplete(with_metaclass(ABCMeta, Base)):
27+
"""Abstract Base Class of VideoPlayComplete."""
28+
29+
def __init__(self, tracking_id=None, **kwargs):
30+
"""__init__ method.
31+
32+
:param str tracking_id: the video viewing complete event occurs
33+
when the user finishes watching the video.
34+
Max character limit: 100.
35+
:param kwargs:
36+
"""
37+
super(VideoPlayComplete, self).__init__(**kwargs)
38+
self.tracking_id = tracking_id

linebot/webhook.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
AccountLinkEvent,
3535
MemberJoinedEvent,
3636
MemberLeftEvent,
37-
ThingsEvent,
37+
ThingsEvent, UnsendEvent, VideoPlayCompleteEvent,
3838
)
3939
from .utils import LOGGER, PY3, safe_compare_digest
4040

@@ -168,6 +168,10 @@ def parse(self, body, signature, as_payload=False):
168168
events.append(MemberLeftEvent.new_from_json_dict(event))
169169
elif event_type == 'things':
170170
events.append(ThingsEvent.new_from_json_dict(event))
171+
elif event_type == 'unsend':
172+
events.append(UnsendEvent.new_from_json_dict(event))
173+
elif event_type == 'videoPlayComplete':
174+
events.append(VideoPlayCompleteEvent.new_from_json_dict(event))
171175
else:
172176
LOGGER.warn('Unknown event type. type=' + event_type)
173177

tests/api/test_send_video_message.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ def setUp(self):
3333

3434
self.video_message = VideoSendMessage(
3535
original_content_url='https://example.com/original.mp4',
36-
preview_image_url='https://example.com/preview.jpg'
36+
preview_image_url='https://example.com/preview.jpg',
37+
tracking_id='TrackId'
3738
)
3839

3940
self.message = [{
4041
"type": "video",
4142
"originalContentUrl": "https://example.com/original.mp4",
4243
"previewImageUrl": "https://example.com/preview.jpg",
44+
"trackingId": "TrackId"
4345
}]
4446

4547
@responses.activate

tests/models/test_send_messages.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ def test_video_message(self):
7272
VideoSendMessage(**arg).as_json_dict()
7373
)
7474

75+
def test_video_message_wtih_track(self):
76+
arg = {
77+
'type': 'video',
78+
'original_content_url': 'https://example.com/original.mp4',
79+
'preview_image_url': 'https://example.com/preview.jpg',
80+
"tracking_id": "track_id"
81+
}
82+
self.assertEqual(
83+
self.serialize_as_dict(arg, type=self.VIDEO),
84+
VideoSendMessage(**arg).as_json_dict()
85+
)
86+
7587
def test_audio_message(self):
7688
arg = {
7789
'original_content_url': 'https://example.com/original.m4a',

tests/test_webhook.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
LocationMessage, StickerMessage, FileMessage,
3131
SourceUser, SourceRoom, SourceGroup,
3232
DeviceLink, DeviceUnlink, ScenarioResult, ActionResult)
33+
from linebot.models.events import UnsendEvent, VideoPlayCompleteEvent
34+
from linebot.models.unsend import Unsend
35+
from linebot.models.video_play_complete import VideoPlayComplete
3336
from linebot.utils import PY3
3437

3538

@@ -386,7 +389,6 @@ def test_parse(self):
386389
self.assertIsInstance(events[23].source, SourceUser)
387390
self.assertEqual(events[23].source.type, 'user')
388391
self.assertEqual(events[23].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
389-
self.assertEqual(events[23].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
390392
self.assertIsInstance(events[23].message, FileMessage)
391393
self.assertEqual(events[23].message.id, '325708')
392394
self.assertEqual(events[23].message.type, 'file')
@@ -417,6 +419,29 @@ def test_parse(self):
417419
self.assertIsInstance(events[24].things.result.action_results[1], ActionResult)
418420
self.assertEqual(events[24].things.result.action_results[1].type, 'void')
419421

422+
# UnsendEvent
423+
self.assertIsInstance(events[25], UnsendEvent)
424+
self.assertEqual(events[25].type, 'unsend')
425+
self.assertEqual(events[25].mode, 'active')
426+
self.assertEqual(events[25].timestamp, 1547817848122)
427+
self.assertIsInstance(events[25].source, SourceGroup)
428+
self.assertEqual(events[25].source.type, 'group')
429+
self.assertEqual(events[25].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
430+
self.assertIsInstance(events[25].unsend, Unsend)
431+
self.assertEqual(events[25].unsend.message_id, '325708')
432+
433+
# VideoPlayCompleteEvent
434+
self.assertIsInstance(events[26], VideoPlayCompleteEvent)
435+
self.assertEqual(events[26].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
436+
self.assertEqual(events[26].type, 'videoPlayComplete')
437+
self.assertEqual(events[26].mode, 'active')
438+
self.assertEqual(events[26].timestamp, 1462629479859)
439+
self.assertIsInstance(events[26].source, SourceUser)
440+
self.assertEqual(events[26].source.type, 'user')
441+
self.assertEqual(events[26].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
442+
self.assertIsInstance(events[26].video_play_complete, VideoPlayComplete)
443+
self.assertEqual(events[26].video_play_complete.tracking_id, 'track_id')
444+
420445
def test_parse_webhook_req_without_destination(self):
421446
body = """
422447
{
@@ -538,6 +563,7 @@ def wrapper(*args):
538563
else:
539564
arg_spec = inspect.getargspec(func)
540565
return func(*args[0:len(arg_spec.args)])
566+
541567
return wrapper
542568

543569
def func_with_0_args():

tests/text/webhook.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,32 @@
400400
]
401401
}
402402
}
403+
},
404+
{
405+
"type": "unsend",
406+
"mode": "active",
407+
"timestamp": 1547817848122,
408+
"source": {
409+
"type": "group",
410+
"groupId": "Ca56f94637cc4347f90a25382909b24b9",
411+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
412+
},
413+
"unsend": {
414+
"messageId": "325708"
415+
}
416+
},
417+
{
418+
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
419+
"type": "videoPlayComplete",
420+
"timestamp": 1462629479859,
421+
"mode": "active",
422+
"source": {
423+
"type": "user",
424+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
425+
},
426+
"videoPlayComplete": {
427+
"trackingId": "track_id"
428+
}
403429
}
404430
]
405431
}

0 commit comments

Comments
 (0)