Skip to content

Commit d1c2ba7

Browse files
okdtskbe-hase
authored andcommitted
Add datetime picker action (#61)
* Set maxDiff to None to check long diff when test fails * Implement datetime picker action for template message * Add postback.params to postback event of webhook for datetime picker
1 parent ce7d2d2 commit d1c2ba7

File tree

7 files changed

+252
-5
lines changed

7 files changed

+252
-5
lines changed

README.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ get\_group\_member\_profile(self, group\_id, user\_id, timeout=None)
145145
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146146

147147
Gets the user profile of a member of a group that the bot is in. This can be
148-
the user ID of a user who has not added the bot as a friend or has blocked
148+
the user ID of a user who has not added the bot as a friend or has blocked
149149
the bot.
150150

151151
https://devdocs.line.me/en/#get-group-room-member-profile
@@ -577,6 +577,10 @@ Event
577577
- reply\_token
578578
- postback: Postback
579579
- data
580+
- params: Params
581+
- date
582+
- time
583+
- datetime
580584
- BeaconEvent
581585
- type
582586
- timestamp

linebot/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
PostbackEvent,
3232
BeaconEvent,
3333
Postback,
34+
Params,
3435
Beacon,
3536
)
3637
from .imagemap import ( # noqa
@@ -79,6 +80,7 @@
7980
PostbackTemplateAction,
8081
MessageTemplateAction,
8182
URITemplateAction,
83+
DatetimePickerTemplateAction,
8284
ImageCarouselTemplate,
8385
ImageCarouselColumn,
8486
)

