import tornado.ioloop
import tornado.web
import json, os
+from linebot import LineBotApi, WebhookParser
+from linebot.exceptions import InvalidSignatureError
+from linebot.models import MessageEvent, TextMessage, TextSendMessage
class WebHookHandler(tornado.web.RequestHandler):
- def get(self):
- token = self.get_argument('hub.verify_token','')
- if token == ch:
- write(token)
- else:
- write('Error, wrong varidation Token.')
+ def post(self):
+ global webhook
+ signature = json.load(self.request.headers['X-Line-Signature'])
+ data = json.load(self.request.body)
+ try:
+ events = webhook.parse(data, signature)
+ except InvalidSignatureError:
+ abort(400)
+ for event in events:
+ if not isinstance(event,MessageEvent):
+ continue
+ if not isinstance(event.message,TextMessage):
+ continue
+ linebot.reply_message(
+ event.rply_token,
+ TextSendMessage(text=event.message.text)
+ )
+ return 'OK'
application = tornado.web.Application([(r'/callback',WebHookHandler)],{
'debug':True
})
if __name__ == '__main__':
- ch_ic = os.environ['Channel_ID']
+ ch_id = os.environ['Channel_ID']
ch = os.environ['Channel_Secret']
+ linebot = LineBotApi(ch_id)
+ Webhook = WebhookParser(ch)
application.listen(5000)
tornado.ioloop.IOLoop.instance().start()
\ No newline at end of file
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Meta data of line-bot-sdk."""
+
+from __future__ import unicode_literals
+
+__version__ = '1.8.0'
+__author__ = 'LINE Corporation'
+__copyright__ = 'Copyright 2016, LINE Corporation'
+__license__ = 'Apache 2.0'
+
+__all__ = (
+ '__version__'
+)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot package."""
+
+from __future__ import unicode_literals
+
+from .__about__ import ( # noqa
+ __version__
+)
+from .api import ( # noqa
+ LineBotApi,
+)
+from .http_client import ( # noqa
+ HttpClient,
+ RequestsHttpClient,
+ HttpResponse,
+)
+from .webhook import ( # noqa
+ SignatureValidator,
+ WebhookParser,
+ WebhookHandler,
+)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.api module."""
+
+from __future__ import unicode_literals
+
+import json
+
+from .__about__ import __version__
+from .exceptions import LineBotApiError
+from .http_client import HttpClient, RequestsHttpClient
+from .models import (
+ Error, Profile, MemberIds, Content, RichMenuResponse
+)
+
+
+class LineBotApi(object):
+ """LineBotApi provides interface for LINE messaging API."""
+
+ DEFAULT_API_ENDPOINT = 'https://api.line.me'
+
+ def __init__(self, channel_access_token, endpoint=DEFAULT_API_ENDPOINT,
+ timeout=HttpClient.DEFAULT_TIMEOUT, http_client=RequestsHttpClient):
+ """__init__ method.
+
+ :param str channel_access_token: Your channel access token
+ :param str endpoint: (optional) Default is https://api.line.me
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is linebot.http_client.HttpClient.DEFAULT_TIMEOUT
+ :type timeout: float | tuple(float, float)
+ :param http_client: (optional) Default is
+ :py:class:`linebot.http_client.RequestsHttpClient`
+ :type http_client: T <= :py:class:`linebot.http_client.HttpClient`
+ """
+ self.endpoint = endpoint
+ self.headers = {
+ 'Authorization': 'Bearer ' + channel_access_token,
+ 'User-Agent': 'line-bot-sdk-python/' + __version__
+ }
+
+ if http_client:
+ self.http_client = http_client(timeout=timeout)
+ else:
+ self.http_client = RequestsHttpClient(timeout=timeout)
+
+ def reply_message(self, reply_token, messages, timeout=None):
+ """Call reply message API.
+
+ https://devdocs.line.me/en/#reply-message
+
+ Respond to events from users, groups, and rooms.
+
+ Webhooks are used to notify you when an event occurs.
+ For events that you can respond to, a replyToken is issued for replying to messages.
+
+ Because the replyToken becomes invalid after a certain period of time,
+ responses should be sent as soon as a message is received.
+
+ Reply tokens can only be used once.
+
+ :param str reply_token: replyToken received via webhook
+ :param messages: Messages.
+ Max: 5
+ :type messages: T <= :py:class:`linebot.models.send_messages.SendMessage` |
+ list[T <= :py:class:`linebot.models.send_messages.SendMessage`]
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ if not isinstance(messages, (list, tuple)):
+ messages = [messages]
+
+ data = {
+ 'replyToken': reply_token,
+ 'messages': [message.as_json_dict() for message in messages]
+ }
+
+ self._post(
+ '/v2/bot/message/reply', data=json.dumps(data), timeout=timeout
+ )
+
+ def push_message(self, to, messages, timeout=None):
+ """Call push message API.
+
+ https://devdocs.line.me/en/#push-message
+
+ Send messages to users, groups, and rooms at any time.
+
+ :param str to: ID of the receiver
+ :param messages: Messages.
+ Max: 5
+ :type messages: T <= :py:class:`linebot.models.send_messages.SendMessage` |
+ list[T <= :py:class:`linebot.models.send_messages.SendMessage`]
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ if not isinstance(messages, (list, tuple)):
+ messages = [messages]
+
+ data = {
+ 'to': to,
+ 'messages': [message.as_json_dict() for message in messages]
+ }
+
+ self._post(
+ '/v2/bot/message/push', data=json.dumps(data), timeout=timeout
+ )
+
+ def multicast(self, to, messages, timeout=None):
+ """Call multicast API.
+
+ https://devdocs.line.me/en/#multicast
+
+ Send messages to multiple users at any time.
+
+ :param to: IDs of the receivers
+ Max: 150 users
+ :type to: list[str]
+ :param messages: Messages.
+ Max: 5
+ :type messages: T <= :py:class:`linebot.models.send_messages.SendMessage` |
+ list[T <= :py:class:`linebot.models.send_messages.SendMessage`]
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ if not isinstance(messages, (list, tuple)):
+ messages = [messages]
+
+ data = {
+ 'to': to,
+ 'messages': [message.as_json_dict() for message in messages]
+ }
+
+ self._post(
+ '/v2/bot/message/multicast', data=json.dumps(data), timeout=timeout
+ )
+
+ def get_profile(self, user_id, timeout=None):
+ """Call get profile API.
+
+ https://devdocs.line.me/en/#bot-api-get-profile
+
+ Get user profile information.
+
+ :param str user_id: User ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.Profile`
+ :return: Profile instance
+ """
+ response = self._get(
+ '/v2/bot/profile/{user_id}'.format(user_id=user_id),
+ timeout=timeout
+ )
+
+ return Profile.new_from_json_dict(response.json)
+
+ def get_group_member_profile(self, group_id, user_id, timeout=None):
+ """Call get group member profile API.
+
+ https://devdocs.line.me/en/#get-group-room-member-profile
+
+ Gets the user profile of a member of a group that
+ the bot is in. This can be the user ID of a user who has
+ not added the bot as a friend or has blocked the bot.
+
+ :param str group_id: Group ID
+ :param str user_id: User ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.Profile`
+ :return: Profile instance
+ """
+ response = self._get(
+ '/v2/bot/group/{group_id}/member/{user_id}'.format(group_id=group_id, user_id=user_id),
+ timeout=timeout
+ )
+
+ return Profile.new_from_json_dict(response.json)
+
+ def get_room_member_profile(self, room_id, user_id, timeout=None):
+ """Call get room member profile API.
+
+ https://devdocs.line.me/en/#get-group-room-member-profile
+
+ Gets the user profile of a member of a room that
+ the bot is in. This can be the user ID of a user who has
+ not added the bot as a friend or has blocked the bot.
+
+ :param str room_id: Room ID
+ :param str user_id: User ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.Profile`
+ :return: Profile instance
+ """
+ response = self._get(
+ '/v2/bot/room/{room_id}/member/{user_id}'.format(room_id=room_id, user_id=user_id),
+ timeout=timeout
+ )
+
+ return Profile.new_from_json_dict(response.json)
+
+ def get_group_member_ids(self, group_id, start=None, timeout=None):
+ """Call get group member IDs API.
+
+ https://devdocs.line.me/en/#get-group-room-member-ids
+
+ Gets the user IDs of the members of a group that the bot is in.
+ This includes the user IDs of users who have not added the bot as a friend
+ or has blocked the bot.
+
+ :param str group_id: Group ID
+ :param str start: continuationToken
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.MemberIds`
+ :return: MemberIds instance
+ """
+ params = None if start is None else {'start': start}
+
+ response = self._get(
+ '/v2/bot/group/{group_id}/members/ids'.format(group_id=group_id),
+ params=params,
+ timeout=timeout
+ )
+
+ return MemberIds.new_from_json_dict(response.json)
+
+ def get_room_member_ids(self, room_id, start=None, timeout=None):
+ """Call get room member IDs API.
+
+ https://devdocs.line.me/en/#get-group-room-member-ids
+
+ Gets the user IDs of the members of a group that the bot is in.
+ This includes the user IDs of users who have not added the bot as a friend
+ or has blocked the bot.
+
+ :param str room_id: Room ID
+ :param str start: continuationToken
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.MemberIds`
+ :return: MemberIds instance
+ """
+ params = None if start is None else {'start': start}
+
+ response = self._get(
+ '/v2/bot/room/{room_id}/members/ids'.format(room_id=room_id),
+ params=params,
+ timeout=timeout
+ )
+
+ return MemberIds.new_from_json_dict(response.json)
+
+ def get_message_content(self, message_id, timeout=None):
+ """Call get content API.
+
+ https://devdocs.line.me/en/#get-content
+
+ Retrieve image, video, and audio data sent by users.
+
+ :param str message_id: Message ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.Content`
+ :return: Content instance
+ """
+ response = self._get(
+ '/v2/bot/message/{message_id}/content'.format(message_id=message_id),
+ stream=True, timeout=timeout
+ )
+
+ return Content(response)
+
+ def leave_group(self, group_id, timeout=None):
+ """Call leave group API.
+
+ https://devdocs.line.me/en/#leave
+
+ Leave a group.
+
+ :param str group_id: Group ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ self._post(
+ '/v2/bot/group/{group_id}/leave'.format(group_id=group_id),
+ timeout=timeout
+ )
+
+ def leave_room(self, room_id, timeout=None):
+ """Call leave room API.
+
+ https://devdocs.line.me/en/#leave
+
+ Leave a room.
+
+ :param str room_id: Room ID
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ self._post(
+ '/v2/bot/room/{room_id}/leave'.format(room_id=room_id),
+ timeout=timeout
+ )
+
+ def get_rich_menu(self, rich_menu_id, timeout=None):
+ """Call get rich menu API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu
+
+ :param str rich_menu_id: ID of the rich menu
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.RichMenuResponse`
+ :return: RichMenuResponse instance
+ """
+ response = self._get(
+ '/v2/bot/richmenu/{rich_menu_id}'.format(rich_menu_id=rich_menu_id),
+ timeout=timeout
+ )
+
+ return RichMenuResponse.new_from_json_dict(response.json)
+
+ def create_rich_menu(self, rich_menu, timeout=None):
+ """Call create rich menu API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#create-rich-menu
+
+ :param rich_menu: Inquired to create a rich menu object.
+ :type rich_menu: T <= :py:class:`linebot.models.rich_menu.RichMenu`
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: str
+ :return: rich menu id
+ """
+ response = self._post(
+ '/v2/bot/richmenu', data=rich_menu.as_json_string(), timeout=timeout
+ )
+
+ return response.json.get('richMenuId')
+
+ def delete_rich_menu(self, rich_menu_id, timeout=None):
+ """Call delete rich menu API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#delete-rich-menu
+
+ :param str rich_menu_id: ID of an uploaded rich menu
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ self._delete(
+ '/v2/bot/richmenu/{rich_menu_id}'.format(rich_menu_id=rich_menu_id),
+ timeout=timeout
+ )
+
+ def get_rich_menu_id_of_user(self, user_id, timeout=None):
+ """Call get rich menu ID of user API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu-id-of-user
+
+ :param str user_id: IDs of the user
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: str
+ :return: rich menu id
+ """
+ response = self._get(
+ '/v2/bot/user/{user_id}/richmenu'.format(user_id=user_id),
+ timeout=timeout
+ )
+
+ return response.json.get('richMenuId')
+
+ def link_rich_menu_to_user(self, user_id, rich_menu_id, timeout=None):
+ """Call link rich menu to user API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#link-rich-menu-to-user
+
+ :param str user_id: ID of an uploaded rich menu
+ :param str rich_menu_id: ID of the user
+ :type timeout: float | tuple(float, float)
+ """
+ self._post(
+ '/v2/bot/user/{user_id}/richmenu/{rich_menu_id}'.format(
+ user_id=user_id,
+ rich_menu_id=rich_menu_id
+ ),
+ timeout=timeout
+ )
+
+ def unlink_rich_menu_from_user(self, user_id, timeout=None):
+ """Call unlink rich menu from user API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#unlink-rich-menu-from-user
+
+ :param str user_id: ID of the user
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ self._delete(
+ '/v2/bot/user/{user_id}/richmenu'.format(user_id=user_id),
+ timeout=timeout
+ )
+
+ def get_rich_menu_image(self, rich_menu_id, timeout=None):
+ """Call download rich menu image API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#download-rich-menu-image
+
+ :param str rich_menu_id: ID of the rich menu with the image to be downloaded
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`linebot.models.responses.Content`
+ :return: Content instance
+ """
+ response = self._get(
+ '/v2/bot/richmenu/{rich_menu_id}/content'.format(rich_menu_id=rich_menu_id),
+ timeout=timeout
+ )
+
+ return Content(response)
+
+ def set_rich_menu_image(self, rich_menu_id, content_type, content, timeout=None):
+ """Call upload rich menu image API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#upload-rich-menu-image
+
+ Uploads and attaches an image to a rich menu.
+
+ :param str rich_menu_id: IDs of the richmenu
+ :param str content_type: image/jpeg or image/png
+ :param content: image content as bytes, or file-like object
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ """
+ self._post(
+ '/v2/bot/richmenu/{rich_menu_id}/content'.format(rich_menu_id=rich_menu_id),
+ data=content,
+ headers={'Content-Type': content_type},
+ timeout=timeout
+ )
+
+ def get_rich_menu_list(self, timeout=None):
+ """Call get rich menu list API.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu-list
+
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is self.http_client.timeout
+ :type timeout: float | tuple(float, float)
+ :rtype: list(T <= :py:class:`linebot.models.reponse.RichMenuResponse`)
+ :return: list[RichMenuResponse] instance
+ """
+ response = self._get(
+ '/v2/bot/richmenu/list',
+ timeout=timeout
+ )
+
+ result = []
+ for richmenu in response.json['richmenus']:
+ result.append(RichMenuResponse.new_from_json_dict(richmenu))
+
+ return result
+
+ def _get(self, path, params=None, headers=None, stream=False, timeout=None):
+ url = self.endpoint + path
+
+ if headers is None:
+ headers = {}
+ headers.update(self.headers)
+
+ response = self.http_client.get(
+ url, headers=headers, params=params, stream=stream, timeout=timeout
+ )
+
+ self.__check_error(response)
+ return response
+
+ def _post(self, path, data=None, headers=None, timeout=None):
+ url = self.endpoint + path
+
+ if headers is None:
+ headers = {'Content-Type': 'application/json'}
+ headers.update(self.headers)
+
+ response = self.http_client.post(
+ url, headers=headers, data=data, timeout=timeout
+ )
+
+ self.__check_error(response)
+ return response
+
+ def _delete(self, path, data=None, headers=None, timeout=None):
+ url = self.endpoint + path
+
+ if headers is None:
+ headers = {}
+ headers.update(self.headers)
+
+ response = self.http_client.delete(
+ url, headers=headers, data=data, timeout=timeout
+ )
+
+ self.__check_error(response)
+ return response
+
+ @staticmethod
+ def __check_error(response):
+ if 200 <= response.status_code < 300:
+ pass
+ else:
+ error = Error.new_from_json_dict(response.json)
+ raise LineBotApiError(response.status_code, error)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.exceptions module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+
+class BaseError(with_metaclass(ABCMeta, Exception)):
+ """Base Exception class."""
+
+ def __init__(self, message='-'):
+ """__init__ method.
+
+ :param str message: Human readable message
+ """
+ self.message = message
+
+ def __repr__(self):
+ """repr.
+
+ :return:
+ """
+ return str(self)
+
+ def __str__(self):
+ """str.
+
+ :rtype: str
+ :return:
+ """
+ return '<{0} [{1}]>'.format(
+ self.__class__.__name__, self.message)
+
+
+class InvalidSignatureError(BaseError):
+ """When Webhook signature does NOT match, this error will be raised."""
+
+ def __init__(self, message='-'):
+ """__init__ method.
+
+ :param str message: Human readable message
+ """
+ super(InvalidSignatureError, self).__init__(message)
+
+
+class LineBotApiError(BaseError):
+ """When LINE Messaging API response error, this error will be raised."""
+
+ def __init__(self, status_code, error=None):
+ """__init__ method.
+
+ :param int status_code: http status code
+ :param error: (optional) Error class object.
+ :type error: :py:class:`linebot.models.error.Error`
+ """
+ super(LineBotApiError, self).__init__(error.message)
+
+ self.status_code = status_code
+ self.error = error
+
+ def __str__(self):
+ """str.
+
+ :rtype: str
+ :return:
+ """
+ return '{0}: status_code={1}, error_response={2}'.format(
+ self.__class__.__name__, self.status_code, self.error)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.http_client module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta, abstractmethod, abstractproperty
+
+import requests
+from future.utils import with_metaclass
+
+
+class HttpClient(with_metaclass(ABCMeta)):
+ """Abstract Base Classes of HttpClient."""
+
+ DEFAULT_TIMEOUT = 5
+
+ def __init__(self, timeout=DEFAULT_TIMEOUT):
+ """__init__ method.
+
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`DEFAULT_TIMEOUT`
+ :type timeout: float | tuple(float, float)
+ :rtype: T <= :py:class:`HttpResponse`
+ :return: HttpResponse instance
+ """
+ self.timeout = timeout
+
+ @abstractmethod
+ def get(self, url, headers=None, params=None, stream=False, timeout=None):
+ """GET request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param dict params: (optional) Request query parameter
+ :param bool stream: (optional) get content as stream
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: T <= :py:class:`HttpResponse`
+ :return: HttpResponse instance
+ """
+ raise NotImplementedError
+
+ @abstractmethod
+ def post(self, url, headers=None, data=None, timeout=None):
+ """POST request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param data: (optional) Dictionary, bytes, or file-like object to send in the body
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: T <= :py:class:`HttpResponse`
+ :return: HttpResponse instance
+ """
+ raise NotImplementedError
+
+ @abstractmethod
+ def delete(self, url, headers=None, data=None, timeout=None):
+ """DELETE request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param data: (optional) Dictionary, bytes, or file-like object to send in the body
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: T <= :py:class:`HttpResponse`
+ :return: HttpResponse instance
+ """
+ raise NotImplementedError
+
+
+class RequestsHttpClient(HttpClient):
+ """HttpClient implemented by requests."""
+
+ def __init__(self, timeout=HttpClient.DEFAULT_TIMEOUT):
+ """__init__ method.
+
+ :param timeout: (optional) How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`DEFAULT_TIMEOUT`
+ :type timeout: float | tuple(float, float)
+ """
+ super(RequestsHttpClient, self).__init__(timeout)
+
+ def get(self, url, headers=None, params=None, stream=False, timeout=None):
+ """GET request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param dict params: (optional) Request query parameter
+ :param bool stream: (optional) get content as stream
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`RequestsHttpResponse`
+ :return: RequestsHttpResponse instance
+ """
+ if timeout is None:
+ timeout = self.timeout
+
+ response = requests.get(
+ url, headers=headers, params=params, stream=stream, timeout=timeout
+ )
+
+ return RequestsHttpResponse(response)
+
+ def post(self, url, headers=None, data=None, timeout=None):
+ """POST request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param data: (optional) Dictionary, bytes, or file-like object to send in the body
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`RequestsHttpResponse`
+ :return: RequestsHttpResponse instance
+ """
+ if timeout is None:
+ timeout = self.timeout
+
+ response = requests.post(
+ url, headers=headers, data=data, timeout=timeout
+ )
+
+ return RequestsHttpResponse(response)
+
+ def delete(self, url, headers=None, data=None, timeout=None):
+ """DELETE request.
+
+ :param str url: Request url
+ :param dict headers: (optional) Request headers
+ :param data: (optional) Dictionary, bytes, or file-like object to send in the body
+ :param timeout: (optional), How long to wait for the server
+ to send data before giving up, as a float,
+ or a (connect timeout, read timeout) float tuple.
+ Default is :py:attr:`self.timeout`
+ :type timeout: float | tuple(float, float)
+ :rtype: :py:class:`RequestsHttpResponse`
+ :return: RequestsHttpResponse instance
+ """
+ if timeout is None:
+ timeout = self.timeout
+
+ response = requests.delete(
+ url, headers=headers, data=data, timeout=timeout
+ )
+
+ return RequestsHttpResponse(response)
+
+
+class HttpResponse(with_metaclass(ABCMeta)):
+ """HttpResponse."""
+
+ @abstractproperty
+ def status_code(self):
+ """Get status code."""
+ raise NotImplementedError
+
+ @abstractproperty
+ def headers(self):
+ """Get headers."""
+ raise NotImplementedError
+
+ @abstractproperty
+ def text(self):
+ """Get request body as text-decoded."""
+ raise NotImplementedError
+
+ @abstractproperty
+ def content(self):
+ """Get request body as binary."""
+ raise NotImplementedError
+
+ @abstractproperty
+ def json(self):
+ """Get request body as json-decoded."""
+ raise NotImplementedError
+
+ @abstractmethod
+ def iter_content(self, chunk_size=1024, decode_unicode=False):
+ """Get request body as iterator content (stream).
+
+ :param int chunk_size:
+ :param bool decode_unicode:
+ """
+ raise NotImplementedError
+
+
+class RequestsHttpResponse(HttpResponse):
+ """HttpResponse implemented by requests lib's response."""
+
+ def __init__(self, response):
+ """__init__ method.
+
+ :param response: requests lib's response
+ """
+ self.response = response
+
+ @property
+ def status_code(self):
+ """Get status code."""
+ return self.response.status_code
+
+ @property
+ def headers(self):
+ """Get headers."""
+ return self.response.headers
+
+ @property
+ def text(self):
+ """Get request body as text-decoded."""
+ return self.response.text
+
+ @property
+ def content(self):
+ """Get request body as binary."""
+ return self.response.content
+
+ @property
+ def json(self):
+ """Get request body as json-decoded."""
+ return self.response.json()
+
+ def iter_content(self, chunk_size=1024, decode_unicode=False):
+ """Get request body as iterator content (stream).
+
+ :param int chunk_size:
+ :param bool decode_unicode:
+ """
+ return self.response.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models package."""
+
+from .actions import ( # noqa
+ Action,
+ PostbackAction,
+ MessageAction,
+ URIAction,
+ DatetimePickerAction,
+ CameraAction,
+ CameraRollAction,
+ LocationAction,
+ Action as TemplateAction, # backward compatibility
+ PostbackAction as PostbackTemplateAction, # backward compatibility
+ MessageAction as MessageTemplateAction, # backward compatibility
+ URIAction as URITemplateAction, # backward compatibility
+ DatetimePickerAction as DatetimePickerTemplateAction, # backward compatibility
+)
+from .base import ( # noqa
+ Base,
+)
+from .error import ( # noqa
+ Error,
+ ErrorDetail,
+)
+from .events import ( # noqa
+ Event,
+ MessageEvent,
+ FollowEvent,
+ UnfollowEvent,
+ JoinEvent,
+ LeaveEvent,
+ PostbackEvent,
+ AccountLinkEvent,
+ BeaconEvent,
+ Postback,
+ Beacon,
+ Link,
+)
+from .flex_message import ( # noqa
+ FlexSendMessage,
+ FlexContainer,
+ BubbleContainer,
+ BubbleStyle,
+ BlockStyle,
+ CarouselContainer,
+ FlexComponent,
+ BoxComponent,
+ ButtonComponent,
+ FillerComponent,
+ IconComponent,
+ ImageComponent,
+ SeparatorComponent,
+ SpacerComponent,
+ TextComponent
+)
+from .imagemap import ( # noqa
+ ImagemapSendMessage,
+ BaseSize,
+ ImagemapAction,
+ URIImagemapAction,
+ MessageImagemapAction,
+ ImagemapArea,
+)
+from .messages import ( # noqa
+ Message,
+ TextMessage,
+ ImageMessage,
+ VideoMessage,
+ AudioMessage,
+ LocationMessage,
+ StickerMessage,
+ FileMessage,
+)
+from .responses import ( # noqa
+ Profile,
+ MemberIds,
+ Content,
+ RichMenuResponse,
+ Content as MessageContent, # backward compatibility
+)
+from .rich_menu import ( # noqa
+ RichMenu,
+ RichMenuSize,
+ RichMenuArea,
+ RichMenuBounds,
+)
+from .send_messages import ( # noqa
+ SendMessage,
+ TextSendMessage,
+ ImageSendMessage,
+ VideoSendMessage,
+ AudioSendMessage,
+ LocationSendMessage,
+ StickerSendMessage,
+ QuickReply,
+ QuickReplyButton,
+)
+from .sources import ( # noqa
+ Source,
+ SourceUser,
+ SourceGroup,
+ SourceRoom,
+)
+from .template import ( # noqa
+ TemplateSendMessage,
+ Template,
+ ButtonsTemplate,
+ ConfirmTemplate,
+ CarouselTemplate,
+ CarouselColumn,
+ ImageCarouselTemplate,
+ ImageCarouselColumn,
+)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.template module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .base import Base
+
+
+def get_action(action):
+ """Get action."""
+ action_obj = Base.get_or_new_from_json_dict_with_types(
+ action, {
+ 'postback': PostbackAction,
+ 'message': MessageAction,
+ 'uri': URIAction,
+ 'datetimepicker': DatetimePickerAction,
+ 'camera': CameraAction,
+ 'cameraRoll': CameraRollAction,
+ 'location': LocationAction,
+ }
+ )
+ return action_obj
+
+
+def get_actions(actions):
+ """Get actions."""
+ new_actions = []
+ if actions:
+ for action in actions:
+ action_obj = get_action(action)
+ if action_obj:
+ new_actions.append(action_obj)
+
+ return new_actions
+
+
+class Action(with_metaclass(ABCMeta, Base)):
+ """Abstract base class of Action."""
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(Action, self).__init__(**kwargs)
+
+ self.type = None
+
+
+class PostbackAction(Action):
+ """PostbackAction.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#postback-action
+
+ When a control associated with this action is tapped,
+ a postback event is returned via webhook with the specified string in the data property.
+ """
+
+ def __init__(self, label=None, data=None, display_text=None, text=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action.
+ :param str data: String returned via webhook
+ in the postback.data property of the postback event.
+ :param str display_text: Text displayed in the chat as a message sent by
+ the user when the action is performed.
+ :param str text: Deprecated. Text displayed in the chat as a message sent by
+ the user when the action is performed. Returned from the server through a webhook.
+ :param kwargs:
+ """
+ super(PostbackAction, self).__init__(**kwargs)
+
+ self.type = 'postback'
+ self.label = label
+ self.data = data
+ self.display_text = display_text
+ self.text = text
+
+
+class MessageAction(Action):
+ """MessageAction.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#message-action
+
+ When a control associated with this action is tapped,
+ the string in the text property is sent as a message from the user.
+ """
+
+ def __init__(self, label=None, text=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action.
+ :param str text: Text sent when the action is performed.
+ :param kwargs:
+ """
+ super(MessageAction, self).__init__(**kwargs)
+
+ self.type = 'message'
+ self.label = label
+ self.text = text
+
+
+class URIAction(Action):
+ """URIAction.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#uri-action
+
+ When a control associated with this action is tapped,
+ the URI specified in the uri property is opened.
+ """
+
+ def __init__(self, label=None, uri=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action
+ Max: 20 characters
+ :param str uri: URI opened when the action is performed.
+ :param kwargs:
+ """
+ super(URIAction, self).__init__(**kwargs)
+
+ self.type = 'uri'
+ self.label = label
+ self.uri = uri
+
+
+class DatetimePickerAction(Action):
+ """DatetimePickerAction.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#datetime-picker-action
+
+ When a control associated with this action is tapped,
+ a postback event is returned via webhook with the date and time
+ selected by the user from the date and time selection dialog.
+ The datetime picker action does not support time zones.
+ """
+
+ def __init__(self, label=None, data=None, mode=None,
+ initial=None, max=None, min=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action
+ :param str data: String returned via webhook
+ in the postback.data property of the postback event
+ :param str mode: Action mode
+ date: Pick date
+ time: Pick time
+ datetime: Pick date and time
+ :param str initial: Initial value of date or time
+ :param str max: Largest date or time value that can be selected.
+ Must be greater than the min value.
+ :param str min: Smallest date or time value that can be selected.
+ Must be less than the max value.
+ :param kwargs:
+ """
+ super(DatetimePickerAction, self).__init__(**kwargs)
+
+ self.type = 'datetimepicker'
+ self.label = label
+ self.data = data
+ self.mode = mode
+ self.initial = initial
+ self.max = max
+ self.min = min
+
+
+class CameraAction(Action):
+ """CameraAction.
+
+ https://developers.line.me/en/reference/messaging-api/#camera-action
+
+ This action can be configured only with quick reply buttons.
+ When a button associated with this action is tapped,
+ the camera screen in the LINE app is opened.
+ """
+
+ def __init__(self, label=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action
+ :param kwargs:
+ """
+ super(CameraAction, self).__init__(**kwargs)
+
+ self.type = 'camera'
+ self.label = label
+
+
+class CameraRollAction(Action):
+ """CameraRollAction.
+
+ https://developers.line.me/en/reference/messaging-api/#camera-roll-action
+
+ This action can be configured only with quick reply buttons.
+ When a button associated with this action is tapped,
+ the camera roll screen in the LINE app is opened.
+ """
+
+ def __init__(self, label=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action
+ :param kwargs:
+ """
+ super(CameraRollAction, self).__init__(**kwargs)
+
+ self.type = 'cameraRoll'
+ self.label = label
+
+
+class LocationAction(Action):
+ """LocationRollAction.
+
+ https://developers.line.me/en/reference/messaging-api/#location-action
+
+ This action can be configured only with quick reply buttons.
+ When a button associated with this action is tapped,
+ the location screen in the LINE app is opened.
+ """
+
+ def __init__(self, label=None, **kwargs):
+ """__init__ method.
+
+ :param str label: Label for the action
+ :param kwargs:
+ """
+ super(LocationAction, self).__init__(**kwargs)
+
+ self.type = 'location'
+ self.label = label
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.base module."""
+
+from __future__ import unicode_literals
+
+import json
+
+from .. import utils
+
+
+class Base(object):
+ """Base class of model.
+
+ Suitable for JSON base data.
+ """
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ pass
+
+ def __str__(self):
+ """__str__ method.
+
+ :return:
+ """
+ return self.as_json_string()
+
+ def __repr__(self):
+ """__repr__ method.
+
+ :return:
+ """
+ return str(self)
+
+ def __eq__(self, other):
+ """__eq__ method.
+
+ :param other:
+ :return:
+ """
+ return other and self.as_json_dict() == other.as_json_dict()
+
+ def __ne__(self, other):
+ """__ne__ method.
+
+ :param other:
+ :return:
+ """
+ return not self.__eq__(other)
+
+ def as_json_string(self):
+ """Return JSON string from this object.
+
+ :rtype: str
+ :return:
+ """
+ return json.dumps(self.as_json_dict(), sort_keys=True)
+
+ def as_json_dict(self):
+ """Return dictionary from this object.
+
+ :return: dict
+ """
+ data = {}
+ for key, value in self.__dict__.items():
+ camel_key = utils.to_camel_case(key)
+ if isinstance(value, (list, tuple, set)):
+ data[camel_key] = list()
+ for item in value:
+ if hasattr(item, 'as_json_dict'):
+ data[camel_key].append(item.as_json_dict())
+ else:
+ data[camel_key].append(item)
+
+ elif hasattr(value, 'as_json_dict'):
+ data[camel_key] = value.as_json_dict()
+ elif value is not None:
+ data[camel_key] = value
+
+ return data
+
+ @classmethod
+ def new_from_json_dict(cls, data):
+ """Create a new instance from a dict.
+
+ :param data: JSON dict
+ :rtype:
+ :return:
+ """
+ new_data = {utils.to_snake_case(key): value
+ for key, value in data.items()}
+
+ return cls(**new_data)
+
+ @staticmethod
+ def get_or_new_from_json_dict(data, cls):
+ """Get `cls` object w/ deserialization from json if needed.
+
+ If data is instance of cls, return data.
+ Else if data is instance of dict, create instance from dict.
+ Else, return None.
+
+ :param data:
+ :param cls:
+ :rtype: object
+ :return:
+ """
+ if isinstance(data, cls):
+ return data
+ elif isinstance(data, dict):
+ return cls.new_from_json_dict(data)
+
+ return None
+
+ @staticmethod
+ def get_or_new_from_json_dict_with_types(
+ data, cls_map, type_key='type'
+ ):
+ """Get `cls` object w/ deserialization from json by using type key hint if needed.
+
+ If data is instance of one of cls, return data.
+ Else if data is instance of dict, create instance from dict.
+ Else, return None.
+
+ :param data:
+ :param cls_map:
+ :param type_key:
+ :rtype: object
+ :return:
+ """
+ if isinstance(data, tuple(cls_map.values())):
+ return data
+ elif isinstance(data, dict):
+ type_val = data[type_key]
+ if type_val in cls_map:
+ return cls_map[type_val].new_from_json_dict(data)
+
+ return None
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.error module."""
+
+from __future__ import unicode_literals
+
+from .base import Base
+
+
+class Error(Base):
+ """Error response of LINE messaging API.
+
+ https://devdocs.line.me/en/#error-response
+ """
+
+ def __init__(self, message=None, details=None, **kwargs):
+ """__init__ method.
+
+ :param str message: Summary of the error
+ :param details: ErrorDetail instance list
+ :type details: list[T <= :py:class:`linebot.models.error.ErrorDetail`]
+ :type
+ :param kwargs:
+ """
+ super(Error, self).__init__(**kwargs)
+
+ self.message = message
+
+ new_details = []
+ if details:
+ for detail in details:
+ new_details.append(
+ self.get_or_new_from_json_dict(detail, ErrorDetail)
+ )
+ self.details = new_details
+
+
+class ErrorDetail(Base):
+ """ErrorDetail response of LINE messaging API.
+
+ https://devdocs.line.me/en/#error-response
+ """
+
+ def __init__(self, message=None, property=None, **kwargs):
+ """__init__ method.
+
+ https://devdocs.line.me/en/#error-response
+
+ :param str message: Details of the error message
+ :param str property: Related property
+ :param kwargs:
+ """
+ super(ErrorDetail, self).__init__(**kwargs)
+
+ self.message = message
+ self.property = property
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.events module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .base import Base
+from .messages import (
+ TextMessage,
+ ImageMessage,
+ VideoMessage,
+ AudioMessage,
+ LocationMessage,
+ StickerMessage,
+ FileMessage
+)
+from .sources import SourceUser, SourceGroup, SourceRoom
+
+
+class Event(with_metaclass(ABCMeta, Base)):
+ """Abstract Base Class of Webhook Event.
+
+ https://devdocs.line.me/en/#webhook-event-object
+ """
+
+ def __init__(self, timestamp=None, source=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 kwargs:
+ """
+ super(Event, self).__init__(**kwargs)
+
+ self.type = None
+ self.timestamp = timestamp
+ self.source = self.get_or_new_from_json_dict_with_types(
+ source, {
+ 'user': SourceUser,
+ 'group': SourceGroup,
+ 'room': SourceRoom,
+ }
+ )
+
+
+class MessageEvent(Event):
+ """Webhook MessageEvent.
+
+ https://devdocs.line.me/en/#message-event
+
+ Event object which contains the sent message.
+ The message field contains a message object which corresponds with the message type.
+ You can reply to message events.
+ """
+
+ def __init__(self, timestamp=None, source=None, reply_token=None, message=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 message: Message object
+ :type message: T <= :py:class:`linebot.models.messages.Message`
+ :param kwargs:
+ """
+ super(MessageEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'message'
+ self.reply_token = reply_token
+ self.message = self.get_or_new_from_json_dict_with_types(
+ message, {
+ 'text': TextMessage,
+ 'image': ImageMessage,
+ 'video': VideoMessage,
+ 'audio': AudioMessage,
+ 'location': LocationMessage,
+ 'sticker': StickerMessage,
+ 'file': FileMessage
+ }
+ )
+
+
+class FollowEvent(Event):
+ """Webhook FollowEvent.
+
+ https://devdocs.line.me/en/#follow-event
+
+ Event object for when your account is added as a friend (or unblocked).
+ You can reply to follow events.
+ """
+
+ def __init__(self, timestamp=None, source=None, reply_token=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 kwargs:
+ """
+ super(FollowEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'follow'
+ self.reply_token = reply_token
+
+
+class UnfollowEvent(Event):
+ """Webhook UnfollowEvent.
+
+ https://devdocs.line.me/en/#unfollow-event
+
+ Event object for when your account is blocked.
+ """
+
+ def __init__(self, timestamp=None, source=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 kwargs:
+ """
+ super(UnfollowEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'unfollow'
+
+
+class JoinEvent(Event):
+ """Webhook JoinEvent.
+
+ https://devdocs.line.me/en/#join-event
+
+ Event object for when your account joins a group or talk room.
+ You can reply to join events.
+ """
+
+ def __init__(self, timestamp=None, source=None, reply_token=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 kwargs:
+ """
+ super(JoinEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'join'
+ self.reply_token = reply_token
+
+
+class LeaveEvent(Event):
+ """Webhook LeaveEvent.
+
+ https://devdocs.line.me/en/#leave-event
+
+ Event object for when your account leaves a group.
+ """
+
+ def __init__(self, timestamp=None, source=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 kwargs:
+ """
+ super(LeaveEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'leave'
+
+
+class PostbackEvent(Event):
+ """Webhook PostbackEvent.
+
+ https://devdocs.line.me/en/#postback-event
+
+ Event object for when a user performs an action on
+ a template message which initiates a postback.
+ You can reply to postback events.
+ """
+
+ def __init__(self, timestamp=None, source=None, reply_token=None, postback=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 postback: Postback object
+ :type postback: :py:class:`linebot.models.events.Postback`
+ :param kwargs:
+ """
+ super(PostbackEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'postback'
+ self.reply_token = reply_token
+ self.postback = self.get_or_new_from_json_dict(
+ postback, Postback
+ )
+
+
+class BeaconEvent(Event):
+ """Webhook BeaconEvent.
+
+ https://devdocs.line.me/en/#beacon-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):
+ """__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 beacon: Beacon object
+ :type beacon: :py:class:`linebot.models.events.Beacon`
+ :param kwargs:
+ """
+ super(BeaconEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'beacon'
+ self.reply_token = reply_token
+ self.beacon = self.get_or_new_from_json_dict(
+ beacon, Beacon
+ )
+
+
+class AccountLinkEvent(Event):
+ """Webhook AccountLinkEvent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#account-link-event
+
+ Event object for when a user has linked his/her LINE account with a provider's service account.
+ You can reply to account link events.
+ If the link token has expired or has already been used,
+ 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):
+ """__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 link: Link object
+ :type link: :py:class:`linebot.models.events.Link`
+ :param kwargs:
+ """
+ super(AccountLinkEvent, self).__init__(
+ timestamp=timestamp, source=source, **kwargs
+ )
+
+ self.type = 'accountLink'
+ self.reply_token = reply_token
+ self.link = self.get_or_new_from_json_dict(
+ link, Link
+ )
+
+
+class Postback(Base):
+ """Postback.
+
+ https://devdocs.line.me/en/#postback-event
+ """
+
+ def __init__(self, data=None, params=None, **kwargs):
+ """__init__ method.
+
+ :param str data: Postback data
+ :param dict params: JSON object with the date and time
+ selected by a user through a datetime picker action.
+ Only returned for postback actions via the datetime picker.
+ :param kwargs:
+ """
+ super(Postback, self).__init__(**kwargs)
+
+ self.data = data
+ self.params = params
+
+
+class Beacon(Base):
+ """Beacon.
+
+ https://devdocs.line.me/en/#beacon-event
+ """
+
+ def __init__(self, type=None, hwid=None, dm=None, **kwargs):
+ """__init__ method.
+
+ :param str type: Type of beacon event
+ :param str hwid: Hardware ID of the beacon that was detected
+ :param str dm: Optional. Device message of beacon which is hex string
+ :param kwargs:
+ """
+ super(Beacon, self).__init__(**kwargs)
+
+ self.type = type
+ self.hwid = hwid
+ self.dm = dm
+
+ @property
+ def device_message(self):
+ """Get dm(device_message) as bytearray.
+
+ :rtype: bytearray
+ :return:
+ """
+ return bytearray.fromhex(self.dm) if self.dm is not None else None
+
+
+class Link(Base):
+ """Link.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#link-object
+ """
+
+ def __init__(self, result=None, nonce=None, **kwargs):
+ """__init__ method.
+
+ :param str result: Indicate whether the link was successful or not.
+ :param str nonce: Specified nonce when verifying the user ID.
+ """
+ super(Link, self).__init__(**kwargs)
+
+ self.result = result
+ self.nonce = nonce
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.flex_message module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .actions import get_action
+from .base import Base
+from .send_messages import SendMessage
+
+
+class FlexSendMessage(SendMessage):
+ """FlexSendMessage.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#flex-message
+
+ Flex Messages are messages with a customizable layout.
+ You can customize the layout freely by combining multiple elements.
+ """
+
+ def __init__(self, alt_text=None, contents=None, **kwargs):
+ """__init__ method.
+
+ :param str alt_text: Alternative text
+ :param contents: Flex Message container object
+ :type contents: :py:class:`linebot.models.flex_message.FlexContainer`
+ :param kwargs:
+ """
+ super(FlexSendMessage, self).__init__(**kwargs)
+
+ self.type = 'flex'
+ self.alt_text = alt_text
+ self.contents = contents
+ self.contents = self.get_or_new_from_json_dict_with_types(
+ contents, {
+ 'bubble': BubbleContainer,
+ 'carousel': CarouselContainer
+ }
+ )
+
+
+class FlexContainer(with_metaclass(ABCMeta, Base)):
+ """FlexContainer.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#container
+
+ A container is the top-level structure of a Flex Message.
+ """
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(FlexContainer, self).__init__(**kwargs)
+
+ self.type = None
+
+
+class BubbleContainer(FlexContainer):
+ """BubbleContainer.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#bubble-container
+
+ This is a container that contains one message bubble.
+ It can contain four blocks: header, hero, body, and footer.
+ """
+
+ def __init__(self, direction=None, header=None, hero=None, body=None, footer=None, styles=None,
+ **kwargs):
+ """__init__ method.
+
+ :param str direction: Text directionality and the order of components
+ in horizontal boxes in the container
+ :param header: Header block
+ :type header: :py:class:`linebot.models.flex_message.BoxComponent`
+ :param hero: Hero block
+ :type hero: :py:class:`linebot.models.flex_message.ImageComponent`
+ :param body: Body block
+ :type body: :py:class:`linebot.models.flex_message.BoxComponent`
+ :param footer: Footer block
+ :type footer: :py:class:`linebot.models.flex_message.BoxComponent`
+ :param styles: Style of each block
+ :type styles: :py:class:`linebot.models.flex_message.BubbleStyle`
+ :param kwargs:
+ """
+ super(BubbleContainer, self).__init__(**kwargs)
+
+ self.type = 'bubble'
+ self.direction = direction
+ self.header = self.get_or_new_from_json_dict(header, BoxComponent)
+ self.hero = self.get_or_new_from_json_dict(hero, ImageComponent)
+ self.body = self.get_or_new_from_json_dict(body, BoxComponent)
+ self.footer = self.get_or_new_from_json_dict(footer, BoxComponent)
+ self.styles = self.get_or_new_from_json_dict(styles, BubbleStyle)
+
+
+class BubbleStyle(with_metaclass(ABCMeta, Base)):
+ """BubbleStyle.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#objects-for-the-block-style
+ """
+
+ def __init__(self, header=None, hero=None, body=None, footer=None, **kwargs):
+ """__init__ method.
+
+ :param header: Style of the header block
+ :type header: :py:class:`linebot.models.flex_message.BlockStyle`
+ :param hero: Style of the hero block
+ :type hero: :py:class:`linebot.models.flex_message.BlockStyle`
+ :param body: Style of the body block
+ :type body: :py:class:`linebot.models.flex_message.BlockStyle`
+ :param footer: Style of the footer block
+ :type footer: :py:class:`linebot.models.flex_message.BlockStyle`
+ :param kwargs:
+ """
+ super(BubbleStyle, self).__init__(**kwargs)
+
+ self.header = self.get_or_new_from_json_dict(header, BlockStyle)
+ self.hero = self.get_or_new_from_json_dict(hero, BlockStyle)
+ self.body = self.get_or_new_from_json_dict(body, BlockStyle)
+ self.footer = self.get_or_new_from_json_dict(footer, BlockStyle)
+
+
+class BlockStyle(with_metaclass(ABCMeta, Base)):
+ """BlockStyle.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#objects-for-the-block-style
+ """
+
+ def __init__(self, background_color=None, separator=None, separator_color=None, **kwargs):
+ """__init__ method.
+
+ :param str background_color: Background color of the block. Use a hexadecimal color code
+ :param bool separator: True to place a separator above the block
+ True will be ignored for the first block in a container
+ because you cannot place a separator above the first block.
+ The default value is False
+ :param str separator_color: Color of the separator. Use a hexadecimal color code
+ :param kwargs:
+ """
+ super(BlockStyle, self).__init__(**kwargs)
+ self.background_color = background_color
+ self.separator = separator
+ self.separator_color = separator_color
+
+
+class CarouselContainer(FlexContainer):
+ """CarouselContainer.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#carousel-container
+
+ This is a container that contains multiple bubble containers, or message bubbles.
+ The bubbles will be shown in order by scrolling horizontally.
+ """
+
+ def __init__(self, contents=None, **kwargs):
+ """__init__ method.
+
+ :param contents: Array of bubble containers
+ :type contents: list[T <= :py:class:`linebot.models.flex_message.BubbleContainer`]
+ :param kwargs:
+ """
+ super(CarouselContainer, self).__init__(**kwargs)
+
+ self.type = 'carousel'
+
+ new_contents = []
+ if contents:
+ for it in contents:
+ new_contents.append(self.get_or_new_from_json_dict(
+ it, BubbleContainer
+ ))
+ self.contents = new_contents
+
+
+class FlexComponent(with_metaclass(ABCMeta, Base)):
+ """FlexComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#component
+
+ Components are objects that compose a Flex Message container.
+ """
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(FlexComponent, self).__init__(**kwargs)
+
+ self.type = None
+
+
+class BoxComponent(FlexComponent):
+ """BoxComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#box-component
+
+ This is a component that defines the layout of child components.
+ You can also include a box in a box.
+ """
+
+ def __init__(self, layout=None, contents=None, flex=None, spacing=None, margin=None, **kwargs):
+ """__init__ method.
+
+ :param str layout: The placement style of components in this box
+ :param contents: Components in this box
+ :param float flex: The ratio of the width or height of this box within the parent box
+ :param str spacing: Minimum space between components in this box
+ :param str margin: Minimum space between this box
+ and the previous component in the parent box
+ :param kwargs:
+ """
+ super(BoxComponent, self).__init__(**kwargs)
+ self.type = 'box'
+ self.layout = layout
+ self.flex = flex
+ self.spacing = spacing
+ self.margin = margin
+
+ new_contents = []
+ if contents:
+ for it in contents:
+ new_contents.append(self.get_or_new_from_json_dict_with_types(
+ it, {
+ 'box': BoxComponent,
+ 'button': ButtonComponent,
+ 'filler': FillerComponent,
+ 'icon': IconComponent,
+ 'image': ImageComponent,
+ 'separator': SeparatorComponent,
+ 'spacer': SpacerComponent,
+ 'text': TextComponent
+ }
+ ))
+ self.contents = new_contents
+
+
+class ButtonComponent(FlexComponent):
+ """ButtonComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#button-component
+
+ This component draws a button.
+ When the user taps a button, a specified action is performed.
+ """
+
+ def __init__(self, action=None, flex=None, margin=None, height=None, style=None, color=None,
+ gravity=None, **kwargs):
+ """__init__ method.
+
+ :param action: Action performed when this button is tapped
+ :type action: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param float flex: The ratio of the width or height of this component within the parent box
+ :param str margin: Minimum space between this component
+ and the previous component in the parent box
+ :param str height: Height of the button
+ :param str style: Style of the button
+ :param str color: Character color when the style property is link.
+ Background color when the style property is primary or secondary.
+ Use a hexadecimal color code
+ :param str gravity: Vertical alignment style
+ :param kwargs:
+ """
+ super(ButtonComponent, self).__init__(**kwargs)
+ self.type = 'button'
+ self.action = get_action(action)
+ self.flex = flex
+ self.margin = margin
+ self.height = height
+ self.style = style
+ self.color = color
+ self.gravity = gravity
+
+
+class FillerComponent(FlexComponent):
+ """FillerComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#filler-component
+
+ This is an invisible component to fill extra space between components.
+ """
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(FillerComponent, self).__init__(**kwargs)
+ self.type = 'filler'
+
+
+class IconComponent(FlexComponent):
+ """IconComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#icon-component
+
+ This component draws an icon.
+ """
+
+ def __init__(self, url=None, margin=None, size=None, aspect_ratio=None, **kwargs):
+ """__init__ method.
+
+ :param str url: Image URL
+ Protocol: HTTPS
+ Image format: JPEG or PNG
+ :param str margin: Minimum space between this component
+ and the previous component in the parent box
+ :param str size: Maximum size of the icon width
+ :param str aspect_ratio: Aspect ratio of the icon
+ :param kwargs:
+ """
+ super(IconComponent, self).__init__(**kwargs)
+ self.type = 'icon'
+ self.url = url
+ self.margin = margin
+ self.size = size
+ self.aspect_ratio = aspect_ratio
+
+
+class ImageComponent(FlexComponent):
+ """ImageComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#image-component
+
+ This component draws an image.
+ """
+
+ def __init__(self, url=None, flex=None, margin=None, align=None, gravity=None, size=None,
+ aspect_ratio=None, aspect_mode=None, background_color=None, action=None,
+ **kwargs):
+ """__init__ method.
+
+ :param str url: Image URL
+ Protocol: HTTPS
+ Image format: JPEG or PNG
+ :param float flex: The ratio of the width or height of this component within the parent box
+ :param str margin: Minimum space between this component
+ and the previous component in the parent box
+ :param str align: Horizontal alignment style
+ :param str gravity: Vertical alignment style
+ :param str size: Maximum size of the image width
+ :param str aspect_ratio: Aspect ratio of the image
+ :param str aspect_mode: Style of the image
+ :param str background_color: Background color of the image. Use a hexadecimal color code.
+ :param action: Action performed when this image is tapped
+ :type action: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param kwargs:
+ """
+ super(ImageComponent, self).__init__(**kwargs)
+ self.type = 'image'
+ self.url = url
+ self.flex = flex
+ self.margin = margin
+ self.align = align
+ self.gravity = gravity
+ self.size = size
+ self.aspect_ratio = aspect_ratio
+ self.aspect_mode = aspect_mode
+ self.background_color = background_color
+ self.action = get_action(action)
+
+
+class SeparatorComponent(FlexComponent):
+ """SeparatorComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#separator-component
+
+ This component draws a separator between components in the parent box.
+ """
+
+ def __init__(self, margin=None, color=None, **kwargs):
+ """__init__ method.
+
+ :param str margin: Minimum space between this component
+ and the previous component in the parent box
+ :param str color: Color of the separator. Use a hexadecimal color code
+ :param kwargs:
+ """
+ super(SeparatorComponent, self).__init__(**kwargs)
+ self.type = 'separator'
+ self.margin = margin
+ self.color = color
+
+
+class SpacerComponent(FlexComponent):
+ """SpacerComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#spacer-component
+
+ This is an invisible component that places a fixed-size space
+ at the beginning or end of the box
+ """
+
+ def __init__(self, size=None, **kwargs):
+ """__init__ method.
+
+ :param str size: Size of the space
+ :param kwargs:
+ """
+ super(SpacerComponent, self).__init__(**kwargs)
+ self.type = 'spacer'
+ self.size = size
+
+
+class TextComponent(FlexComponent):
+ """TextComponent.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#text-component
+
+ This component draws text. You can format the text.
+ """
+
+ def __init__(self, text=None, flex=None, margin=None, size=None, align=None, gravity=None,
+ wrap=None, weight=None,
+ color=None, action=None, **kwargs):
+ r"""__init__ method.
+
+ :param str text: Text
+ :param float flex: The ratio of the width or height of this component within the parent box
+ :param str margin: Minimum space between this component
+ and the previous component in the parent box
+ :param str size: Font size
+ :param str align: Horizontal alignment style
+ :param str gravity: Vertical alignment style
+ :param bool wrap: rue to wrap text. The default value is False.
+ If set to True, you can use a new line character (\n) to begin on a new line.
+ :param str weight: Font weight
+ :param str color: Font color
+ :param action: Action performed when this image is tapped
+ :type action: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param kwargs:
+ """
+ super(TextComponent, self).__init__(**kwargs)
+ self.type = 'text'
+ self.text = text
+ self.flex = flex
+ self.margin = margin
+ self.size = size
+ self.align = align
+ self.gravity = gravity
+ self.wrap = wrap
+ self.weight = weight
+ self.color = color
+ self.action = get_action(action)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.imagemap module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .base import Base
+from .send_messages import SendMessage
+
+
+class ImagemapSendMessage(SendMessage):
+ """ImagemapSendMessage.
+
+ https://devdocs.line.me/en/#imagemap-message
+
+ Imagemaps are images with one or more links. You can assign one link for the entire image
+ or multiple links which correspond to different regions of the image.
+ """
+
+ def __init__(self, base_url=None, alt_text=None, base_size=None, actions=None, **kwargs):
+ """__init__ method.
+
+ :param str base_url: Base URL of image.
+ HTTPS
+ :param str alt_text: Alternative text
+ :param base_size: Width and height of base image
+ :type base_size: :py:class:`linebot.models.imagemap.BaseSize`
+ :param actions: Action when tapped
+ :type actions: list[T <= :py:class:`linebot.models.imagemap.ImagemapAction`]
+ :param kwargs:
+ """
+ super(ImagemapSendMessage, self).__init__(**kwargs)
+
+ self.type = 'imagemap'
+ self.base_url = base_url
+ self.alt_text = alt_text
+ self.base_size = self.get_or_new_from_json_dict(
+ base_size, BaseSize
+ )
+
+ new_actions = []
+ if actions:
+ for action in actions:
+ action_obj = self.get_or_new_from_json_dict_with_types(
+ action, {
+ 'uri': URIImagemapAction,
+ 'message': MessageImagemapAction
+ }
+ )
+ if action_obj:
+ new_actions.append(action_obj)
+ self.actions = new_actions
+
+
+class BaseSize(Base):
+ """BaseSize.
+
+ https://devdocs.line.me/en/#imagemap-message
+ """
+
+ def __init__(self, width=None, height=None, **kwargs):
+ """__init__ method.
+
+ https://devdocs.line.me/en/#imagemap-message
+
+ :param int width: Width of base image (set to 1040px)
+ :param int height: Height of base image(set to the height
+ that corresponds to a width of 1040px
+ :param kwargs:
+ """
+ super(BaseSize, self).__init__(**kwargs)
+
+ self.width = width
+ self.height = height
+
+
+class ImagemapAction(with_metaclass(ABCMeta, Base)):
+ """ImagemapAction.
+
+ https://devdocs.line.me/en/#imagemap-message
+ """
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(ImagemapAction, self).__init__(**kwargs)
+
+ self.type = None
+
+
+class URIImagemapAction(ImagemapAction):
+ """URIImagemapAction.
+
+ https://devdocs.line.me/en/#imagemap-message
+ """
+
+ def __init__(self, link_uri=None, area=None, **kwargs):
+ """__init__ method.
+
+ :param str link_uri: Webpage URL
+ :param area: Defined tappable area
+ :type area: :py:class:`linebot.models.imagemap.ImagemapArea`
+ :param kwargs:
+ """
+ super(URIImagemapAction, self).__init__(**kwargs)
+
+ self.type = 'uri'
+ self.link_uri = link_uri
+ self.area = self.get_or_new_from_json_dict(area, ImagemapArea)
+
+
+class MessageImagemapAction(ImagemapAction):
+ """MessageImagemapAction.
+
+ https://devdocs.line.me/en/#imagemap-message
+ """
+
+ def __init__(self, text=None, area=None, **kwargs):
+ """__init__ method.
+
+ :param str text: Message to send
+ :param area: Defined tappable area
+ :type area: :py:class:`linebot.models.imagemap.ImagemapArea`
+ :param kwargs:
+ """
+ super(MessageImagemapAction, self).__init__(**kwargs)
+
+ self.type = 'message'
+ self.text = text
+ self.area = self.get_or_new_from_json_dict(area, ImagemapArea)
+
+
+class ImagemapArea(Base):
+ """ImagemapArea.
+
+ https://devdocs.line.me/en/#imagemap-area-object
+
+ Defines the size of the full imagemap with the width as 1040px.
+ The top left is used as the origin of the area.
+ """
+
+ def __init__(self, x=None, y=None, width=None, height=None, **kwargs):
+ """__init__ method.
+
+ :param int x: Horizontal position of the tappable area
+ :param int y: Vertical position of the tappable area
+ :param int width: Width of the tappable area
+ :param int height: Height of the tappable area
+ :param kwargs:
+ """
+ super(ImagemapArea, self).__init__(**kwargs)
+
+ self.x = x
+ self.y = y
+ self.width = width
+ self.height = height
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.messages module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .base import Base
+
+
+class Message(with_metaclass(ABCMeta, Base)):
+ """Abstract Base Class of Message."""
+
+ def __init__(self, id=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param kwargs:
+ """
+ super(Message, self).__init__(**kwargs)
+
+ self.type = None
+ self.id = id
+
+
+class TextMessage(Message):
+ """TextMessage.
+
+ https://devdocs.line.me/en/#text-message
+
+ Message object which contains the text sent from the source.
+ """
+
+ def __init__(self, id=None, text=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param str text: Message text
+ :param kwargs:
+ """
+ super(TextMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'text'
+ self.text = text
+
+
+class ImageMessage(Message):
+ """ImageMessage.
+
+ https://devdocs.line.me/en/#image-message
+
+ Message object which contains the image content sent from the source.
+ The binary image data can be retrieved with the Content API.
+ """
+
+ def __init__(self, id=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param kwargs:
+ """
+ super(ImageMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'image'
+
+
+class VideoMessage(Message):
+ """VideoMessage.
+
+ https://devdocs.line.me/en/#video-message
+
+ Message object which contains the video content sent from the source.
+ The binary video data can be retrieved with the Content API.
+ """
+
+ def __init__(self, id=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param kwargs:
+ """
+ super(VideoMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'video'
+
+
+class AudioMessage(Message):
+ """AudioMessage.
+
+ https://devdocs.line.me/en/#audio-message
+
+ Message object which contains the audio content sent from the source.
+ The binary audio data can be retrieved with the Content API.
+ """
+
+ def __init__(self, id=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param kwargs:
+ """
+ super(AudioMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'audio'
+
+
+class LocationMessage(Message):
+ """LocationMessage.
+
+ https://devdocs.line.me/en/#location-message
+ """
+
+ def __init__(self, id=None, title=None, address=None, latitude=None, longitude=None,
+ **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param str title: Title
+ :param str address: Address
+ :param float latitude: Latitude
+ :param float longitude: Longitude
+ :param kwargs:
+ """
+ super(LocationMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'location'
+ self.title = title
+ self.address = address
+ self.latitude = latitude
+ self.longitude = longitude
+
+
+class StickerMessage(Message):
+ """StickerMessage.
+
+ https://devdocs.line.me/en/#sticker-message
+
+ Message object which contains the sticker data sent from the source.
+ For a list of basic LINE stickers and sticker IDs, see sticker list.
+ """
+
+ def __init__(self, id=None, package_id=None, sticker_id=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param str package_id: Package ID
+ :param str sticker_id: Sticker ID
+ :param kwargs:
+ """
+ super(StickerMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'sticker'
+ self.package_id = package_id
+ self.sticker_id = sticker_id
+
+
+class FileMessage(Message):
+ """FileMessage.
+
+ https://devdocs.line.me/en/#file-message
+
+ Message object which contains the file content sent from the source.
+ The binary file data can be retrieved with the Content API.
+ """
+
+ def __init__(self, id=None, file_name=None, file_size=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Message ID
+ :param str file_name: File Name
+ :param int file_size: File Size
+ :param kwargs:
+ """
+ super(FileMessage, self).__init__(id=id, **kwargs)
+
+ self.type = 'file'
+ self.file_size = file_size
+ self.file_name = file_name
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.responses module."""
+
+from __future__ import unicode_literals
+
+from .base import Base
+from .rich_menu import RichMenuSize, RichMenuArea
+
+
+class Profile(Base):
+ """Profile.
+
+ https://devdocs.line.me/en/#bot-api-get-profile
+ """
+
+ def __init__(self, display_name=None, user_id=None, picture_url=None,
+ status_message=None, **kwargs):
+ """__init__ method.
+
+ :param str display_name: Display name
+ :param str user_id: User ID
+ :param str picture_url: Image URL
+ :param str status_message: Status message
+ :param kwargs:
+ """
+ super(Profile, self).__init__(**kwargs)
+
+ self.display_name = display_name
+ self.user_id = user_id
+ self.picture_url = picture_url
+ self.status_message = status_message
+
+
+class MemberIds(Base):
+ """MemberIds.
+
+ https://devdocs.line.me/en/#get-group-room-member-ids
+ """
+
+ def __init__(self, member_ids=None, next=None, **kwargs):
+ """__init__ method.
+
+ :param member_ids: List of user IDs of the members in the group or room.
+ Max: 100 user IDs
+ :type member_ids: list[str]
+ :param str next: continuationToken.
+ Only returned when there are more user IDs remaining in memberIds.
+ :param kwargs:
+ """
+ super(MemberIds, self).__init__(**kwargs)
+
+ self.member_ids = member_ids
+ self.next = next
+
+
+class Content(object):
+ """MessageContent.
+
+ https://devdocs.line.me/ja/#get-content
+ """
+
+ def __init__(self, response):
+ """__init__ method.
+
+ :param response: HttpResponse object
+ :type response: T <= :py:class:`linebot.http_client.HttpResponse`
+ """
+ self.response = response
+
+ @property
+ def content_type(self):
+ """Get Content-type header value.
+
+ :rtype: str
+ :return: content-type header value
+ """
+ return self.response.headers.get('content-type')
+
+ @property
+ def content(self):
+ """Get content.
+
+ If content size is large, should use iter_content.
+
+ :rtype: binary
+ """
+ return self.response.content
+
+ def iter_content(self, chunk_size=1024):
+ """Get content as iterator (stream).
+
+ If content size is large, should use this.
+
+ :param chunk_size: Chunk size
+ :rtype: iterator
+ :return:
+ """
+ return self.response.iter_content(chunk_size=chunk_size)
+
+
+class RichMenuResponse(Base):
+ """RichMenuResponse.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#rich-menu-response-object
+ """
+
+ def __init__(self, rich_menu_id=None, size=None, selected=None, name=None,
+ chat_bar_text=None, areas=None, **kwargs):
+ """__init__ method.
+
+ :param str id: Rich Menu ID
+ :param size: size object which describe the rich menu displayed in the chat.
+ Rich menu images must be one of the following sizes: 2500x1686, 2500x843.
+ :type size: :py:class:`linebot.models.rich_menu.RichMenuSize`
+ :param bool selected: true to display the rich menu by default. Otherwise, false.
+ :param str name: Name of the rich menu.
+ Maximum of 300 characters.
+ :param str chat_bar_text: Text displayed in the chat bar.
+ Maximum of 14 characters.
+ :param areas: Array of area objects which define coordinates and size of tappable areas.
+ Maximum of 20 area objects.
+ :type areas: list[T <= :py:class:`linebot.models.rich_menu.RichMenuArea`]
+ :param kwargs:
+ """
+ super(RichMenuResponse, self).__init__(**kwargs)
+
+ self.rich_menu_id = rich_menu_id
+ self.size = self.get_or_new_from_json_dict(size, RichMenuSize)
+ self.selected = selected
+ self.name = name
+ self.chat_bar_text = chat_bar_text
+
+ new_areas = []
+ if areas:
+ for area in areas:
+ new_areas.append(
+ self.get_or_new_from_json_dict(area, RichMenuArea)
+ )
+ self.areas = new_areas
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.rich_menu module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .actions import get_action
+from .base import Base
+
+
+class RichMenu(with_metaclass(ABCMeta, Base)):
+ """RichMenu.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#rich-menu-object
+ """
+
+ def __init__(self, size=None, selected=None, name=None, chat_bar_text=None,
+ areas=None, **kwargs):
+ """__init__ method.
+
+ :param size: size object which describe the rich menu displayed in the chat.
+ Rich menu images must be one of the following sizes: 2500x1686, 2500x843.
+ :type size: :py:class:`linebot.models.rich_menu.RichMenuSize`
+ :param bool selected: true to display the rich menu by default. Otherwise, false.
+ :param str name: Name of the rich menu.
+ Maximum of 300 characters.
+ :param str chatBarText: Text displayed in the chat bar.
+ Maximum of 14 characters.
+ :param areas: Array of area objects which define coordinates and size of tappable areas.
+ Maximum of 20 area objects.
+ :type areas: list[T <= :py:class:`linebot.models.rich_menu.RichMenuArea`]
+ :param kwargs:
+ """
+ super(RichMenu, self).__init__(**kwargs)
+
+ self.size = self.get_or_new_from_json_dict(size, RichMenuSize)
+ self.selected = selected
+ self.name = name
+ self.chat_bar_text = chat_bar_text
+
+ new_areas = []
+ if areas:
+ for area in areas:
+ new_areas.append(
+ self.get_or_new_from_json_dict(area, RichMenuArea)
+ )
+ self.areas = new_areas
+
+
+class RichMenuSize(with_metaclass(ABCMeta, Base)):
+ """RichMenuSize.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#size-object
+ """
+
+ def __init__(self, width=None, height=None, **kwargs):
+ """__init__ method.
+
+ :param int width: Width of the rich menu. Must be 2500.
+ :param int height: Height of the rich menu. Possible values: 1686, 843.
+ :param kwargs:
+ """
+ super(RichMenuSize, self).__init__(**kwargs)
+
+ self.width = width
+ self.height = height
+
+
+class RichMenuArea(with_metaclass(ABCMeta, Base)):
+ """RichMenuArea.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#area-object
+ """
+
+ def __init__(self, bounds=None, action=None, **kwargs):
+ """__init__ method.
+
+ :param bounds: Object describing the boundaries of the area in pixels. See bounds object.
+ :type bounds: :py:class:`linebot.models.rich_menu.RichMenuBound`
+ :param action: Action performed when the area is tapped. See action objects.
+ :type action: T <= :py:class:`linebot.models.actions.Action`
+ :param kwargs:
+ """
+ super(RichMenuArea, self).__init__(**kwargs)
+
+ self.bounds = self.get_or_new_from_json_dict(bounds, RichMenuBounds)
+ self.action = get_action(action)
+
+
+class RichMenuBounds(with_metaclass(ABCMeta, Base)):
+ """RichMenuBounds.
+
+ https://developers.line.me/en/docs/messaging-api/reference/#bounds-object
+ """
+
+ def __init__(self, x=None, y=None, width=None, height=None, **kwargs):
+ """__init__ method.
+
+ :param int x: Horizontal position relative to the top-left corner of the area.
+ :param int y: Vertical position relative to the top-left corner of the area.
+ :param int width: Width of the area.
+ :param int height: Height of the area.
+ :param kwargs:
+ """
+ super(RichMenuBounds, self).__init__(**kwargs)
+
+ self.x = x
+ self.y = y
+ self.width = width
+ self.height = height
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.send_messages module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .actions import get_action
+from .base import Base
+
+
+class SendMessage(with_metaclass(ABCMeta, Base)):
+ """Abstract Base Class of SendMessage."""
+
+ def __init__(self, quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(SendMessage, self).__init__(**kwargs)
+
+ self.type = None
+ self.quick_reply = self.get_or_new_from_json_dict(quick_reply, QuickReply)
+
+
+class TextSendMessage(SendMessage):
+ """TextSendMessage.
+
+ https://devdocs.line.me/en/#text
+ """
+
+ def __init__(self, text=None, quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str text: Message text
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(TextSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'text'
+ self.text = text
+
+
+class ImageSendMessage(SendMessage):
+ """ImageSendMessage.
+
+ https://devdocs.line.me/en/#image
+ """
+
+ def __init__(self, original_content_url=None, preview_image_url=None,
+ quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str original_content_url: Image URL.
+ HTTPS
+ JPEG
+ Max: 1024 x 1024
+ Max: 1 MB
+ :param str preview_image_url: Preview image URL
+ HTTPS
+ JPEG
+ Max: 240 x 240
+ Max: 1 MB
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(ImageSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'image'
+ self.original_content_url = original_content_url
+ self.preview_image_url = preview_image_url
+
+
+class VideoSendMessage(SendMessage):
+ """VideoSendMessage.
+
+ https://devdocs.line.me/en/#video
+ """
+
+ def __init__(self, original_content_url=None, preview_image_url=None,
+ quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str original_content_url: URL of video file.
+ HTTPS
+ mp4
+ Less than 1 minute
+ Max: 10 MB
+ :param str preview_image_url: URL of preview image.
+ HTTPS
+ JPEG
+ Max: 240 x 240
+ Max: 1 MB
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(VideoSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'video'
+ self.original_content_url = original_content_url
+ self.preview_image_url = preview_image_url
+
+
+class AudioSendMessage(SendMessage):
+ """AudioSendMessage.
+
+ https://devdocs.line.me/en/#audio
+ """
+
+ def __init__(self, original_content_url=None, duration=None, quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str original_content_url: URL of audio file.
+ HTTPS
+ m4a
+ Less than 1 minute
+ Max 10 MB
+ :param long duration: Length of audio file (milliseconds).
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(AudioSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'audio'
+ self.original_content_url = original_content_url
+ self.duration = duration
+
+
+class LocationSendMessage(SendMessage):
+ """LocationSendMessage.
+
+ https://devdocs.line.me/en/#location
+ """
+
+ def __init__(self, title=None, address=None, latitude=None, longitude=None,
+ quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str title: Title
+ :param str address: Address
+ :param float latitude: Latitude
+ :param float longitude: Longitude
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(LocationSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'location'
+ self.title = title
+ self.address = address
+ self.latitude = latitude
+ self.longitude = longitude
+
+
+class StickerSendMessage(SendMessage):
+ """StickerSendMessage.
+
+ https://devdocs.line.me/en/#sticker
+ """
+
+ def __init__(self, package_id=None, sticker_id=None, quick_reply=None, **kwargs):
+ """__init__ method.
+
+ :param str package_id: Package ID
+ :param str sticker_id: Sticker ID
+ :param quick_reply: QuickReply object
+ :type quick_reply: T <= :py:class:`linebot.models.send_messages.QuickReply`
+ :param kwargs:
+ """
+ super(StickerSendMessage, self).__init__(quick_reply=quick_reply, **kwargs)
+
+ self.type = 'sticker'
+ self.package_id = package_id
+ self.sticker_id = sticker_id
+
+
+class QuickReply(with_metaclass(ABCMeta, Base)):
+ """QuickReply.
+
+ https://developers.line.me/en/docs/messaging-api/using-quick-reply/
+ """
+
+ def __init__(self, items=None, **kwargs):
+ """__init__ method.
+
+ :param items: Quick reply button objects
+ :type items: list[T <= :py:class:`linebot.models.send_messages.QuickReplyButton`]
+ :param kwargs:
+ """
+ super(QuickReply, self).__init__(**kwargs)
+
+ new_items = []
+ if items:
+ for item in items:
+ new_items.append(self.get_or_new_from_json_dict(
+ item, QuickReplyButton
+ ))
+ self.items = new_items
+
+
+class QuickReplyButton(with_metaclass(ABCMeta, Base)):
+ """QuickReplyButton.
+
+ https://developers.line.me/en/reference/messaging-api/#items-object
+ """
+
+ def __init__(self, image_url=None, action=None, **kwargs):
+ """__init__ method.
+
+ :param str image_url: URL of the icon that is displayed
+ at the beginning of the button
+ :param action: Action performed when this button is tapped
+ :type action: T <= :py:class:`linebot.models.actions.Action`
+ :param kwargs:
+ """
+ super(QuickReplyButton, self).__init__(**kwargs)
+
+ self.type = 'action'
+ self.image_url = image_url
+ self.action = get_action(action)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.sources module."""
+
+from __future__ import unicode_literals
+
+import warnings
+from abc import ABCMeta, abstractproperty
+
+from future.utils import with_metaclass
+
+from .base import Base
+
+
+class Source(with_metaclass(ABCMeta, Base)):
+ """Abstract Base Class of Source."""
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(Source, self).__init__(**kwargs)
+ self.type = None
+
+ @abstractproperty
+ def sender_id(self):
+ """Abstract property of id to send a message.
+
+ If SourceUser, return user_id.
+ If SourceGroup, return group_id.
+ If SourceRoom, return room_id.
+
+ 'sender_id' is deprecated.
+
+ :rtype: str
+ :return:
+ """
+ warnings.warn("'sender_id' is deprecated.", DeprecationWarning, stacklevel=2)
+ raise NotImplementedError
+
+
+class SourceUser(Source):
+ """SourceUser.
+
+ https://devdocs.line.me/en/#source-user
+
+ JSON object which contains the source user of the event.
+ """
+
+ def __init__(self, user_id=None, **kwargs):
+ """__init__ method.
+
+ :param str user_id: ID of the source user
+ :param kwargs:
+ """
+ super(SourceUser, self).__init__(**kwargs)
+
+ self.type = 'user'
+ self.user_id = user_id
+
+ @property
+ def sender_id(self):
+ """Alias of user_id.
+
+ 'sender_id' is deprecated. Use 'user_id' instead.
+
+ :rtype: str
+ :return:
+ """
+ warnings.warn("'sender_id' is deprecated.", DeprecationWarning, stacklevel=2)
+ return self.user_id
+
+
+class SourceGroup(Source):
+ """SourceGroup.
+
+ https://devdocs.line.me/en/#source-group
+
+ JSON object which contains the source group of the event.
+ """
+
+ def __init__(self, group_id=None, user_id=None, **kwargs):
+ """__init__ method.
+
+ :param str group_id: ID of the source group
+ :param str user_id: ID of the source user
+ :param kwargs:
+ """
+ super(SourceGroup, self).__init__(**kwargs)
+
+ self.type = 'group'
+ self.group_id = group_id
+ self.user_id = user_id
+
+ @property
+ def sender_id(self):
+ """Alias of group_id.
+
+ 'sender_id' is deprecated. Use 'group_id' instead.
+
+ :rtype: str
+ :return:
+ """
+ warnings.warn("'sender_id' is deprecated.", DeprecationWarning, stacklevel=2)
+ return self.group_id
+
+
+class SourceRoom(Source):
+ """SourceRoom.
+
+ https://devdocs.line.me/en/#source-room
+
+ JSON object which contains the source room of the event.
+ """
+
+ def __init__(self, room_id=None, user_id=None, **kwargs):
+ """__init__ method.
+
+ :param str room_id: ID of the source room
+ :param str user_id: ID of the source user
+ :param kwargs:
+ """
+ super(SourceRoom, self).__init__(**kwargs)
+
+ self.type = 'room'
+ self.room_id = room_id
+ self.user_id = user_id
+
+ @property
+ def sender_id(self):
+ """Alias of room_id.
+
+ 'sender_id' is deprecated. Use 'room_id' instead.
+
+ :rtype: str
+ :return:
+ """
+ warnings.warn("'sender_id' is deprecated.", DeprecationWarning, stacklevel=2)
+ return self.room_id
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.models.template module."""
+
+from __future__ import unicode_literals
+
+from abc import ABCMeta
+
+from future.utils import with_metaclass
+
+from .actions import get_action, get_actions
+from .base import Base
+from .send_messages import SendMessage
+
+
+class TemplateSendMessage(SendMessage):
+ """TemplateSendMessage.
+
+ https://devdocs.line.me/en/#template-messages
+
+ Template messages are messages with predefined layouts which you can customize.
+ There are three types of templates available
+ that can be used to interact with users through your bot.
+ """
+
+ def __init__(self, alt_text=None, template=None, **kwargs):
+ """__init__ method.
+
+ :param str alt_text: Alternative text for unsupported devices.
+ :param template: Object with the contents of the template.
+ :type template: T <= :py:class:`linebot.models.template.Template`
+ :param kwargs:
+ """
+ super(TemplateSendMessage, self).__init__(**kwargs)
+
+ self.type = 'template'
+ self.alt_text = alt_text
+ self.template = self.get_or_new_from_json_dict_with_types(
+ template, {
+ 'buttons': ButtonsTemplate,
+ 'confirm': ConfirmTemplate,
+ 'carousel': CarouselTemplate,
+ 'image_carousel': ImageCarouselTemplate
+ }
+ )
+
+
+class Template(with_metaclass(ABCMeta, Base)):
+ """Abstract Base Class of Template."""
+
+ def __init__(self, **kwargs):
+ """__init__ method.
+
+ :param kwargs:
+ """
+ super(Template, self).__init__(**kwargs)
+
+ self.type = None
+
+
+class ButtonsTemplate(Template):
+ """ButtonsTemplate.
+
+ https://devdocs.line.me/en/#buttons
+
+ Template message with an image, title, text, and multiple action buttons.
+ """
+
+ def __init__(self, text=None, title=None, thumbnail_image_url=None,
+ image_aspect_ratio=None,
+ image_size=None, image_background_color=None,
+ actions=None, **kwargs):
+ """__init__ method.
+
+ :param str text: Message text.
+ Max: 160 characters (no image or title).
+ Max: 60 characters (message with an image or title)
+ :param str title: Title.
+ Max: 40 characters
+ :param str thumbnail_image_url: Image URL.
+ HTTPS
+ JPEG or PNG
+ Aspect ratio: 1:1.51
+ Max width: 1024px
+ Max: 1 MB
+ :param str image_aspect_ratio: Aspect ratio of the image.
+ Specify one of the following values:
+ rectangle: 1.51:1
+ square: 1:1
+ :param str image_size: Size of the image.
+ Specify one of the following values:
+ cover: The image fills the entire image area.
+ Parts of the image that do not fit in the area are not displayed.
+ contain: The entire image is displayed in the image area.
+ A background is displayed in the unused areas to the left and right
+ of vertical images and in the areas above and below horizontal images.
+ :param str image_background_color: Background color of image.
+ Specify a RGB color value.
+ :param actions: Action when tapped.
+ Max: 4
+ :type actions: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param kwargs:
+ """
+ super(ButtonsTemplate, self).__init__(**kwargs)
+
+ self.type = 'buttons'
+ self.text = text
+ self.title = title
+ self.thumbnail_image_url = thumbnail_image_url
+ self.image_aspect_ratio = image_aspect_ratio
+ self.image_size = image_size
+ self.image_background_color = image_background_color
+ self.actions = get_actions(actions)
+
+
+class ConfirmTemplate(Template):
+ """ConfirmTemplate.
+
+ https://devdocs.line.me/en/#confirm
+
+ Template message with two action buttons.
+ """
+
+ def __init__(self, text=None, actions=None, **kwargs):
+ """__init__ method.
+
+ :param str text: Message text.
+ Max: 240 characters
+ :param actions: Action when tapped.
+ Max: 2
+ :type actions: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param kwargs:
+ """
+ super(ConfirmTemplate, self).__init__(**kwargs)
+
+ self.type = 'confirm'
+ self.text = text
+ self.actions = get_actions(actions)
+
+
+class CarouselTemplate(Template):
+ """CarouselTemplate.
+
+ https://devdocs.line.me/en/#carousel
+
+ Template message with multiple columns which can be cycled like a carousel.
+ """
+
+ def __init__(self, columns=None, image_aspect_ratio=None,
+ image_size=None, **kwargs):
+ """__init__ method.
+
+ :param columns: Array of columns.
+ Max: 10
+ :type columns: list[T <= :py:class:`linebot.models.template.CarouselColumn`]
+ :param str image_aspect_ratio: Aspect ratio of the image.
+ Specify one of the following values:
+ rectangle: 1.51:1
+ square: 1:1
+ :param str image_size: Size of the image.
+ Specify one of the following values:
+ cover: The image fills the entire image area.
+ Parts of the image that do not fit in the area are not displayed.
+ contain: The entire image is displayed in the image area.
+ A background is displayed in the unused areas to the left and right
+ of vertical images and in the areas above and below horizontal images.
+ :param kwargs:
+ """
+ super(CarouselTemplate, self).__init__(**kwargs)
+
+ self.type = 'carousel'
+
+ new_columns = []
+ if columns:
+ for column in columns:
+ new_columns.append(self.get_or_new_from_json_dict(
+ column, CarouselColumn
+ ))
+ self.columns = new_columns
+ self.image_aspect_ratio = image_aspect_ratio
+ self.image_size = image_size
+
+
+class ImageCarouselTemplate(Template):
+ """ImageCarouselTemplate.
+
+ https://devdocs.line.me/en/#image-carousel
+
+ Template message with multiple images columns which can be cycled like as carousel.
+ """
+
+ def __init__(self, columns=None, **kwargs):
+ """__init__ method.
+
+ :param columns: Array of columns.
+ Max: 10
+ :type columns: list[T <= :py:class:`linebot.models.template.ImageCarouselColumn`]
+ :param kwargs:
+ """
+ super(ImageCarouselTemplate, self).__init__(**kwargs)
+
+ self.type = 'image_carousel'
+
+ new_columns = []
+ if columns:
+ for column in columns:
+ new_columns.append(self.get_or_new_from_json_dict(
+ column, ImageCarouselColumn
+ ))
+ self.columns = new_columns
+
+
+class CarouselColumn(Base):
+ """CarouselColumn.
+
+ https://devdocs.line.me/en/#column-object
+ """
+
+ def __init__(self, text=None, title=None, thumbnail_image_url=None,
+ image_background_color=None, actions=None, **kwargs):
+ """__init__ method.
+
+ :param str text: Message text.
+ Max: 120 characters (no image or title)
+ Max: 60 characters (message with an image or title)
+ :param str title: Title.
+ Max: 40 characters
+ :param str thumbnail_image_url: Image URL.
+ HTTPS
+ JPEG or PNG
+ Aspect ratio: 1:1.51
+ Max width: 1024px
+ Max: 1 MB
+ :param str image_background_color: Background color of image.
+ Specify a RGB color value.
+ :param actions: Action when tapped.
+ Max: 3
+ :type actions: list[T <= :py:class:`linebot.models.actions.Action`]
+ :param kwargs:
+ """
+ super(CarouselColumn, self).__init__(**kwargs)
+
+ self.text = text
+ self.title = title
+ self.thumbnail_image_url = thumbnail_image_url
+ self.image_background_color = image_background_color
+ self.actions = get_actions(actions)
+
+
+class ImageCarouselColumn(Base):
+ """ImageCarouselColumn.
+
+ https://devdocs.line.me/en/#column-object-for-image-carousel
+ """
+
+ def __init__(self, image_url=None, action=None, **kwargs):
+ """__init__ method.
+
+ :param str image_url: Image URL.
+ HTTPS
+ JPEG or PNG
+ Aspect ratio: 1:1
+ Max width: 1024px
+ Max: 1 MB
+ :param action: Action when image is tapped
+ Max: 5
+ :type action: T <= :py:class:`linebot.models.actions.Action`
+ :param kwargs:
+ """
+ super(ImageCarouselColumn, self).__init__(**kwargs)
+
+ self.image_url = image_url
+ self.action = get_action(action)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.http_client module."""
+
+from __future__ import unicode_literals
+
+import logging
+import re
+import sys
+
+LOGGER = logging.getLogger('linebot')
+
+PY3 = sys.version_info[0] == 3
+
+
+def to_snake_case(text):
+ """Convert to snake case.
+
+ :param str text:
+ :rtype: str
+ :return:
+ """
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', text)
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+
+def to_camel_case(text):
+ """Convert to camel case.
+
+ :param str text:
+ :rtype: str
+ :return:
+ """
+ split = text.split('_')
+ return split[0] + "".join(x.title() for x in split[1:])
+
+
+def safe_compare_digest(val1, val2):
+ """safe_compare_digest method.
+
+ :param val1: string or bytes for compare
+ :type val1: str | bytes
+ :param val2: string or bytes for compare
+ :type val2: str | bytes
+ """
+ if len(val1) != len(val2):
+ return False
+
+ result = 0
+ if PY3 and isinstance(val1, bytes) and isinstance(val2, bytes):
+ for i, j in zip(val1, val2):
+ result |= i ^ j
+ else:
+ for i, j in zip(val1, val2):
+ result |= (ord(i) ^ ord(j))
+
+ return result == 0
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""linebot.http_client webhook."""
+
+from __future__ import unicode_literals
+
+import base64
+import hashlib
+import hmac
+import inspect
+import json
+
+from .exceptions import InvalidSignatureError
+from .models.events import (
+ MessageEvent,
+ FollowEvent,
+ UnfollowEvent,
+ JoinEvent,
+ LeaveEvent,
+ PostbackEvent,
+ BeaconEvent,
+ AccountLinkEvent,
+)
+from .utils import LOGGER, PY3, safe_compare_digest
+
+
+if hasattr(hmac, "compare_digest"):
+ def compare_digest(val1, val2):
+ """compare_digest function.
+
+ If hmac module has compare_digest function, use it.
+ Or not, use linebot.utils.safe_compare_digest.
+
+ :param val1: string or bytes for compare
+ :type val1: str | bytes
+ :param val2: string or bytes for compare
+ :type val2: str | bytes
+ :rtype: bool
+ :return: result
+ """
+ return hmac.compare_digest(val1, val2)
+else:
+ def compare_digest(val1, val2):
+ """compare_digest function.
+
+ If hmac module has compare_digest function, use it.
+ Or not, use linebot.utils.safe_compare_digest.
+
+ :param val1: string or bytes for compare
+ :type val1: str | bytes
+ :param val2: string or bytes for compare
+ :type val2: str | bytes
+ :rtype: bool
+ :return: result
+ """
+ return safe_compare_digest(val1, val2)
+
+
+class SignatureValidator(object):
+ """Signature validator.
+
+ https://devdocs.line.me/en/#webhook-authentication
+ """
+
+ def __init__(self, channel_secret):
+ """__init__ method.
+
+ :param str channel_secret: Channel secret (as text)
+ """
+ self.channel_secret = channel_secret.encode('utf-8')
+
+ def validate(self, body, signature):
+ """Check signature.
+
+ https://devdocs.line.me/en/#webhook-authentication
+
+ :param str body: Request body (as text)
+ :param str signature: X-Line-Signature value (as text)
+ :rtype: bool
+ :return: result
+ """
+ gen_signature = hmac.new(
+ self.channel_secret,
+ body.encode('utf-8'),
+ hashlib.sha256
+ ).digest()
+
+ return compare_digest(
+ signature.encode('utf-8'), base64.b64encode(gen_signature)
+ )
+
+
+class WebhookParser(object):
+ """Webhook Parser."""
+
+ def __init__(self, channel_secret):
+ """__init__ method.
+
+ :param str channel_secret: Channel secret (as text)
+ """
+ self.signature_validator = SignatureValidator(channel_secret)
+
+ def parse(self, body, signature):
+ """Parse webhook request body as text.
+
+ :param str body: Webhook request body (as text)
+ :param str signature: X-Line-Signature value (as text)
+ :rtype: list[T <= :py:class:`linebot.models.events.Event`]
+ :return:
+ """
+ if not self.signature_validator.validate(body, signature):
+ raise InvalidSignatureError(
+ 'Invalid signature. signature=' + signature)
+
+ body_json = json.loads(body)
+ events = []
+ for event in body_json['events']:
+ event_type = event['type']
+ if event_type == 'message':
+ events.append(MessageEvent.new_from_json_dict(event))
+ elif event_type == 'follow':
+ events.append(FollowEvent.new_from_json_dict(event))
+ elif event_type == 'unfollow':
+ events.append(UnfollowEvent.new_from_json_dict(event))
+ elif event_type == 'join':
+ events.append(JoinEvent.new_from_json_dict(event))
+ elif event_type == 'leave':
+ events.append(LeaveEvent.new_from_json_dict(event))
+ elif event_type == 'postback':
+ events.append(PostbackEvent.new_from_json_dict(event))
+ elif event_type == 'beacon':
+ events.append(BeaconEvent.new_from_json_dict(event))
+ elif event_type == 'accountLink':
+ events.append(AccountLinkEvent.new_from_json_dict(event))
+ else:
+ LOGGER.warn('Unknown event type. type=' + event_type)
+
+ return events
+
+
+class WebhookHandler(object):
+ """Webhook Handler."""
+
+ def __init__(self, channel_secret):
+ """__init__ method.
+
+ :param str channel_secret: Channel secret (as text)
+ """
+ self.parser = WebhookParser(channel_secret)
+ self._handlers = {}
+ self._default = None
+
+ def add(self, event, message=None):
+ """[Decorator] Add handler method.
+
+ :param event: Specify a kind of Event which you want to handle
+ :type event: T <= :py:class:`linebot.models.events.Event` class
+ :param message: (optional) If event is MessageEvent,
+ specify kind of Messages which you want to handle
+ :type: message: T <= :py:class:`linebot.models.messages.Message` class
+ :rtype: func
+ :return: decorator
+ """
+ def decorator(func):
+ if isinstance(message, (list, tuple)):
+ for it in message:
+ self.__add_handler(func, event, message=it)
+ else:
+ self.__add_handler(func, event, message=message)
+
+ return func
+
+ return decorator
+
+ def default(self):
+ """[Decorator] Set default handler method.
+
+ :rtype: func
+ :return:
+ """
+ def decorator(func):
+ self._default = func
+ return func
+
+ return decorator
+
+ def handle(self, body, signature):
+ """Handle webhook.
+
+ :param str body: Webhook request body (as text)
+ :param str signature: X-Line-Signature value (as text)
+ """
+ events = self.parser.parse(body, signature)
+
+ for event in events:
+ func = None
+ key = None
+
+ if isinstance(event, MessageEvent):
+ key = self.__get_handler_key(
+ event.__class__, event.message.__class__)
+ func = self._handlers.get(key, None)
+
+ if func is None:
+ key = self.__get_handler_key(event.__class__)
+ func = self._handlers.get(key, None)
+
+ if func is None:
+ func = self._default
+
+ if func is None:
+ LOGGER.info('No handler of ' + key + ' and no default handler')
+ else:
+ args_count = self.__get_args_count(func)
+ if args_count == 0:
+ func()
+ else:
+ func(event)
+
+ def __add_handler(self, func, event, message=None):
+ key = self.__get_handler_key(event, message=message)
+ self._handlers[key] = func
+
+ @staticmethod
+ def __get_args_count(func):
+ if PY3:
+ arg_spec = inspect.getfullargspec(func)
+ return len(arg_spec.args)
+ else:
+ arg_spec = inspect.getargspec(func)
+ return len(arg_spec.args)
+
+ @staticmethod
+ def __get_handler_key(event, message=None):
+ if message is None:
+ return event.__name__
+ else:
+ return event.__name__ + '_' + message.__name__
tornado==4.4.2
-pymongo==3.4.0
\ No newline at end of file
+future
+requests
\ No newline at end of file
+++ /dev/null
-python-3.6.3