Repository: line/line-bot-sdk-python

Issue:
mode property added to webhook event
https://developers.line.biz/en/news/2019/12/#mode-property-added-to-messaging-api-webhook-events-2019-12-17

Discussion:
claassistantio: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/line/line-bot-sdk-python?pullRequest=233) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/line/line-bot-sdk-python?pullRequest=233) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/line/line-bot-sdk-python?pullRequest=233) it.</sub>

Patch:
diff --git a/README.rst b/README.rst
index a0c2786..b576a2f 100644
--- a/README.rst
+++ b/README.rst
@@ -303,7 +303,7 @@ delete\_rich\_menu(self, rich\_menu\_id, timeout=None)
 Deletes a rich menu.
 
 https://developers.line.biz/en/reference/messaging-api/#delete-rich-menu
-        
+
 .. code:: python
 
     line_bot_api.delete_rich_menu(rich_menu_id)
@@ -949,30 +949,36 @@ The following classes are found in the ``linebot.models`` package.
 
 - MessageEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
     - message: `Message <#message>`__
 - FollowEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
 - UnfollowEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
 - JoinEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
 - LeaveEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
 - PostbackEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
@@ -981,6 +987,7 @@ The following classes are found in the ``linebot.models`` package.
         - params: dict
 - BeaconEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
@@ -990,24 +997,28 @@ The following classes are found in the ``linebot.models`` package.
         - device_message
 - MemberJoinedEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
     - joined: Joined
 - MemberLeftEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
     - left: Left
 - AccountLinkEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
     - link: Link
 - ThingsEvent
     - type
+    - mode
     - timestamp
     - source: `Source <#source>`__
     - reply\_token
@@ -1109,7 +1120,7 @@ Community Q&A: https://www.line-community.me/questions
 
 News: https://developers.line.biz/en/news/
 
-Twitter: @LINE_DEV 
+Twitter: @LINE_DEV
 
 Versioning
 ----------