linebot/models/events.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,54 @@ class Postback(Base):
265265
https://devdocs.line.me/en/#postback-event
266266
"""
267267

268-
def __init__(self, data=None, **kwargs):
268+
def __init__(self, data=None, params=None, **kwargs):
269269
"""__init__ method.
270270
271-
:param str data:
271+
:param str data: Postback data
272+
:param params: JSON object with the date and time
273+
selected by a user through a datetime picker action.
274+
Only returned for postback actions via the datetime picker.
275+
:type params: T <= :py:class:`linebot.models.events.Params`
272276
:param kwargs:
273277
"""
274278
super(Postback, self).__init__(**kwargs)
275279

276280
self.data = data
281+
self.params = self.get_or_new_from_json_dict(
282+
params, Params
283+
)
284+
285+
286+
class Params(Base):
287+
"""Params.
288+
289+
Object with the date and time selected by a user
290+
through a datetime picker action.
291+
The format for the full-date, time-hour, and time-minute
292+
as shown below follow the RFC3339 protocol.
293+
294+
https://devdocs.line.me/en/#postback-params-object
295+
"""
296+
297+
def __init__(self, date=None, time=None, datetime=None, **kwargs):
298+
"""__init__ method.
299+
300+
:param str date: Date selected by user.
301+
Only included in the date mode.
302+
Format: full-date
303+
:param str time: Time selected by the user.
304+
Only included in the time mode.
305+
Format: time-hour":"time-minute
306+
:param str datetime: Date and time selected by the user.
307+
Only included in the datetime mode.
308+
Format: full-date"T"time-hour":"time-minute
309+
:param kwargs:
310+
"""
311+
super(Params, self).__init__(**kwargs)
312+
313+
self.date = date
314+
self.time = time
315+
self.datetime = datetime
277316

278317

279318
class Beacon(Base):

linebot/models/template.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def _get_action(action):
2929
action, {
3030
'postback': PostbackTemplateAction,
3131
'message': MessageTemplateAction,
32-
'uri': URITemplateAction
32+
'uri': URITemplateAction,
33+
'datetimepicker': DatetimePickerTemplateAction,
3334
}
3435
)
3536
return action_obj
@@ -361,3 +362,43 @@ def __init__(self, label=None, uri=None, **kwargs):
361362
self.type = 'uri'
362363
self.label = label
363364
self.uri = uri
365+
366+
367+
class DatetimePickerTemplateAction(TemplateAction):
368+
"""DatetimePickerTemplateAction.
369+
370+
https://devdocs.line.me/en/#template-messages
371+
372+
When this action is tapped, a postback event is returned via webhook
373+
with the date and time selected by the user from the date and time selection dialog.
374+
"""
375+
376+
def __init__(self, label=None, data=None, mode=None,
377+
initial=None, max=None, min=None, **kwargs):
378+
"""__init__ method.
379+
380+
:param str label: Label for the action
381+
Max: 20 characters
382+
:param str data: String returned via webhook
383+
in the postback.data property of the postback event
384+
Max: 300 characters
385+
:param str mode: Action mode
386+
date: Pick date
387+
time: Pick time
388+
datetime: Pick date and time
389+
:param str initial: Initial value of date or time
390+
:param str max: Largest date or time value that can be selected.
391+
Must be greater than the min value.
392+
:param str min: Smallest date or time value that can be selected.
393+
Must be less than the max value.
394+
:param kwargs:
395+
"""
396+
super(DatetimePickerTemplateAction, self).__init__(**kwargs)
397+
398+
self.type = 'datetimepicker'
399+
self.label = label
400+
self.data = data
401+
self.mode = mode
402+
self.initial = initial
403+
self.max = max
404+
self.min = min

tests/api/test_send_template_message.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
)
2525
from linebot.models import (
2626
TemplateSendMessage, ButtonsTemplate,
27-
PostbackTemplateAction, MessageTemplateAction, URITemplateAction,
27+
PostbackTemplateAction, MessageTemplateAction,
28+
URITemplateAction, DatetimePickerTemplateAction,
2829
ConfirmTemplate, CarouselTemplate, CarouselColumn,
2930
ImageCarouselTemplate, ImageCarouselColumn
3031
)
3132

3233

3334
class TestLineBotApi(unittest.TestCase):
35+
36+
maxDiff = None
37+
3438
def setUp(self):
3539
self.tested = LineBotApi('channel_secret')
3640

@@ -161,6 +165,37 @@ def setUp(self):
161165
uri='http://example.com/2'
162166
)
163167
]
168+
),
169+
CarouselColumn(
170+
thumbnail_image_url='https://example.com'
171+
'/item3.jpg',
172+
title='this is menu3', text='description3',
173+
actions=[
174+
DatetimePickerTemplateAction(
175+
label="datetime picker date",
176+
data="action=sell&itemid=2&mode=date",
177+
mode="date",
178+
initial="2013-04-01",
179+
min="2011-06-23",
180+
max="2017-09-08"
181+
),
182+
DatetimePickerTemplateAction(
183+
label="datetime picker time",
184+
data="action=sell&itemid=2&mode=time",
185+
mode="time",
186+
initial="10:00",
187+
min="00:00",
188+
max="23:59"
189+
),
190+
DatetimePickerTemplateAction(
191+
label="datetime picker datetime",
192+
data="action=sell&itemid=2&mode=datetime",
193+
mode="datetime",
194+
initial="2013-04-01T10:00",
195+
min="2011-06-23T00:00",
196+
max="2017-09-08T23:59"
197+
)
198+
]
164199
)
165200
]
166201
)
@@ -219,6 +254,41 @@ def setUp(self):
219254
"uri": "http://example.com/2"
220255
}
221256
]
257+
},
258+
{
259+
"thumbnailImageUrl":
260+
"https://example.com/item3.jpg",
261+
"title": "this is menu3",
262+
"text": "description3",
263+
"actions": [
264+
{
265+
"type": "datetimepicker",
266+
"label": "datetime picker date",
267+
"data": "action=sell&itemid=2&mode=date",
268+
"mode": "date",
269+
"initial": "2013-04-01",
270+
"min": "2011-06-23",
271+
"max": "2017-09-08"
272+
},
273+
{
274+
"type": "datetimepicker",
275+
"label": "datetime picker time",
276+
"data": "action=sell&itemid=2&mode=time",
277+
"mode": "time",
278+
"initial": "10:00",
279+
"min": "00:00",
280+
"max": "23:59"
281+
},
282+
{
283+
"type": "datetimepicker",
284+
"label": "datetime picker datetime",
285+
"data": "action=sell&itemid=2&mode=datetime",
286+
"mode": "datetime",
287+
"initial": "2013-04-01T10:00",
288+
"min": "2011-06-23T00:00",
289+
"max": "2017-09-08T23:59"
290+
}
291+
]
222292
}
223293
]
224294
}

tests/test_webhook.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
TextMessage, ImageMessage, VideoMessage, AudioMessage,
2828
LocationMessage, StickerMessage,
2929
SourceUser, SourceRoom, SourceGroup,
30+
Params,
3031
)
3132

3233

@@ -195,6 +196,7 @@ def test_parse(self):
195196
self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
196197
self.assertEqual(events[10].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
197198
self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red')
199+
self.assertEqual(events[10].postback.params, None)
198200

199201
# BeaconEvent, SourceUser
200202
self.assertIsInstance(events[11], BeaconEvent)
@@ -254,6 +256,45 @@ def test_parse(self):
254256
self.assertEqual(events[14].message.type, 'text')
255257
self.assertEqual(events[14].message.text, 'Hello, world')
256258

259+
# PostbackEvent, SourceUser, with date params
260+
self.assertIsInstance(events[15], PostbackEvent)
261+
self.assertEqual(events[15].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
262+
self.assertEqual(events[15].type, 'postback')
263+
self.assertEqual(events[15].timestamp, 1462629479859)
264+
self.assertIsInstance(events[15].source, SourceUser)
265+
self.assertEqual(events[15].source.type, 'user')
266+
self.assertEqual(events[15].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
267+
self.assertEqual(events[15].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
268+
self.assertEqual(events[15].postback.data, 'action=buyItem&itemId=123123&color=red')
269+
self.assertIsInstance(events[15].postback.params, Params)
270+
self.assertEqual(events[15].postback.params.date, '2013-04-01')
271+
272+
# PostbackEvent, SourceUser, with date params
273+
self.assertIsInstance(events[16], PostbackEvent)
274+
self.assertEqual(events[16].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
275+
self.assertEqual(events[16].type, 'postback')
276+
self.assertEqual(events[16].timestamp, 1462629479859)
277+
self.assertIsInstance(events[16].source, SourceUser)
278+
self.assertEqual(events[16].source.type, 'user')
279+
self.assertEqual(events[16].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
280+
self.assertEqual(events[16].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
281+
self.assertEqual(events[16].postback.data, 'action=buyItem&itemId=123123&color=red')
282+
self.assertIsInstance(events[15].postback.params, Params)
283+
self.assertEqual(events[16].postback.params.time, '10:00')
284+
285+
# PostbackEvent, SourceUser, with date params
286+
self.assertIsInstance(events[17], PostbackEvent)
287+
self.assertEqual(events[17].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
288+
self.assertEqual(events[17].type, 'postback')
289+
self.assertEqual(events[17].timestamp, 1462629479859)
290+
self.assertIsInstance(events[17].source, SourceUser)
291+
self.assertEqual(events[17].source.type, 'user')
292+
self.assertEqual(events[17].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
293+
self.assertEqual(events[17].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
294+
self.assertEqual(events[17].postback.data, 'action=buyItem&itemId=123123&color=red')
295+
self.assertIsInstance(events[15].postback.params, Params)
296+
self.assertEqual(events[17].postback.params.datetime, '2013-04-01T10:00')
297+
257298

258299
class TestWebhookHandler(unittest.TestCase):
259300
def setUp(self):
@@ -321,6 +362,11 @@ def test_handler(self):
321362
self.assertEqual(self.calls[10], '6 postback')
322363
self.assertEqual(self.calls[11], '7 beacon')
323364
self.assertEqual(self.calls[12], '7 beacon')
365+
self.assertEqual(self.calls[13], '1 message_text')
366+
self.assertEqual(self.calls[14], '1 message_text')
367+
self.assertEqual(self.calls[15], '6 postback')
368+
self.assertEqual(self.calls[16], '6 postback')
369+
self.assertEqual(self.calls[17], '6 postback')
324370

325371

326372
if __name__ == '__main__':

tests/text/webhook.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,51 @@
187187
"type": "text",
188188
"text": "Hello, world"
189189
}
190+
},
191+
{
192+
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
193+
"type": "postback",
194+
"timestamp": 1462629479859,
195+
"source": {
196+
"type": "user",
197+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
198+
},
199+
"postback": {
200+
"data": "action=buyItem&itemId=123123&color=red",
201+
"params": {
202+
"date": "2013-04-01"
203+
}
204+
}
205+
},
206+
{
207+
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
208+
"type": "postback",
209+
"timestamp": 1462629479859,
210+
"source": {
211+
"type": "user",
212+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
213+
},
214+
"postback": {
215+
"data": "action=buyItem&itemId=123123&color=red",
216+
"params": {
217+
"time": "10:00"
218+
}
219+
}
220+
},
221+
{
222+
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
223+
"type": "postback",
224+
"timestamp": 1462629479859,
225+
"source": {
226+
"type": "user",
227+
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
228+
},
229+
"postback": {
230+
"data": "action=buyItem&itemId=123123&color=red",
231+
"params": {
232+
"datetime": "2013-04-01T10:00"
233+
}
234+
}
190235
}
191236
]
192237
}

0 commit comments

Comments
 (0)