diff --git a/linebot/models/events.py b/linebot/models/events.py
index 90bd3ca..439b341 100644
--- a/linebot/models/events.py
+++ b/linebot/models/events.py
@@ -45,9 +45,10 @@ class Event(with_metaclass(ABCMeta, Base)):
     https://developers.line.biz/en/reference/messaging-api/#webhook-event-objects
     """
 
-    def __init__(self, timestamp=None, source=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -56,6 +57,7 @@ class Event(with_metaclass(ABCMeta, Base)):
         super(Event, self).__init__(**kwargs)
 
         self.type = None
+        self.mode = mode
         self.timestamp = timestamp
         self.source = self.get_or_new_from_json_dict_with_types(
             source, {
@@ -76,9 +78,11 @@ class MessageEvent(Event):
     You can reply to message events.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, message=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, message=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -88,7 +92,7 @@ class MessageEvent(Event):
         :param kwargs:
         """
         super(MessageEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'message'
@@ -115,9 +119,10 @@ class FollowEvent(Event):
     You can reply to follow events.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -125,7 +130,7 @@ class FollowEvent(Event):
         :param kwargs:
         """
         super(FollowEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'follow'
@@ -140,16 +145,17 @@ class UnfollowEvent(Event):
     Event object for when your account is blocked.
     """
 
-    def __init__(self, timestamp=None, source=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
         :param kwargs:
         """
         super(UnfollowEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'unfollow'
@@ -164,9 +170,10 @@ class JoinEvent(Event):
     You can reply to join events.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -174,7 +181,7 @@ class JoinEvent(Event):
         :param kwargs:
         """
         super(JoinEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'join'
@@ -189,16 +196,17 @@ class LeaveEvent(Event):
     Event object for when your account leaves a group.
     """
 
-    def __init__(self, timestamp=None, source=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
         :param kwargs:
         """
         super(LeaveEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'leave'
@@ -214,9 +222,11 @@ class PostbackEvent(Event):
     You can reply to postback events.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, postback=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, postback=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -226,7 +236,7 @@ class PostbackEvent(Event):
         :param kwargs:
         """
         super(PostbackEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'postback'
@@ -244,10 +254,11 @@ class BeaconEvent(Event):
     Event object for when a user detects a LINE Beacon. You can reply to beacon events.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None,
-                 beacon=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, beacon=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -257,7 +268,7 @@ class BeaconEvent(Event):
         :param kwargs:
         """
         super(BeaconEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'beacon'
@@ -276,10 +287,11 @@ class MemberJoinedEvent(Event):
 
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None,
-                 joined=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, joined=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -289,7 +301,7 @@ class MemberJoinedEvent(Event):
         :param kwargs:
         """
         super(MemberJoinedEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'memberJoined'
@@ -308,10 +320,10 @@ class MemberLeftEvent(Event):
 
     """
 
-    def __init__(self, timestamp=None, source=None,
-                 left=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, left=None, **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -320,7 +332,7 @@ class MemberLeftEvent(Event):
         :param kwargs:
         """
         super(MemberLeftEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'memberLeft'
@@ -340,9 +352,11 @@ class AccountLinkEvent(Event):
     no webhook event will be sent and the user will be shown an error.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, link=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, link=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
         :param long timestamp: Time of the event in milliseconds
         :param source: Source object
         :type source: T <= :py:class:`linebot.models.sources.Source`
@@ -352,7 +366,7 @@ class AccountLinkEvent(Event):
         :param kwargs:
         """
         super(AccountLinkEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'accountLink'
@@ -372,9 +386,12 @@ class ThingsEvent(Event):
     Event sent from LINE Things Webhook service.
     """
 
-    def __init__(self, timestamp=None, source=None, reply_token=None, things=None, **kwargs):
+    def __init__(self, mode=None, timestamp=None, source=None, reply_token=None, things=None,
+                 **kwargs):
         """__init__ method.
 
+        :param str mode: Channel state
+        :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
@@ -383,7 +400,7 @@ class ThingsEvent(Event):
         :param kwargs:
         """
         super(ThingsEvent, self).__init__(
-            timestamp=timestamp, source=source, **kwargs
+            mode=mode, timestamp=timestamp, source=source, **kwargs
         )
 
         self.type = 'things'


Test patch:
diff --git a/tests/test_webhook.py b/tests/test_webhook.py
index edf2c98..51b884f 100644
--- a/tests/test_webhook.py
+++ b/tests/test_webhook.py
@@ -66,6 +66,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[0], MessageEvent)
         self.assertEqual(events[0].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[0].type, 'message')
+        self.assertEqual(events[0].mode, 'active')
         self.assertEqual(events[0].timestamp, 1462629479859)
         self.assertIsInstance(events[0].source, SourceUser)
         self.assertEqual(events[0].source.type, 'user')
@@ -79,6 +80,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[1], MessageEvent)
         self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[1].type, 'message')
+        self.assertEqual(events[1].mode, 'active')
         self.assertEqual(events[1].timestamp, 1462629479859)
         self.assertIsInstance(events[1].source, SourceRoom)
         self.assertEqual(events[1].source.type, 'room')
@@ -97,6 +99,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[2], MessageEvent)
         self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[2].type, 'message')
+        self.assertEqual(events[2].mode, 'active')
         self.assertEqual(events[2].timestamp, 1462629479859)
         self.assertIsInstance(events[2].source, SourceUser)
         self.assertEqual(events[2].source.type, 'user')
@@ -115,6 +118,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[3], MessageEvent)
         self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[3].type, 'message')
+        self.assertEqual(events[3].mode, 'active')
         self.assertEqual(events[3].timestamp, 1462629479859)
         self.assertIsInstance(events[3].source, SourceUser)
         self.assertEqual(events[3].source.type, 'user')
@@ -131,6 +135,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[4], MessageEvent)
         self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[4].type, 'message')
+        self.assertEqual(events[4].mode, 'active')
         self.assertEqual(events[4].timestamp, 1462629479859)
         self.assertIsInstance(events[4].source, SourceUser)
         self.assertEqual(events[4].source.type, 'user')
@@ -147,6 +152,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[5], MessageEvent)
         self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[5].type, 'message')
+        self.assertEqual(events[5].mode, 'active')
         self.assertEqual(events[5].timestamp, 1462629479859)
         self.assertIsInstance(events[5].source, SourceUser)
         self.assertEqual(events[5].source.type, 'user')
@@ -162,6 +168,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[6], FollowEvent)
         self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[6].type, 'follow')
+        self.assertEqual(events[6].mode, 'active')
         self.assertEqual(events[6].timestamp, 1462629479859)
         self.assertIsInstance(events[6].source, SourceUser)
         self.assertEqual(events[6].source.type, 'user')
@@ -170,6 +177,7 @@ class TestWebhookParser(unittest.TestCase):
         # UnfollowEvent, SourceUser
         self.assertIsInstance(events[7], UnfollowEvent)
         self.assertEqual(events[7].type, 'unfollow')
+        self.assertEqual(events[7].mode, 'active')
         self.assertEqual(events[7].timestamp, 1462629479859)
         self.assertIsInstance(events[7].source, SourceUser)
         self.assertEqual(events[7].source.type, 'user')
@@ -179,6 +187,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[8], JoinEvent)
         self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[8].type, 'join')
+        self.assertEqual(events[8].mode, 'active')
         self.assertEqual(events[8].timestamp, 1462629479859)
         self.assertIsInstance(events[8].source, SourceGroup)
         self.assertEqual(events[8].source.type, 'group')
@@ -188,6 +197,7 @@ class TestWebhookParser(unittest.TestCase):
         # LeaveEvent, SourceGroup
         self.assertIsInstance(events[9], LeaveEvent)
         self.assertEqual(events[9].type, 'leave')
+        self.assertEqual(events[9].mode, 'active')
         self.assertEqual(events[9].timestamp, 1462629479859)
         self.assertIsInstance(events[9].source, SourceGroup)
         self.assertEqual(events[9].source.type, 'group')
@@ -198,6 +208,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[10], PostbackEvent)
         self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[10].type, 'postback')
+        self.assertEqual(events[10].mode, 'active')
         self.assertEqual(events[10].timestamp, 1462629479859)
         self.assertIsInstance(events[10].source, SourceUser)
         self.assertEqual(events[10].source.type, 'user')
@@ -209,6 +220,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[11], BeaconEvent)
         self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[11].type, 'beacon')
+        self.assertEqual(events[11].mode, 'active')
         self.assertEqual(events[11].timestamp, 1462629479859)
         self.assertIsInstance(events[11].source, SourceUser)
         self.assertEqual(events[11].source.type, 'user')
@@ -222,6 +234,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[12], BeaconEvent)
         self.assertEqual(events[12].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[12].type, 'beacon')
+        self.assertEqual(events[12].mode, 'active')
         self.assertEqual(events[12].timestamp, 1462629479859)
         self.assertIsInstance(events[12].source, SourceUser)
         self.assertEqual(events[12].source.type, 'user')
@@ -235,6 +248,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[13], AccountLinkEvent)
         self.assertEqual(events[13].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[13].type, 'accountLink')
+        self.assertEqual(events[13].mode, 'active')
         self.assertEqual(events[13].timestamp, 1462629479859)
         self.assertIsInstance(events[13].source, SourceUser)
         self.assertEqual(events[13].source.type, 'user')
@@ -246,6 +260,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[14], MessageEvent)
         self.assertEqual(events[14].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[14].type, 'message')
+        self.assertEqual(events[14].mode, 'active')
         self.assertEqual(events[14].timestamp, 1462629479859)
         self.assertIsInstance(events[14].source, SourceGroup)
         self.assertEqual(events[14].source.type, 'group')
@@ -260,6 +275,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[15], MessageEvent)
         self.assertEqual(events[15].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[15].type, 'message')
+        self.assertEqual(events[15].mode, 'active')
         self.assertEqual(events[15].timestamp, 1462629479859)
         self.assertIsInstance(events[15].source, SourceRoom)
         self.assertEqual(events[15].source.type, 'room')
@@ -274,6 +290,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[16], PostbackEvent)
         self.assertEqual(events[16].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[16].type, 'postback')
+        self.assertEqual(events[16].mode, 'active')
         self.assertEqual(events[16].timestamp, 1462629479859)
         self.assertIsInstance(events[16].source, SourceUser)
         self.assertEqual(events[16].source.type, 'user')
@@ -285,6 +302,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[17], PostbackEvent)
         self.assertEqual(events[17].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[17].type, 'postback')
+        self.assertEqual(events[17].mode, 'active')
         self.assertEqual(events[17].timestamp, 1462629479859)
         self.assertIsInstance(events[17].source, SourceUser)
         self.assertEqual(events[17].source.type, 'user')
@@ -296,6 +314,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[18], PostbackEvent)
         self.assertEqual(events[18].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[18].type, 'postback')
+        self.assertEqual(events[18].mode, 'active')
         self.assertEqual(events[18].timestamp, 1462629479859)
         self.assertIsInstance(events[18].source, SourceUser)
         self.assertEqual(events[18].source.type, 'user')
@@ -307,6 +326,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[19], ThingsEvent)
         self.assertEqual(events[19].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[19].type, 'things')
+        self.assertEqual(events[19].mode, 'active')
         self.assertEqual(events[19].timestamp, 1462629479859)
         self.assertIsInstance(events[19].source, SourceUser)
         self.assertEqual(events[19].source.type, 'user')
@@ -319,6 +339,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[20], MemberJoinedEvent)
         self.assertEqual(events[20].reply_token, '0f3779fba3b349968c5d07db31eabf65')
         self.assertEqual(events[20].type, 'memberJoined')
+        self.assertEqual(events[20].mode, 'active')
         self.assertEqual(events[20].timestamp, 1462629479859)
         self.assertIsInstance(events[20].source, SourceGroup)
         self.assertEqual(events[20].source.type, 'group')
@@ -331,6 +352,7 @@ class TestWebhookParser(unittest.TestCase):
         # MemberLeftEvent
         self.assertIsInstance(events[21], MemberLeftEvent)
         self.assertEqual(events[21].type, 'memberLeft')
+        self.assertEqual(events[21].mode, 'active')
         self.assertEqual(events[21].timestamp, 1462629479960)
         self.assertIsInstance(events[21].source, SourceGroup)
         self.assertEqual(events[21].source.type, 'group')
@@ -344,6 +366,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[22], ThingsEvent)
         self.assertEqual(events[22].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[22].type, 'things')
+        self.assertEqual(events[22].mode, 'active')
         self.assertEqual(events[22].timestamp, 1462629479859)
         self.assertIsInstance(events[22].source, SourceUser)
         self.assertEqual(events[22].source.type, 'user')
@@ -356,6 +379,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[23], MessageEvent)
         self.assertEqual(events[23].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[23].type, 'message')
+        self.assertEqual(events[23].mode, 'active')
         self.assertEqual(events[23].timestamp, 1462629479859)
         self.assertIsInstance(events[23].source, SourceUser)
         self.assertEqual(events[23].source.type, 'user')
@@ -371,6 +395,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertIsInstance(events[24], ThingsEvent)
         self.assertEqual(events[24].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA')
         self.assertEqual(events[24].type, 'things')
+        self.assertEqual(events[24].mode, 'active')
         self.assertEqual(events[24].timestamp, 1547817848122)
         self.assertIsInstance(events[24].source, SourceUser)
         self.assertEqual(events[24].source.type, 'user')
diff --git a/tests/text/webhook.json b/tests/text/webhook.json
index 72a8d05..eb95618 100644
--- a/tests/text/webhook.json
+++ b/tests/text/webhook.json
@@ -4,6 +4,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -18,6 +19,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "room",
@@ -36,6 +38,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -55,6 +58,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -73,6 +77,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -90,6 +95,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -106,6 +112,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "follow",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -114,6 +121,7 @@
     },
     {
       "type": "unfollow",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -123,6 +131,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "join",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "group",
@@ -131,6 +140,7 @@
     },
     {
       "type": "leave",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "group",
@@ -140,6 +150,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "postback",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -152,6 +163,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "beacon",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -165,6 +177,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "beacon",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -179,6 +192,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "accountLink",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -192,6 +206,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "group",
@@ -207,6 +222,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "room",
@@ -222,6 +238,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "postback",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -237,6 +254,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "postback",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -252,6 +270,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "postback",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -267,6 +286,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "things",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -280,6 +300,7 @@
     {
       "replyToken": "0f3779fba3b349968c5d07db31eabf65",
       "type": "memberJoined",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "group",
@@ -300,6 +321,7 @@
     },
     {
       "type": "memberLeft",
+      "mode": "active",
       "timestamp": 1462629479960,
       "source": {
         "type": "group",
@@ -321,6 +343,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "things",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -334,6 +357,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "message",
+      "mode": "active",
       "timestamp": 1462629479859,
       "source": {
         "type": "user",
@@ -349,6 +373,7 @@
     {
       "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
       "type": "things",
+      "mode": "active",
       "source": {
         "type": "user",
         "userId": "U206d25c2ea6bd87c17655609a1c37cb8"