diff --git a/lib/telegram/__init__.py b/lib/telegram/__init__.py new file mode 100644 index 00000000..b3dffe97 --- /dev/null +++ b/lib/telegram/__init__.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""A library that provides a Python interface to the Telegram Bot API""" + +__author__ = 'leandrotoledodesouza@gmail.com' +__version__ = '2.8.7' + +from .base import TelegramObject +from .user import User +from .groupchat import GroupChat +from .photosize import PhotoSize +from .audio import Audio +from .voice import Voice +from .document import Document +from .sticker import Sticker +from .video import Video +from .contact import Contact +from .location import Location +from .chataction import ChatAction +from .userprofilephotos import UserProfilePhotos +from .replymarkup import ReplyMarkup +from .replykeyboardmarkup import ReplyKeyboardMarkup +from .replykeyboardhide import ReplyKeyboardHide +from .forcereply import ForceReply +from .error import TelegramError +from .inputfile import InputFile +from .file import File +from .nullhandler import NullHandler +from .emoji import Emoji +from .parsemode import ParseMode +from .message import Message +from .update import Update +from .bot import Bot + +__all__ = ['Bot', 'Emoji', 'TelegramError', 'InputFile', 'ReplyMarkup', + 'ForceReply', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', + 'UserProfilePhotos', 'ChatAction', 'Location', 'Contact', + 'Video', 'Sticker', 'Document', 'File', 'Audio', 'PhotoSize', + 'GroupChat', 'Update', 'ParseMode', 'Message', 'User', + 'TelegramObject', 'NullHandler', 'Voice'] diff --git a/lib/telegram/audio.py b/lib/telegram/audio.py new file mode 100644 index 00000000..c7a669d3 --- /dev/null +++ b/lib/telegram/audio.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Audio""" + +from telegram import TelegramObject + + +class Audio(TelegramObject): + """This object represents a Telegram Audio. + + Attributes: + file_id (str): + duration (int): + performer (str): + title (str): + mime_type (str): + file_size (int): + + Args: + file_id (str): + duration (int): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + performer (Optional[str]): + title (Optional[str]): + mime_type (Optional[str]): + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + duration, + **kwargs): + # Required + self.file_id = str(file_id) + self.duration = int(duration) + # Optionals + self.performer = kwargs.get('performer', '') + self.title = kwargs.get('title', '') + self.mime_type = str(kwargs.get('mime_type', '')) + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Audio: + """ + if not data: + return None + + return Audio(**data) diff --git a/lib/telegram/base.py b/lib/telegram/base.py new file mode 100644 index 00000000..ac0deac0 --- /dev/null +++ b/lib/telegram/base.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""Base class for Telegram Objects""" + +import json +from abc import ABCMeta + + +class TelegramObject(object): + """Base class for most telegram objects""" + + __metaclass__ = ABCMeta + + def __str__(self): + return str(self.to_dict()) + + def __getitem__(self, item): + return self.__dict__[item] + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.TelegramObject: + """ + raise NotImplementedError + + def to_json(self): + """ + Returns: + str: + """ + return json.dumps(self.to_dict()) + + def to_dict(self): + """ + Returns: + dict: + """ + data = dict() + + for key, value in self.__dict__.items(): + if value: + if hasattr(value, 'to_dict'): + data[key] = value.to_dict() + else: + data[key] = value + + return data diff --git a/lib/telegram/bot.py b/lib/telegram/bot.py new file mode 100644 index 00000000..3805335b --- /dev/null +++ b/lib/telegram/bot.py @@ -0,0 +1,741 @@ +#!/usr/bin/env python +# pylint: disable=E0611,E0213,E1102,C0103,E1101,W0613,R0913,R0904 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Bot""" + +import functools +import logging + +from telegram import (User, Message, Update, UserProfilePhotos, File, + TelegramError, ReplyMarkup, TelegramObject, NullHandler) +from telegram.utils import request + +H = NullHandler() +logging.getLogger(__name__).addHandler(H) + + +class Bot(TelegramObject): + + """This object represents a Telegram Bot. + + Attributes: + id (int): + first_name (str): + last_name (str): + username (str): + name (str): + + Args: + token (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + base_url (Optional[str]): + """ + + def __init__(self, + token, + base_url=None): + self.token = token + + if base_url is None: + self.base_url = 'https://api.telegram.org/bot%s' % self.token + else: + self.base_url = base_url + self.token + + self.base_file_url = 'https://api.telegram.org/file/bot%s' % self.token + + self.bot = None + + self.logger = logging.getLogger(__name__) + + def info(func): + """ + Returns: + """ + @functools.wraps(func) + def decorator(self, *args, **kwargs): + """ + decorator + """ + if not self.bot: + self.getMe() + + result = func(self, *args, **kwargs) + return result + return decorator + + @property + @info + def id(self): + """int: """ + return self.bot.id + + @property + @info + def first_name(self): + """str: """ + return self.bot.first_name + + @property + @info + def last_name(self): + """str: """ + return self.bot.last_name + + @property + @info + def username(self): + """str: """ + return self.bot.username + + @property + def name(self): + """str: """ + return '@%s' % self.username + + def log(func): + """ + Returns: + A telegram.Message instance representing the message posted. + """ + logger = logging.getLogger(func.__module__) + + @functools.wraps(func) + def decorator(self, *args, **kwargs): + """ + decorator + """ + logger.debug('Entering: %s', func.__name__) + result = func(self, *args, **kwargs) + logger.debug(result) + logger.debug('Exiting: %s', func.__name__) + return result + return decorator + + def message(func): + """ + Returns: + A telegram.Message instance representing the message posted. + """ + @functools.wraps(func) + def decorator(self, *args, **kwargs): + """ + decorator + """ + url, data = func(self, *args, **kwargs) + + if not data.get('chat_id'): + raise TelegramError('Invalid chat_id') + + if kwargs.get('reply_to_message_id'): + reply_to_message_id = kwargs.get('reply_to_message_id') + data['reply_to_message_id'] = reply_to_message_id + + if kwargs.get('reply_markup'): + reply_markup = kwargs.get('reply_markup') + if isinstance(reply_markup, ReplyMarkup): + data['reply_markup'] = reply_markup.to_json() + else: + data['reply_markup'] = reply_markup + + result = request.post(url, data) + + if result is True: + return result + + return Message.de_json(result) + return decorator + + @log + def getMe(self): + """A simple method for testing your bot's auth token. + + Returns: + A telegram.User instance representing that bot if the + credentials are valid, None otherwise. + """ + url = '%s/getMe' % self.base_url + + result = request.get(url) + + self.bot = User.de_json(result) + + return self.bot + + @log + @message + def sendMessage(self, + chat_id, + text, + parse_mode=None, + disable_web_page_preview=None, + **kwargs): + """Use this method to send text messages. + + Args: + chat_id: + Unique identifier for the message recipient - telegram.User or + telegram.GroupChat id. + parse_mode: + Send Markdown, if you want Telegram apps to show bold, italic and + inline URLs in your bot's message. For the moment, only Telegram + for Android supports this. [Optional] + text: + Text of the message to be sent. + disable_web_page_preview: + Disables link previews for links in this message. [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a custom + reply keyboard, instructions to hide keyboard or to force a reply + from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendMessage' % self.base_url + + data = {'chat_id': chat_id, + 'text': text} + + if parse_mode: + data['parse_mode'] = parse_mode + if disable_web_page_preview: + data['disable_web_page_preview'] = disable_web_page_preview + + return url, data + + @log + @message + def forwardMessage(self, + chat_id, + from_chat_id, + message_id): + """Use this method to forward messages of any kind. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + from_chat_id: + Unique identifier for the chat where the original message was sent + - User or GroupChat id. + message_id: + Unique message identifier. + + Returns: + A telegram.Message instance representing the message forwarded. + """ + + url = '%s/forwardMessage' % self.base_url + + data = {} + if chat_id: + data['chat_id'] = chat_id + if from_chat_id: + data['from_chat_id'] = from_chat_id + if message_id: + data['message_id'] = message_id + + return url, data + + @log + @message + def sendPhoto(self, + chat_id, + photo, + caption=None, + **kwargs): + """Use this method to send photos. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + photo: + Photo to send. You can either pass a file_id as String to resend a + photo that is already on the Telegram servers, or upload a new + photo using multipart/form-data. + caption: + Photo caption (may also be used when resending photos by file_id). + [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a custom + reply keyboard, instructions to hide keyboard or to force a reply + from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendPhoto' % self.base_url + + data = {'chat_id': chat_id, + 'photo': photo} + + if caption: + data['caption'] = caption + + return url, data + + @log + @message + def sendAudio(self, + chat_id, + audio, + duration=None, + performer=None, + title=None, + **kwargs): + """Use this method to send audio files, if you want Telegram clients to + display them in the music player. Your audio must be in an .mp3 format. + On success, the sent Message is returned. Bots can currently send audio + files of up to 50 MB in size, this limit may be changed in the future. + + For backward compatibility, when both fields title and description are + empty and mime-type of the sent file is not "audio/mpeg", file is sent + as playable voice message. In this case, your audio must be in an .ogg + file encoded with OPUS. This will be removed in the future. You need to + use sendVoice method instead. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + audio: + Audio file to send. You can either pass a file_id as String to + resend an audio that is already on the Telegram servers, or upload + a new audio file using multipart/form-data. + duration: + Duration of sent audio in seconds. [Optional] + performer: + Performer of sent audio. [Optional] + title: + Title of sent audio. [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendAudio' % self.base_url + + data = {'chat_id': chat_id, + 'audio': audio} + + if duration: + data['duration'] = duration + if performer: + data['performer'] = performer + if title: + data['title'] = title + + return url, data + + @log + @message + def sendDocument(self, + chat_id, + document, + filename=None, + **kwargs): + """Use this method to send general files. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + document: + File to send. You can either pass a file_id as String to resend a + file that is already on the Telegram servers, or upload a new file + using multipart/form-data. + filename: + File name that shows in telegram message (it is usefull when you + send file generated by temp module, for example). [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendDocument' % self.base_url + + data = {'chat_id': chat_id, + 'document': document} + + if filename: + data['filename'] = filename + + return url, data + + @log + @message + def sendSticker(self, + chat_id, + sticker, + **kwargs): + """Use this method to send .webp stickers. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + sticker: + Sticker to send. You can either pass a file_id as String to resend + a sticker that is already on the Telegram servers, or upload a new + sticker using multipart/form-data. + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendSticker' % self.base_url + + data = {'chat_id': chat_id, + 'sticker': sticker} + + return url, data + + @log + @message + def sendVideo(self, + chat_id, + video, + duration=None, + caption=None, + **kwargs): + """Use this method to send video files, Telegram clients support mp4 + videos (other formats may be sent as telegram.Document). + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + video: + Video to send. You can either pass a file_id as String to resend a + video that is already on the Telegram servers, or upload a new + video file using multipart/form-data. + duration: + Duration of sent video in seconds. [Optional] + caption: + Video caption (may also be used when resending videos by file_id). + [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendVideo' % self.base_url + + data = {'chat_id': chat_id, + 'video': video} + + if duration: + data['duration'] = duration + if caption: + data['caption'] = caption + + return url, data + + @log + @message + def sendVoice(self, + chat_id, + voice, + duration=None, + **kwargs): + """Use this method to send audio files, if you want Telegram clients to + display the file as a playable voice message. For this to work, your + audio must be in an .ogg file encoded with OPUS (other formats may be + sent as Audio or Document). On success, the sent Message is returned. + Bots can currently send audio files of up to 50 MB in size, this limit + may be changed in the future. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + voice: + Audio file to send. You can either pass a file_id as String to + resend an audio that is already on the Telegram servers, or upload + a new audio file using multipart/form-data. + duration: + Duration of sent audio in seconds. [Optional] + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendVoice' % self.base_url + + data = {'chat_id': chat_id, + 'voice': voice} + + if duration: + data['duration'] = duration + + return url, data + + @log + @message + def sendLocation(self, + chat_id, + latitude, + longitude, + **kwargs): + """Use this method to send point on the map. + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + latitude: + Latitude of location. + longitude: + Longitude of location. + reply_to_message_id: + If the message is a reply, ID of the original message. [Optional] + reply_markup: + Additional interface options. A JSON-serialized object for a + custom reply keyboard, instructions to hide keyboard or to force a + reply from the user. [Optional] + + Returns: + A telegram.Message instance representing the message posted. + """ + + url = '%s/sendLocation' % self.base_url + + data = {'chat_id': chat_id, + 'latitude': latitude, + 'longitude': longitude} + + return url, data + + @log + @message + def sendChatAction(self, + chat_id, + action): + """Use this method when you need to tell the user that something is + happening on the bot's side. The status is set for 5 seconds or less + (when a message arrives from your bot, Telegram clients clear its + typing status). + + Args: + chat_id: + Unique identifier for the message recipient - User or GroupChat id. + action: + Type of action to broadcast. Choose one, depending on what the user + is about to receive: + - ChatAction.TYPING for text messages, + - ChatAction.UPLOAD_PHOTO for photos, + - ChatAction.UPLOAD_VIDEO for videos, + - ChatAction.UPLOAD_AUDIO for audio files, + - ChatAction.UPLOAD_DOCUMENT for general files, + - ChatAction.FIND_LOCATION for location data. + """ + + url = '%s/sendChatAction' % self.base_url + + data = {'chat_id': chat_id, + 'action': action} + + return url, data + + @log + def getUserProfilePhotos(self, + user_id, + offset=None, + limit=100): + """Use this method to get a list of profile pictures for a user. + + Args: + user_id: + Unique identifier of the target user. + offset: + Sequential number of the first photo to be returned. By default, + all photos are returned. [Optional] + limit: + Limits the number of photos to be retrieved. Values between 1-100 + are accepted. Defaults to 100. [Optional] + + Returns: + Returns a telegram.UserProfilePhotos object. + """ + + url = '%s/getUserProfilePhotos' % self.base_url + + data = {'user_id': user_id} + + if offset: + data['offset'] = offset + if limit: + data['limit'] = limit + + result = request.post(url, data) + + return UserProfilePhotos.de_json(result) + + @log + def getFile(self, + file_id): + """Use this method to get basic info about a file and prepare it for + downloading. For the moment, bots can download files of up to 20MB in + size. + + Args: + file_id: + File identifier to get info about. + + Returns: + Returns a telegram.File object + """ + + url = '%s/getFile' % self.base_url + + data = {'file_id': file_id} + + result = request.post(url, data) + + if result.get('file_path'): + result['file_path'] = '%s/%s' % (self.base_file_url, + result['file_path']) + + return File.de_json(result) + + @log + def getUpdates(self, + offset=None, + limit=100, + timeout=0): + """Use this method to receive incoming updates using long polling. + + Args: + offset: + Identifier of the first update to be returned. Must be greater by + one than the highest among the identifiers of previously received + updates. By default, updates starting with the earliest unconfirmed + update are returned. An update is considered confirmed as soon as + getUpdates is called with an offset higher than its update_id. + limit: + Limits the number of updates to be retrieved. Values between 1-100 + are accepted. Defaults to 100. + timeout: + Timeout in seconds for long polling. Defaults to 0, i.e. usual + short polling. + + Returns: + A list of telegram.Update objects are returned. + """ + + url = '%s/getUpdates' % self.base_url + + data = {} + if offset: + data['offset'] = offset + if limit: + data['limit'] = limit + if timeout: + data['timeout'] = timeout + + result = request.post(url, data) + + if result: + self.logger.info( + 'Getting updates: %s', [u['update_id'] for u in result]) + else: + self.logger.info('No new updates found.') + + return [Update.de_json(x) for x in result] + + @log + def setWebhook(self, + webhook_url=None, + certificate=None): + """Use this method to specify a url and receive incoming updates via an + outgoing webhook. Whenever there is an update for the bot, we will send + an HTTPS POST request to the specified url, containing a + JSON-serialized Update. In case of an unsuccessful request, we will + give up after a reasonable amount of attempts. + + Args: + url: + HTTPS url to send updates to. + Use an empty string to remove webhook integration + + Returns: + True if successful else TelegramError was raised + """ + url = '%s/setWebhook' % self.base_url + + data = {} + if webhook_url: + data['url'] = webhook_url + if certificate: + data['certificate'] = certificate + + result = request.post(url, data) + + return result + + @staticmethod + def de_json(data): + pass + + def to_dict(self): + """ + Returns: + dict: + """ + data = {'id': self.id, + 'username': self.username, + 'first_name': self.username} + if self.last_name: + data['last_name'] = self.last_name + return data + + def __reduce__(self): + return (self.__class__, (self.token, + self.base_url.replace(self.token, ''))) diff --git a/lib/telegram/chataction.py b/lib/telegram/chataction.py new file mode 100644 index 00000000..fce7e418 --- /dev/null +++ b/lib/telegram/chataction.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# pylint: disable=R0903 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram ChatAction""" + + +class ChatAction(object): + """This object represents a Telegram ChatAction.""" + + TYPING = 'typing' + UPLOAD_PHOTO = 'upload_photo' + RECORD_VIDEO = 'record_video' + UPLOAD_VIDEO = 'upload_video' + RECORD_AUDIO = 'record_audio' + UPLOAD_AUDIO = 'upload_audio' + UPLOAD_DOCUMENT = 'upload_document' + FIND_LOCATION = 'find_location' diff --git a/lib/telegram/contact.py b/lib/telegram/contact.py new file mode 100644 index 00000000..3b9e2b10 --- /dev/null +++ b/lib/telegram/contact.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Contact""" + +from telegram import TelegramObject + + +class Contact(TelegramObject): + """This object represents a Telegram Contact. + + Attributes: + phone_number (str): + first_name (str): + last_name (str): + user_id (int): + + Args: + phone_number (str): + first_name (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + last_name (Optional[str]): + user_id (Optional[int]): + """ + + def __init__(self, + phone_number, + first_name, + **kwargs): + # Required + self.phone_number = str(phone_number) + self.first_name = first_name + # Optionals + self.last_name = kwargs.get('last_name', '') + self.user_id = int(kwargs.get('user_id', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Contact: + """ + if not data: + return None + + return Contact(**data) diff --git a/lib/telegram/document.py b/lib/telegram/document.py new file mode 100644 index 00000000..d492774d --- /dev/null +++ b/lib/telegram/document.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Document""" + +from telegram import PhotoSize, TelegramObject + + +class Document(TelegramObject): + """This object represents a Telegram Document. + + Attributes: + file_id (str): + thumb (:class:`telegram.PhotoSize`): + file_name (str): + mime_type (str): + file_size (int): + + Args: + file_id (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + thumb (Optional[:class:`telegram.PhotoSize`]): + file_name (Optional[str]): + mime_type (Optional[str]): + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + **kwargs): + # Required + self.file_id = str(file_id) + # Optionals + self.thumb = kwargs.get('thumb') + self.file_name = kwargs.get('file_name', '') + self.mime_type = str(kwargs.get('mime_type', '')) + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Document: + """ + if not data: + return None + + data['thumb'] = PhotoSize.de_json(data.get('thumb')) + + return Document(**data) diff --git a/lib/telegram/emoji.py b/lib/telegram/emoji.py new file mode 100644 index 00000000..ee10651a --- /dev/null +++ b/lib/telegram/emoji.py @@ -0,0 +1,878 @@ +#!/usr/bin/env python +# flake8: noqa +# pylint: disable=C0103,C0301,R0903 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents an Emoji""" + + +class Emoji(object): + """This object represents an Emoji.""" + + GRINNING_FACE_WITH_SMILING_EYES = b'\xF0\x9F\x98\x81' + FACE_WITH_TEARS_OF_JOY = b'\xF0\x9F\x98\x82' + SMILING_FACE_WITH_OPEN_MOUTH = b'\xF0\x9F\x98\x83' + SMILING_FACE_WITH_OPEN_MOUTH_AND_SMILING_EYES = b'\xF0\x9F\x98\x84' + SMILING_FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT = b'\xF0\x9F\x98\x85' + SMILING_FACE_WITH_OPEN_MOUTH_AND_TIGHTLY_CLOSED_EYES = b'\xF0\x9F\x98\x86' + WINKING_FACE = b'\xF0\x9F\x98\x89' + SMILING_FACE_WITH_SMILING_EYES = b'\xF0\x9F\x98\x8A' + FACE_SAVOURING_DELICIOUS_FOOD = b'\xF0\x9F\x98\x8B' + RELIEVED_FACE = b'\xF0\x9F\x98\x8C' + SMILING_FACE_WITH_HEART_SHAPED_EYES = b'\xF0\x9F\x98\x8D' + SMIRKING_FACE = b'\xF0\x9F\x98\x8F' + UNAMUSED_FACE = b'\xF0\x9F\x98\x92' + FACE_WITH_COLD_SWEAT = b'\xF0\x9F\x98\x93' + PENSIVE_FACE = b'\xF0\x9F\x98\x94' + CONFOUNDED_FACE = b'\xF0\x9F\x98\x96' + FACE_THROWING_A_KISS = b'\xF0\x9F\x98\x98' + KISSING_FACE_WITH_CLOSED_EYES = b'\xF0\x9F\x98\x9A' + FACE_WITH_STUCK_OUT_TONGUE_AND_WINKING_EYE = b'\xF0\x9F\x98\x9C' + FACE_WITH_STUCK_OUT_TONGUE_AND_TIGHTLY_CLOSED_EYES = b'\xF0\x9F\x98\x9D' + DISAPPOINTED_FACE = b'\xF0\x9F\x98\x9E' + ANGRY_FACE = b'\xF0\x9F\x98\xA0' + POUTING_FACE = b'\xF0\x9F\x98\xA1' + CRYING_FACE = b'\xF0\x9F\x98\xA2' + PERSEVERING_FACE = b'\xF0\x9F\x98\xA3' + FACE_WITH_LOOK_OF_TRIUMPH = b'\xF0\x9F\x98\xA4' + DISAPPOINTED_BUT_RELIEVED_FACE = b'\xF0\x9F\x98\xA5' + FEARFUL_FACE = b'\xF0\x9F\x98\xA8' + WEARY_FACE = b'\xF0\x9F\x98\xA9' + SLEEPY_FACE = b'\xF0\x9F\x98\xAA' + TIRED_FACE = b'\xF0\x9F\x98\xAB' + LOUDLY_CRYING_FACE = b'\xF0\x9F\x98\xAD' + FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT = b'\xF0\x9F\x98\xB0' + FACE_SCREAMING_IN_FEAR = b'\xF0\x9F\x98\xB1' + ASTONISHED_FACE = b'\xF0\x9F\x98\xB2' + FLUSHED_FACE = b'\xF0\x9F\x98\xB3' + DIZZY_FACE = b'\xF0\x9F\x98\xB5' + FACE_WITH_MEDICAL_MASK = b'\xF0\x9F\x98\xB7' + GRINNING_CAT_FACE_WITH_SMILING_EYES = b'\xF0\x9F\x98\xB8' + CAT_FACE_WITH_TEARS_OF_JOY = b'\xF0\x9F\x98\xB9' + SMILING_CAT_FACE_WITH_OPEN_MOUTH = b'\xF0\x9F\x98\xBA' + SMILING_CAT_FACE_WITH_HEART_SHAPED_EYES = b'\xF0\x9F\x98\xBB' + CAT_FACE_WITH_WRY_SMILE = b'\xF0\x9F\x98\xBC' + KISSING_CAT_FACE_WITH_CLOSED_EYES = b'\xF0\x9F\x98\xBD' + POUTING_CAT_FACE = b'\xF0\x9F\x98\xBE' + CRYING_CAT_FACE = b'\xF0\x9F\x98\xBF' + WEARY_CAT_FACE = b'\xF0\x9F\x99\x80' + FACE_WITH_NO_GOOD_GESTURE = b'\xF0\x9F\x99\x85' + FACE_WITH_OK_GESTURE = b'\xF0\x9F\x99\x86' + PERSON_BOWING_DEEPLY = b'\xF0\x9F\x99\x87' + SEE_NO_EVIL_MONKEY = b'\xF0\x9F\x99\x88' + HEAR_NO_EVIL_MONKEY = b'\xF0\x9F\x99\x89' + SPEAK_NO_EVIL_MONKEY = b'\xF0\x9F\x99\x8A' + HAPPY_PERSON_RAISING_ONE_HAND = b'\xF0\x9F\x99\x8B' + PERSON_RAISING_BOTH_HANDS_IN_CELEBRATION = b'\xF0\x9F\x99\x8C' + PERSON_FROWNING = b'\xF0\x9F\x99\x8D' + PERSON_WITH_POUTING_FACE = b'\xF0\x9F\x99\x8E' + PERSON_WITH_FOLDED_HANDS = b'\xF0\x9F\x99\x8F' + BLACK_SCISSORS = b'\xE2\x9C\x82' + WHITE_HEAVY_CHECK_MARK = b'\xE2\x9C\x85' + AIRPLANE = b'\xE2\x9C\x88' + ENVELOPE = b'\xE2\x9C\x89' + RAISED_FIST = b'\xE2\x9C\x8A' + RAISED_HAND = b'\xE2\x9C\x8B' + VICTORY_HAND = b'\xE2\x9C\x8C' + PENCIL = b'\xE2\x9C\x8F' + BLACK_NIB = b'\xE2\x9C\x92' + HEAVY_CHECK_MARK = b'\xE2\x9C\x94' + HEAVY_MULTIPLICATION_X = b'\xE2\x9C\x96' + SPARKLES = b'\xE2\x9C\xA8' + EIGHT_SPOKED_ASTERISK = b'\xE2\x9C\xB3' + EIGHT_POINTED_BLACK_STAR = b'\xE2\x9C\xB4' + SNOWFLAKE = b'\xE2\x9D\x84' + SPARKLE = b'\xE2\x9D\x87' + CROSS_MARK = b'\xE2\x9D\x8C' + NEGATIVE_SQUARED_CROSS_MARK = b'\xE2\x9D\x8E' + BLACK_QUESTION_MARK_ORNAMENT = b'\xE2\x9D\x93' + WHITE_QUESTION_MARK_ORNAMENT = b'\xE2\x9D\x94' + WHITE_EXCLAMATION_MARK_ORNAMENT = b'\xE2\x9D\x95' + HEAVY_EXCLAMATION_MARK_SYMBOL = b'\xE2\x9D\x97' + HEAVY_BLACK_HEART = b'\xE2\x9D\xA4' + HEAVY_PLUS_SIGN = b'\xE2\x9E\x95' + HEAVY_MINUS_SIGN = b'\xE2\x9E\x96' + HEAVY_DIVISION_SIGN = b'\xE2\x9E\x97' + BLACK_RIGHTWARDS_ARROW = b'\xE2\x9E\xA1' + CURLY_LOOP = b'\xE2\x9E\xB0' + ROCKET = b'\xF0\x9F\x9A\x80' + RAILWAY_CAR = b'\xF0\x9F\x9A\x83' + HIGH_SPEED_TRAIN = b'\xF0\x9F\x9A\x84' + HIGH_SPEED_TRAIN_WITH_BULLET_NOSE = b'\xF0\x9F\x9A\x85' + METRO = b'\xF0\x9F\x9A\x87' + STATION = b'\xF0\x9F\x9A\x89' + BUS = b'\xF0\x9F\x9A\x8C' + BUS_STOP = b'\xF0\x9F\x9A\x8F' + AMBULANCE = b'\xF0\x9F\x9A\x91' + FIRE_ENGINE = b'\xF0\x9F\x9A\x92' + POLICE_CAR = b'\xF0\x9F\x9A\x93' + TAXI = b'\xF0\x9F\x9A\x95' + AUTOMOBILE = b'\xF0\x9F\x9A\x97' + RECREATIONAL_VEHICLE = b'\xF0\x9F\x9A\x99' + DELIVERY_TRUCK = b'\xF0\x9F\x9A\x9A' + SHIP = b'\xF0\x9F\x9A\xA2' + SPEEDBOAT = b'\xF0\x9F\x9A\xA4' + HORIZONTAL_TRAFFIC_LIGHT = b'\xF0\x9F\x9A\xA5' + CONSTRUCTION_SIGN = b'\xF0\x9F\x9A\xA7' + POLICE_CARS_REVOLVING_LIGHT = b'\xF0\x9F\x9A\xA8' + TRIANGULAR_FLAG_ON_POST = b'\xF0\x9F\x9A\xA9' + DOOR = b'\xF0\x9F\x9A\xAA' + NO_ENTRY_SIGN = b'\xF0\x9F\x9A\xAB' + SMOKING_SYMBOL = b'\xF0\x9F\x9A\xAC' + NO_SMOKING_SYMBOL = b'\xF0\x9F\x9A\xAD' + BICYCLE = b'\xF0\x9F\x9A\xB2' + PEDESTRIAN = b'\xF0\x9F\x9A\xB6' + MENS_SYMBOL = b'\xF0\x9F\x9A\xB9' + WOMENS_SYMBOL = b'\xF0\x9F\x9A\xBA' + RESTROOM = b'\xF0\x9F\x9A\xBB' + BABY_SYMBOL = b'\xF0\x9F\x9A\xBC' + TOILET = b'\xF0\x9F\x9A\xBD' + WATER_CLOSET = b'\xF0\x9F\x9A\xBE' + BATH = b'\xF0\x9F\x9B\x80' + CIRCLED_LATIN_CAPITAL_LETTER_M = b'\xE2\x93\x82' + NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_A = b'\xF0\x9F\x85\xB0' + NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_B = b'\xF0\x9F\x85\xB1' + NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_O = b'\xF0\x9F\x85\xBE' + NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_P = b'\xF0\x9F\x85\xBF' + NEGATIVE_SQUARED_AB = b'\xF0\x9F\x86\x8E' + SQUARED_CL = b'\xF0\x9F\x86\x91' + SQUARED_COOL = b'\xF0\x9F\x86\x92' + SQUARED_FREE = b'\xF0\x9F\x86\x93' + SQUARED_ID = b'\xF0\x9F\x86\x94' + SQUARED_NEW = b'\xF0\x9F\x86\x95' + SQUARED_NG = b'\xF0\x9F\x86\x96' + SQUARED_OK = b'\xF0\x9F\x86\x97' + SQUARED_SOS = b'\xF0\x9F\x86\x98' + SQUARED_UP_WITH_EXCLAMATION_MARK = b'\xF0\x9F\x86\x99' + SQUARED_VS = b'\xF0\x9F\x86\x9A' + REGIONAL_INDICATOR_SYMBOL_LETTER_D_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_E\ + = b'\xF0\x9F\x87\xA9\xF0\x9F\x87\xAA' + REGIONAL_INDICATOR_SYMBOL_LETTER_G_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_B\ + = b'\xF0\x9F\x87\xAC\xF0\x9F\x87\xA7' + REGIONAL_INDICATOR_SYMBOL_LETTER_C_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_N\ + = b'\xF0\x9F\x87\xA8\xF0\x9F\x87\xB3' + REGIONAL_INDICATOR_SYMBOL_LETTER_J_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_P\ + = b'\xF0\x9F\x87\xAF\xF0\x9F\x87\xB5' + REGIONAL_INDICATOR_SYMBOL_LETTER_K_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\ + = b'\xF0\x9F\x87\xB0\xF0\x9F\x87\xB7' + REGIONAL_INDICATOR_SYMBOL_LETTER_F_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_R\ + = b'\xF0\x9F\x87\xAB\xF0\x9F\x87\xB7' + REGIONAL_INDICATOR_SYMBOL_LETTER_E_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\ + = b'\xF0\x9F\x87\xAA\xF0\x9F\x87\xB8' + REGIONAL_INDICATOR_SYMBOL_LETTER_I_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_T\ + = b'\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9' + REGIONAL_INDICATOR_SYMBOL_LETTER_U_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_S\ + = b'\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8' + REGIONAL_INDICATOR_SYMBOL_LETTER_R_PLUS_REGIONAL_INDICATOR_SYMBOL_LETTER_U\ + = b'\xF0\x9F\x87\xB7\xF0\x9F\x87\xBA' + SQUARED_KATAKANA_KOKO = b'\xF0\x9F\x88\x81' + SQUARED_KATAKANA_SA = b'\xF0\x9F\x88\x82' + SQUARED_CJK_UNIFIED_IDEOGRAPH_7121 = b'\xF0\x9F\x88\x9A' + SQUARED_CJK_UNIFIED_IDEOGRAPH_6307 = b'\xF0\x9F\x88\xAF' + SQUARED_CJK_UNIFIED_IDEOGRAPH_7981 = b'\xF0\x9F\x88\xB2' + SQUARED_CJK_UNIFIED_IDEOGRAPH_7A7A = b'\xF0\x9F\x88\xB3' + SQUARED_CJK_UNIFIED_IDEOGRAPH_5408 = b'\xF0\x9F\x88\xB4' + SQUARED_CJK_UNIFIED_IDEOGRAPH_6E80 = b'\xF0\x9F\x88\xB5' + SQUARED_CJK_UNIFIED_IDEOGRAPH_6709 = b'\xF0\x9F\x88\xB6' + SQUARED_CJK_UNIFIED_IDEOGRAPH_6708 = b'\xF0\x9F\x88\xB7' + SQUARED_CJK_UNIFIED_IDEOGRAPH_7533 = b'\xF0\x9F\x88\xB8' + SQUARED_CJK_UNIFIED_IDEOGRAPH_5272 = b'\xF0\x9F\x88\xB9' + SQUARED_CJK_UNIFIED_IDEOGRAPH_55B6 = b'\xF0\x9F\x88\xBA' + CIRCLED_IDEOGRAPH_ADVANTAGE = b'\xF0\x9F\x89\x90' + CIRCLED_IDEOGRAPH_ACCEPT = b'\xF0\x9F\x89\x91' + COPYRIGHT_SIGN = b'\xC2\xA9' + REGISTERED_SIGN = b'\xC2\xAE' + DOUBLE_EXCLAMATION_MARK = b'\xE2\x80\xBC' + EXCLAMATION_QUESTION_MARK = b'\xE2\x81\x89' + DIGIT_EIGHT_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x38\xE2\x83\xA3' + DIGIT_NINE_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x39\xE2\x83\xA3' + DIGIT_SEVEN_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x37\xE2\x83\xA3' + DIGIT_SIX_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x36\xE2\x83\xA3' + DIGIT_ONE_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x31\xE2\x83\xA3' + DIGIT_ZERO_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x30\xE2\x83\xA3' + DIGIT_TWO_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x32\xE2\x83\xA3' + DIGIT_THREE_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x33\xE2\x83\xA3' + DIGIT_FIVE_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x35\xE2\x83\xA3' + DIGIT_FOUR_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x34\xE2\x83\xA3' + NUMBER_SIGN_PLUS_COMBINING_ENCLOSING_KEYCAP = b'\x23\xE2\x83\xA3' + TRADE_MARK_SIGN = b'\xE2\x84\xA2' + INFORMATION_SOURCE = b'\xE2\x84\xB9' + LEFT_RIGHT_ARROW = b'\xE2\x86\x94' + UP_DOWN_ARROW = b'\xE2\x86\x95' + NORTH_WEST_ARROW = b'\xE2\x86\x96' + NORTH_EAST_ARROW = b'\xE2\x86\x97' + SOUTH_EAST_ARROW = b'\xE2\x86\x98' + SOUTH_WEST_ARROW = b'\xE2\x86\x99' + LEFTWARDS_ARROW_WITH_HOOK = b'\xE2\x86\xA9' + RIGHTWARDS_ARROW_WITH_HOOK = b'\xE2\x86\xAA' + WATCH = b'\xE2\x8C\x9A' + HOURGLASS = b'\xE2\x8C\x9B' + BLACK_RIGHT_POINTING_DOUBLE_TRIANGLE = b'\xE2\x8F\xA9' + BLACK_LEFT_POINTING_DOUBLE_TRIANGLE = b'\xE2\x8F\xAA' + BLACK_UP_POINTING_DOUBLE_TRIANGLE = b'\xE2\x8F\xAB' + BLACK_DOWN_POINTING_DOUBLE_TRIANGLE = b'\xE2\x8F\xAC' + ALARM_CLOCK = b'\xE2\x8F\xB0' + HOURGLASS_WITH_FLOWING_SAND = b'\xE2\x8F\xB3' + BLACK_SMALL_SQUARE = b'\xE2\x96\xAA' + WHITE_SMALL_SQUARE = b'\xE2\x96\xAB' + BLACK_RIGHT_POINTING_TRIANGLE = b'\xE2\x96\xB6' + BLACK_LEFT_POINTING_TRIANGLE = b'\xE2\x97\x80' + WHITE_MEDIUM_SQUARE = b'\xE2\x97\xBB' + BLACK_MEDIUM_SQUARE = b'\xE2\x97\xBC' + WHITE_MEDIUM_SMALL_SQUARE = b'\xE2\x97\xBD' + BLACK_MEDIUM_SMALL_SQUARE = b'\xE2\x97\xBE' + BLACK_SUN_WITH_RAYS = b'\xE2\x98\x80' + CLOUD = b'\xE2\x98\x81' + BLACK_TELEPHONE = b'\xE2\x98\x8E' + BALLOT_BOX_WITH_CHECK = b'\xE2\x98\x91' + UMBRELLA_WITH_RAIN_DROPS = b'\xE2\x98\x94' + HOT_BEVERAGE = b'\xE2\x98\x95' + WHITE_UP_POINTING_INDEX = b'\xE2\x98\x9D' + WHITE_SMILING_FACE = b'\xE2\x98\xBA' + ARIES = b'\xE2\x99\x88' + TAURUS = b'\xE2\x99\x89' + GEMINI = b'\xE2\x99\x8A' + CANCER = b'\xE2\x99\x8B' + LEO = b'\xE2\x99\x8C' + VIRGO = b'\xE2\x99\x8D' + LIBRA = b'\xE2\x99\x8E' + SCORPIUS = b'\xE2\x99\x8F' + SAGITTARIUS = b'\xE2\x99\x90' + CAPRICORN = b'\xE2\x99\x91' + AQUARIUS = b'\xE2\x99\x92' + PISCES = b'\xE2\x99\x93' + BLACK_SPADE_SUIT = b'\xE2\x99\xA0' + BLACK_CLUB_SUIT = b'\xE2\x99\xA3' + BLACK_HEART_SUIT = b'\xE2\x99\xA5' + BLACK_DIAMOND_SUIT = b'\xE2\x99\xA6' + HOT_SPRINGS = b'\xE2\x99\xA8' + BLACK_UNIVERSAL_RECYCLING_SYMBOL = b'\xE2\x99\xBB' + WHEELCHAIR_SYMBOL = b'\xE2\x99\xBF' + ANCHOR = b'\xE2\x9A\x93' + WARNING_SIGN = b'\xE2\x9A\xA0' + HIGH_VOLTAGE_SIGN = b'\xE2\x9A\xA1' + MEDIUM_WHITE_CIRCLE = b'\xE2\x9A\xAA' + MEDIUM_BLACK_CIRCLE = b'\xE2\x9A\xAB' + SOCCER_BALL = b'\xE2\x9A\xBD' + BASEBALL = b'\xE2\x9A\xBE' + SNOWMAN_WITHOUT_SNOW = b'\xE2\x9B\x84' + SUN_BEHIND_CLOUD = b'\xE2\x9B\x85' + OPHIUCHUS = b'\xE2\x9B\x8E' + NO_ENTRY = b'\xE2\x9B\x94' + CHURCH = b'\xE2\x9B\xAA' + FOUNTAIN = b'\xE2\x9B\xB2' + FLAG_IN_HOLE = b'\xE2\x9B\xB3' + SAILBOAT = b'\xE2\x9B\xB5' + TENT = b'\xE2\x9B\xBA' + FUEL_PUMP = b'\xE2\x9B\xBD' + ARROW_POINTING_RIGHTWARDS_THEN_CURVING_UPWARDS = b'\xE2\xA4\xB4' + ARROW_POINTING_RIGHTWARDS_THEN_CURVING_DOWNWARDS = b'\xE2\xA4\xB5' + LEFTWARDS_BLACK_ARROW = b'\xE2\xAC\x85' + UPWARDS_BLACK_ARROW = b'\xE2\xAC\x86' + DOWNWARDS_BLACK_ARROW = b'\xE2\xAC\x87' + BLACK_LARGE_SQUARE = b'\xE2\xAC\x9B' + WHITE_LARGE_SQUARE = b'\xE2\xAC\x9C' + WHITE_MEDIUM_STAR = b'\xE2\xAD\x90' + HEAVY_LARGE_CIRCLE = b'\xE2\xAD\x95' + WAVY_DASH = b'\xE3\x80\xB0' + PART_ALTERNATION_MARK = b'\xE3\x80\xBD' + CIRCLED_IDEOGRAPH_CONGRATULATION = b'\xE3\x8A\x97' + CIRCLED_IDEOGRAPH_SECRET = b'\xE3\x8A\x99' + MAHJONG_TILE_RED_DRAGON = b'\xF0\x9F\x80\x84' + PLAYING_CARD_BLACK_JOKER = b'\xF0\x9F\x83\x8F' + CYCLONE = b'\xF0\x9F\x8C\x80' + FOGGY = b'\xF0\x9F\x8C\x81' + CLOSED_UMBRELLA = b'\xF0\x9F\x8C\x82' + NIGHT_WITH_STARS = b'\xF0\x9F\x8C\x83' + SUNRISE_OVER_MOUNTAINS = b'\xF0\x9F\x8C\x84' + SUNRISE = b'\xF0\x9F\x8C\x85' + CITYSCAPE_AT_DUSK = b'\xF0\x9F\x8C\x86' + SUNSET_OVER_BUILDINGS = b'\xF0\x9F\x8C\x87' + RAINBOW = b'\xF0\x9F\x8C\x88' + BRIDGE_AT_NIGHT = b'\xF0\x9F\x8C\x89' + WATER_WAVE = b'\xF0\x9F\x8C\x8A' + VOLCANO = b'\xF0\x9F\x8C\x8B' + MILKY_WAY = b'\xF0\x9F\x8C\x8C' + EARTH_GLOBE_ASIA_AUSTRALIA = b'\xF0\x9F\x8C\x8F' + NEW_MOON_SYMBOL = b'\xF0\x9F\x8C\x91' + FIRST_QUARTER_MOON_SYMBOL = b'\xF0\x9F\x8C\x93' + WAXING_GIBBOUS_MOON_SYMBOL = b'\xF0\x9F\x8C\x94' + FULL_MOON_SYMBOL = b'\xF0\x9F\x8C\x95' + CRESCENT_MOON = b'\xF0\x9F\x8C\x99' + FIRST_QUARTER_MOON_WITH_FACE = b'\xF0\x9F\x8C\x9B' + GLOWING_STAR = b'\xF0\x9F\x8C\x9F' + SHOOTING_STAR = b'\xF0\x9F\x8C\xA0' + CHESTNUT = b'\xF0\x9F\x8C\xB0' + SEEDLING = b'\xF0\x9F\x8C\xB1' + PALM_TREE = b'\xF0\x9F\x8C\xB4' + CACTUS = b'\xF0\x9F\x8C\xB5' + TULIP = b'\xF0\x9F\x8C\xB7' + CHERRY_BLOSSOM = b'\xF0\x9F\x8C\xB8' + ROSE = b'\xF0\x9F\x8C\xB9' + HIBISCUS = b'\xF0\x9F\x8C\xBA' + SUNFLOWER = b'\xF0\x9F\x8C\xBB' + BLOSSOM = b'\xF0\x9F\x8C\xBC' + EAR_OF_MAIZE = b'\xF0\x9F\x8C\xBD' + EAR_OF_RICE = b'\xF0\x9F\x8C\xBE' + HERB = b'\xF0\x9F\x8C\xBF' + FOUR_LEAF_CLOVER = b'\xF0\x9F\x8D\x80' + MAPLE_LEAF = b'\xF0\x9F\x8D\x81' + FALLEN_LEAF = b'\xF0\x9F\x8D\x82' + LEAF_FLUTTERING_IN_WIND = b'\xF0\x9F\x8D\x83' + MUSHROOM = b'\xF0\x9F\x8D\x84' + TOMATO = b'\xF0\x9F\x8D\x85' + AUBERGINE = b'\xF0\x9F\x8D\x86' + GRAPES = b'\xF0\x9F\x8D\x87' + MELON = b'\xF0\x9F\x8D\x88' + WATERMELON = b'\xF0\x9F\x8D\x89' + TANGERINE = b'\xF0\x9F\x8D\x8A' + BANANA = b'\xF0\x9F\x8D\x8C' + PINEAPPLE = b'\xF0\x9F\x8D\x8D' + RED_APPLE = b'\xF0\x9F\x8D\x8E' + GREEN_APPLE = b'\xF0\x9F\x8D\x8F' + PEACH = b'\xF0\x9F\x8D\x91' + CHERRIES = b'\xF0\x9F\x8D\x92' + STRAWBERRY = b'\xF0\x9F\x8D\x93' + HAMBURGER = b'\xF0\x9F\x8D\x94' + SLICE_OF_PIZZA = b'\xF0\x9F\x8D\x95' + MEAT_ON_BONE = b'\xF0\x9F\x8D\x96' + POULTRY_LEG = b'\xF0\x9F\x8D\x97' + RICE_CRACKER = b'\xF0\x9F\x8D\x98' + RICE_BALL = b'\xF0\x9F\x8D\x99' + COOKED_RICE = b'\xF0\x9F\x8D\x9A' + CURRY_AND_RICE = b'\xF0\x9F\x8D\x9B' + STEAMING_BOWL = b'\xF0\x9F\x8D\x9C' + SPAGHETTI = b'\xF0\x9F\x8D\x9D' + BREAD = b'\xF0\x9F\x8D\x9E' + FRENCH_FRIES = b'\xF0\x9F\x8D\x9F' + ROASTED_SWEET_POTATO = b'\xF0\x9F\x8D\xA0' + DANGO = b'\xF0\x9F\x8D\xA1' + ODEN = b'\xF0\x9F\x8D\xA2' + SUSHI = b'\xF0\x9F\x8D\xA3' + FRIED_SHRIMP = b'\xF0\x9F\x8D\xA4' + FISH_CAKE_WITH_SWIRL_DESIGN = b'\xF0\x9F\x8D\xA5' + SOFT_ICE_CREAM = b'\xF0\x9F\x8D\xA6' + SHAVED_ICE = b'\xF0\x9F\x8D\xA7' + ICE_CREAM = b'\xF0\x9F\x8D\xA8' + DOUGHNUT = b'\xF0\x9F\x8D\xA9' + COOKIE = b'\xF0\x9F\x8D\xAA' + CHOCOLATE_BAR = b'\xF0\x9F\x8D\xAB' + CANDY = b'\xF0\x9F\x8D\xAC' + LOLLIPOP = b'\xF0\x9F\x8D\xAD' + CUSTARD = b'\xF0\x9F\x8D\xAE' + HONEY_POT = b'\xF0\x9F\x8D\xAF' + SHORTCAKE = b'\xF0\x9F\x8D\xB0' + BENTO_BOX = b'\xF0\x9F\x8D\xB1' + POT_OF_FOOD = b'\xF0\x9F\x8D\xB2' + COOKING = b'\xF0\x9F\x8D\xB3' + FORK_AND_KNIFE = b'\xF0\x9F\x8D\xB4' + TEACUP_WITHOUT_HANDLE = b'\xF0\x9F\x8D\xB5' + SAKE_BOTTLE_AND_CUP = b'\xF0\x9F\x8D\xB6' + WINE_GLASS = b'\xF0\x9F\x8D\xB7' + COCKTAIL_GLASS = b'\xF0\x9F\x8D\xB8' + TROPICAL_DRINK = b'\xF0\x9F\x8D\xB9' + BEER_MUG = b'\xF0\x9F\x8D\xBA' + CLINKING_BEER_MUGS = b'\xF0\x9F\x8D\xBB' + RIBBON = b'\xF0\x9F\x8E\x80' + WRAPPED_PRESENT = b'\xF0\x9F\x8E\x81' + BIRTHDAY_CAKE = b'\xF0\x9F\x8E\x82' + JACK_O_LANTERN = b'\xF0\x9F\x8E\x83' + CHRISTMAS_TREE = b'\xF0\x9F\x8E\x84' + FATHER_CHRISTMAS = b'\xF0\x9F\x8E\x85' + FIREWORKS = b'\xF0\x9F\x8E\x86' + FIREWORK_SPARKLER = b'\xF0\x9F\x8E\x87' + BALLOON = b'\xF0\x9F\x8E\x88' + PARTY_POPPER = b'\xF0\x9F\x8E\x89' + CONFETTI_BALL = b'\xF0\x9F\x8E\x8A' + TANABATA_TREE = b'\xF0\x9F\x8E\x8B' + CROSSED_FLAGS = b'\xF0\x9F\x8E\x8C' + PINE_DECORATION = b'\xF0\x9F\x8E\x8D' + JAPANESE_DOLLS = b'\xF0\x9F\x8E\x8E' + CARP_STREAMER = b'\xF0\x9F\x8E\x8F' + WIND_CHIME = b'\xF0\x9F\x8E\x90' + MOON_VIEWING_CEREMONY = b'\xF0\x9F\x8E\x91' + SCHOOL_SATCHEL = b'\xF0\x9F\x8E\x92' + GRADUATION_CAP = b'\xF0\x9F\x8E\x93' + CAROUSEL_HORSE = b'\xF0\x9F\x8E\xA0' + FERRIS_WHEEL = b'\xF0\x9F\x8E\xA1' + ROLLER_COASTER = b'\xF0\x9F\x8E\xA2' + FISHING_POLE_AND_FISH = b'\xF0\x9F\x8E\xA3' + MICROPHONE = b'\xF0\x9F\x8E\xA4' + MOVIE_CAMERA = b'\xF0\x9F\x8E\xA5' + CINEMA = b'\xF0\x9F\x8E\xA6' + HEADPHONE = b'\xF0\x9F\x8E\xA7' + ARTIST_PALETTE = b'\xF0\x9F\x8E\xA8' + TOP_HAT = b'\xF0\x9F\x8E\xA9' + CIRCUS_TENT = b'\xF0\x9F\x8E\xAA' + TICKET = b'\xF0\x9F\x8E\xAB' + CLAPPER_BOARD = b'\xF0\x9F\x8E\xAC' + PERFORMING_ARTS = b'\xF0\x9F\x8E\xAD' + VIDEO_GAME = b'\xF0\x9F\x8E\xAE' + DIRECT_HIT = b'\xF0\x9F\x8E\xAF' + SLOT_MACHINE = b'\xF0\x9F\x8E\xB0' + BILLIARDS = b'\xF0\x9F\x8E\xB1' + GAME_DIE = b'\xF0\x9F\x8E\xB2' + BOWLING = b'\xF0\x9F\x8E\xB3' + FLOWER_PLAYING_CARDS = b'\xF0\x9F\x8E\xB4' + MUSICAL_NOTE = b'\xF0\x9F\x8E\xB5' + MULTIPLE_MUSICAL_NOTES = b'\xF0\x9F\x8E\xB6' + SAXOPHONE = b'\xF0\x9F\x8E\xB7' + GUITAR = b'\xF0\x9F\x8E\xB8' + MUSICAL_KEYBOARD = b'\xF0\x9F\x8E\xB9' + TRUMPET = b'\xF0\x9F\x8E\xBA' + VIOLIN = b'\xF0\x9F\x8E\xBB' + MUSICAL_SCORE = b'\xF0\x9F\x8E\xBC' + RUNNING_SHIRT_WITH_SASH = b'\xF0\x9F\x8E\xBD' + TENNIS_RACQUET_AND_BALL = b'\xF0\x9F\x8E\xBE' + SKI_AND_SKI_BOOT = b'\xF0\x9F\x8E\xBF' + BASKETBALL_AND_HOOP = b'\xF0\x9F\x8F\x80' + CHEQUERED_FLAG = b'\xF0\x9F\x8F\x81' + SNOWBOARDER = b'\xF0\x9F\x8F\x82' + RUNNER = b'\xF0\x9F\x8F\x83' + SURFER = b'\xF0\x9F\x8F\x84' + TROPHY = b'\xF0\x9F\x8F\x86' + AMERICAN_FOOTBALL = b'\xF0\x9F\x8F\x88' + SWIMMER = b'\xF0\x9F\x8F\x8A' + HOUSE_BUILDING = b'\xF0\x9F\x8F\xA0' + HOUSE_WITH_GARDEN = b'\xF0\x9F\x8F\xA1' + OFFICE_BUILDING = b'\xF0\x9F\x8F\xA2' + JAPANESE_POST_OFFICE = b'\xF0\x9F\x8F\xA3' + HOSPITAL = b'\xF0\x9F\x8F\xA5' + BANK = b'\xF0\x9F\x8F\xA6' + AUTOMATED_TELLER_MACHINE = b'\xF0\x9F\x8F\xA7' + HOTEL = b'\xF0\x9F\x8F\xA8' + LOVE_HOTEL = b'\xF0\x9F\x8F\xA9' + CONVENIENCE_STORE = b'\xF0\x9F\x8F\xAA' + SCHOOL = b'\xF0\x9F\x8F\xAB' + DEPARTMENT_STORE = b'\xF0\x9F\x8F\xAC' + FACTORY = b'\xF0\x9F\x8F\xAD' + IZAKAYA_LANTERN = b'\xF0\x9F\x8F\xAE' + JAPANESE_CASTLE = b'\xF0\x9F\x8F\xAF' + EUROPEAN_CASTLE = b'\xF0\x9F\x8F\xB0' + SNAIL = b'\xF0\x9F\x90\x8C' + SNAKE = b'\xF0\x9F\x90\x8D' + HORSE = b'\xF0\x9F\x90\x8E' + SHEEP = b'\xF0\x9F\x90\x91' + MONKEY = b'\xF0\x9F\x90\x92' + CHICKEN = b'\xF0\x9F\x90\x94' + BOAR = b'\xF0\x9F\x90\x97' + ELEPHANT = b'\xF0\x9F\x90\x98' + OCTOPUS = b'\xF0\x9F\x90\x99' + SPIRAL_SHELL = b'\xF0\x9F\x90\x9A' + BUG = b'\xF0\x9F\x90\x9B' + ANT = b'\xF0\x9F\x90\x9C' + HONEYBEE = b'\xF0\x9F\x90\x9D' + LADY_BEETLE = b'\xF0\x9F\x90\x9E' + FISH = b'\xF0\x9F\x90\x9F' + TROPICAL_FISH = b'\xF0\x9F\x90\xA0' + BLOWFISH = b'\xF0\x9F\x90\xA1' + TURTLE = b'\xF0\x9F\x90\xA2' + HATCHING_CHICK = b'\xF0\x9F\x90\xA3' + BABY_CHICK = b'\xF0\x9F\x90\xA4' + FRONT_FACING_BABY_CHICK = b'\xF0\x9F\x90\xA5' + BIRD = b'\xF0\x9F\x90\xA6' + PENGUIN = b'\xF0\x9F\x90\xA7' + KOALA = b'\xF0\x9F\x90\xA8' + POODLE = b'\xF0\x9F\x90\xA9' + BACTRIAN_CAMEL = b'\xF0\x9F\x90\xAB' + DOLPHIN = b'\xF0\x9F\x90\xAC' + MOUSE_FACE = b'\xF0\x9F\x90\xAD' + COW_FACE = b'\xF0\x9F\x90\xAE' + TIGER_FACE = b'\xF0\x9F\x90\xAF' + RABBIT_FACE = b'\xF0\x9F\x90\xB0' + CAT_FACE = b'\xF0\x9F\x90\xB1' + DRAGON_FACE = b'\xF0\x9F\x90\xB2' + SPOUTING_WHALE = b'\xF0\x9F\x90\xB3' + HORSE_FACE = b'\xF0\x9F\x90\xB4' + MONKEY_FACE = b'\xF0\x9F\x90\xB5' + DOG_FACE = b'\xF0\x9F\x90\xB6' + PIG_FACE = b'\xF0\x9F\x90\xB7' + FROG_FACE = b'\xF0\x9F\x90\xB8' + HAMSTER_FACE = b'\xF0\x9F\x90\xB9' + WOLF_FACE = b'\xF0\x9F\x90\xBA' + BEAR_FACE = b'\xF0\x9F\x90\xBB' + PANDA_FACE = b'\xF0\x9F\x90\xBC' + PIG_NOSE = b'\xF0\x9F\x90\xBD' + PAW_PRINTS = b'\xF0\x9F\x90\xBE' + EYES = b'\xF0\x9F\x91\x80' + EAR = b'\xF0\x9F\x91\x82' + NOSE = b'\xF0\x9F\x91\x83' + MOUTH = b'\xF0\x9F\x91\x84' + TONGUE = b'\xF0\x9F\x91\x85' + WHITE_UP_POINTING_BACKHAND_INDEX = b'\xF0\x9F\x91\x86' + WHITE_DOWN_POINTING_BACKHAND_INDEX = b'\xF0\x9F\x91\x87' + WHITE_LEFT_POINTING_BACKHAND_INDEX = b'\xF0\x9F\x91\x88' + WHITE_RIGHT_POINTING_BACKHAND_INDEX = b'\xF0\x9F\x91\x89' + FISTED_HAND_SIGN = b'\xF0\x9F\x91\x8A' + WAVING_HAND_SIGN = b'\xF0\x9F\x91\x8B' + OK_HAND_SIGN = b'\xF0\x9F\x91\x8C' + THUMBS_UP_SIGN = b'\xF0\x9F\x91\x8D' + THUMBS_DOWN_SIGN = b'\xF0\x9F\x91\x8E' + CLAPPING_HANDS_SIGN = b'\xF0\x9F\x91\x8F' + OPEN_HANDS_SIGN = b'\xF0\x9F\x91\x90' + CROWN = b'\xF0\x9F\x91\x91' + WOMANS_HAT = b'\xF0\x9F\x91\x92' + EYEGLASSES = b'\xF0\x9F\x91\x93' + NECKTIE = b'\xF0\x9F\x91\x94' + T_SHIRT = b'\xF0\x9F\x91\x95' + JEANS = b'\xF0\x9F\x91\x96' + DRESS = b'\xF0\x9F\x91\x97' + KIMONO = b'\xF0\x9F\x91\x98' + BIKINI = b'\xF0\x9F\x91\x99' + WOMANS_CLOTHES = b'\xF0\x9F\x91\x9A' + PURSE = b'\xF0\x9F\x91\x9B' + HANDBAG = b'\xF0\x9F\x91\x9C' + POUCH = b'\xF0\x9F\x91\x9D' + MANS_SHOE = b'\xF0\x9F\x91\x9E' + ATHLETIC_SHOE = b'\xF0\x9F\x91\x9F' + HIGH_HEELED_SHOE = b'\xF0\x9F\x91\xA0' + WOMANS_SANDAL = b'\xF0\x9F\x91\xA1' + WOMANS_BOOTS = b'\xF0\x9F\x91\xA2' + FOOTPRINTS = b'\xF0\x9F\x91\xA3' + BUST_IN_SILHOUETTE = b'\xF0\x9F\x91\xA4' + BOY = b'\xF0\x9F\x91\xA6' + GIRL = b'\xF0\x9F\x91\xA7' + MAN = b'\xF0\x9F\x91\xA8' + WOMAN = b'\xF0\x9F\x91\xA9' + FAMILY = b'\xF0\x9F\x91\xAA' + MAN_AND_WOMAN_HOLDING_HANDS = b'\xF0\x9F\x91\xAB' + POLICE_OFFICER = b'\xF0\x9F\x91\xAE' + WOMAN_WITH_BUNNY_EARS = b'\xF0\x9F\x91\xAF' + BRIDE_WITH_VEIL = b'\xF0\x9F\x91\xB0' + PERSON_WITH_BLOND_HAIR = b'\xF0\x9F\x91\xB1' + MAN_WITH_GUA_PI_MAO = b'\xF0\x9F\x91\xB2' + MAN_WITH_TURBAN = b'\xF0\x9F\x91\xB3' + OLDER_MAN = b'\xF0\x9F\x91\xB4' + OLDER_WOMAN = b'\xF0\x9F\x91\xB5' + BABY = b'\xF0\x9F\x91\xB6' + CONSTRUCTION_WORKER = b'\xF0\x9F\x91\xB7' + PRINCESS = b'\xF0\x9F\x91\xB8' + JAPANESE_OGRE = b'\xF0\x9F\x91\xB9' + JAPANESE_GOBLIN = b'\xF0\x9F\x91\xBA' + GHOST = b'\xF0\x9F\x91\xBB' + BABY_ANGEL = b'\xF0\x9F\x91\xBC' + EXTRATERRESTRIAL_ALIEN = b'\xF0\x9F\x91\xBD' + ALIEN_MONSTER = b'\xF0\x9F\x91\xBE' + IMP = b'\xF0\x9F\x91\xBF' + SKULL = b'\xF0\x9F\x92\x80' + INFORMATION_DESK_PERSON = b'\xF0\x9F\x92\x81' + GUARDSMAN = b'\xF0\x9F\x92\x82' + DANCER = b'\xF0\x9F\x92\x83' + LIPSTICK = b'\xF0\x9F\x92\x84' + NAIL_POLISH = b'\xF0\x9F\x92\x85' + FACE_MASSAGE = b'\xF0\x9F\x92\x86' + HAIRCUT = b'\xF0\x9F\x92\x87' + BARBER_POLE = b'\xF0\x9F\x92\x88' + SYRINGE = b'\xF0\x9F\x92\x89' + PILL = b'\xF0\x9F\x92\x8A' + KISS_MARK = b'\xF0\x9F\x92\x8B' + LOVE_LETTER = b'\xF0\x9F\x92\x8C' + RING = b'\xF0\x9F\x92\x8D' + GEM_STONE = b'\xF0\x9F\x92\x8E' + KISS = b'\xF0\x9F\x92\x8F' + BOUQUET = b'\xF0\x9F\x92\x90' + COUPLE_WITH_HEART = b'\xF0\x9F\x92\x91' + WEDDING = b'\xF0\x9F\x92\x92' + BEATING_HEART = b'\xF0\x9F\x92\x93' + BROKEN_HEART = b'\xF0\x9F\x92\x94' + TWO_HEARTS = b'\xF0\x9F\x92\x95' + SPARKLING_HEART = b'\xF0\x9F\x92\x96' + GROWING_HEART = b'\xF0\x9F\x92\x97' + HEART_WITH_ARROW = b'\xF0\x9F\x92\x98' + BLUE_HEART = b'\xF0\x9F\x92\x99' + GREEN_HEART = b'\xF0\x9F\x92\x9A' + YELLOW_HEART = b'\xF0\x9F\x92\x9B' + PURPLE_HEART = b'\xF0\x9F\x92\x9C' + HEART_WITH_RIBBON = b'\xF0\x9F\x92\x9D' + REVOLVING_HEARTS = b'\xF0\x9F\x92\x9E' + HEART_DECORATION = b'\xF0\x9F\x92\x9F' + DIAMOND_SHAPE_WITH_A_DOT_INSIDE = b'\xF0\x9F\x92\xA0' + ELECTRIC_LIGHT_BULB = b'\xF0\x9F\x92\xA1' + ANGER_SYMBOL = b'\xF0\x9F\x92\xA2' + BOMB = b'\xF0\x9F\x92\xA3' + SLEEPING_SYMBOL = b'\xF0\x9F\x92\xA4' + COLLISION_SYMBOL = b'\xF0\x9F\x92\xA5' + SPLASHING_SWEAT_SYMBOL = b'\xF0\x9F\x92\xA6' + DROPLET = b'\xF0\x9F\x92\xA7' + DASH_SYMBOL = b'\xF0\x9F\x92\xA8' + PILE_OF_POO = b'\xF0\x9F\x92\xA9' + FLEXED_BICEPS = b'\xF0\x9F\x92\xAA' + DIZZY_SYMBOL = b'\xF0\x9F\x92\xAB' + SPEECH_BALLOON = b'\xF0\x9F\x92\xAC' + WHITE_FLOWER = b'\xF0\x9F\x92\xAE' + HUNDRED_POINTS_SYMBOL = b'\xF0\x9F\x92\xAF' + MONEY_BAG = b'\xF0\x9F\x92\xB0' + CURRENCY_EXCHANGE = b'\xF0\x9F\x92\xB1' + HEAVY_DOLLAR_SIGN = b'\xF0\x9F\x92\xB2' + CREDIT_CARD = b'\xF0\x9F\x92\xB3' + BANKNOTE_WITH_YEN_SIGN = b'\xF0\x9F\x92\xB4' + BANKNOTE_WITH_DOLLAR_SIGN = b'\xF0\x9F\x92\xB5' + MONEY_WITH_WINGS = b'\xF0\x9F\x92\xB8' + CHART_WITH_UPWARDS_TREND_AND_YEN_SIGN = b'\xF0\x9F\x92\xB9' + SEAT = b'\xF0\x9F\x92\xBA' + PERSONAL_COMPUTER = b'\xF0\x9F\x92\xBB' + BRIEFCASE = b'\xF0\x9F\x92\xBC' + MINIDISC = b'\xF0\x9F\x92\xBD' + FLOPPY_DISK = b'\xF0\x9F\x92\xBE' + OPTICAL_DISC = b'\xF0\x9F\x92\xBF' + DVD = b'\xF0\x9F\x93\x80' + FILE_FOLDER = b'\xF0\x9F\x93\x81' + OPEN_FILE_FOLDER = b'\xF0\x9F\x93\x82' + PAGE_WITH_CURL = b'\xF0\x9F\x93\x83' + PAGE_FACING_UP = b'\xF0\x9F\x93\x84' + CALENDAR = b'\xF0\x9F\x93\x85' + TEAR_OFF_CALENDAR = b'\xF0\x9F\x93\x86' + CARD_INDEX = b'\xF0\x9F\x93\x87' + CHART_WITH_UPWARDS_TREND = b'\xF0\x9F\x93\x88' + CHART_WITH_DOWNWARDS_TREND = b'\xF0\x9F\x93\x89' + BAR_CHART = b'\xF0\x9F\x93\x8A' + CLIPBOARD = b'\xF0\x9F\x93\x8B' + PUSHPIN = b'\xF0\x9F\x93\x8C' + ROUND_PUSHPIN = b'\xF0\x9F\x93\x8D' + PAPERCLIP = b'\xF0\x9F\x93\x8E' + STRAIGHT_RULER = b'\xF0\x9F\x93\x8F' + TRIANGULAR_RULER = b'\xF0\x9F\x93\x90' + BOOKMARK_TABS = b'\xF0\x9F\x93\x91' + LEDGER = b'\xF0\x9F\x93\x92' + NOTEBOOK = b'\xF0\x9F\x93\x93' + NOTEBOOK_WITH_DECORATIVE_COVER = b'\xF0\x9F\x93\x94' + CLOSED_BOOK = b'\xF0\x9F\x93\x95' + OPEN_BOOK = b'\xF0\x9F\x93\x96' + GREEN_BOOK = b'\xF0\x9F\x93\x97' + BLUE_BOOK = b'\xF0\x9F\x93\x98' + ORANGE_BOOK = b'\xF0\x9F\x93\x99' + BOOKS = b'\xF0\x9F\x93\x9A' + NAME_BADGE = b'\xF0\x9F\x93\x9B' + SCROLL = b'\xF0\x9F\x93\x9C' + MEMO = b'\xF0\x9F\x93\x9D' + TELEPHONE_RECEIVER = b'\xF0\x9F\x93\x9E' + PAGER = b'\xF0\x9F\x93\x9F' + FAX_MACHINE = b'\xF0\x9F\x93\xA0' + SATELLITE_ANTENNA = b'\xF0\x9F\x93\xA1' + PUBLIC_ADDRESS_LOUDSPEAKER = b'\xF0\x9F\x93\xA2' + CHEERING_MEGAPHONE = b'\xF0\x9F\x93\xA3' + OUTBOX_TRAY = b'\xF0\x9F\x93\xA4' + INBOX_TRAY = b'\xF0\x9F\x93\xA5' + PACKAGE = b'\xF0\x9F\x93\xA6' + E_MAIL_SYMBOL = b'\xF0\x9F\x93\xA7' + INCOMING_ENVELOPE = b'\xF0\x9F\x93\xA8' + ENVELOPE_WITH_DOWNWARDS_ARROW_ABOVE = b'\xF0\x9F\x93\xA9' + CLOSED_MAILBOX_WITH_LOWERED_FLAG = b'\xF0\x9F\x93\xAA' + CLOSED_MAILBOX_WITH_RAISED_FLAG = b'\xF0\x9F\x93\xAB' + POSTBOX = b'\xF0\x9F\x93\xAE' + NEWSPAPER = b'\xF0\x9F\x93\xB0' + MOBILE_PHONE = b'\xF0\x9F\x93\xB1' + MOBILE_PHONE_WITH_RIGHTWARDS_ARROW_AT_LEFT = b'\xF0\x9F\x93\xB2' + VIBRATION_MODE = b'\xF0\x9F\x93\xB3' + MOBILE_PHONE_OFF = b'\xF0\x9F\x93\xB4' + ANTENNA_WITH_BARS = b'\xF0\x9F\x93\xB6' + CAMERA = b'\xF0\x9F\x93\xB7' + VIDEO_CAMERA = b'\xF0\x9F\x93\xB9' + TELEVISION = b'\xF0\x9F\x93\xBA' + RADIO = b'\xF0\x9F\x93\xBB' + VIDEOCASSETTE = b'\xF0\x9F\x93\xBC' + CLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS = b'\xF0\x9F\x94\x83' + SPEAKER_WITH_THREE_SOUND_WAVES = b'\xF0\x9F\x94\x8A' + BATTERY = b'\xF0\x9F\x94\x8B' + ELECTRIC_PLUG = b'\xF0\x9F\x94\x8C' + LEFT_POINTING_MAGNIFYING_GLASS = b'\xF0\x9F\x94\x8D' + RIGHT_POINTING_MAGNIFYING_GLASS = b'\xF0\x9F\x94\x8E' + LOCK_WITH_INK_PEN = b'\xF0\x9F\x94\x8F' + CLOSED_LOCK_WITH_KEY = b'\xF0\x9F\x94\x90' + KEY = b'\xF0\x9F\x94\x91' + LOCK = b'\xF0\x9F\x94\x92' + OPEN_LOCK = b'\xF0\x9F\x94\x93' + BELL = b'\xF0\x9F\x94\x94' + BOOKMARK = b'\xF0\x9F\x94\x96' + LINK_SYMBOL = b'\xF0\x9F\x94\x97' + RADIO_BUTTON = b'\xF0\x9F\x94\x98' + BACK_WITH_LEFTWARDS_ARROW_ABOVE = b'\xF0\x9F\x94\x99' + END_WITH_LEFTWARDS_ARROW_ABOVE = b'\xF0\x9F\x94\x9A' + ON_WITH_EXCLAMATION_MARK_WITH_LEFT_RIGHT_ARROW_ABOVE = b'\xF0\x9F\x94\x9B' + SOON_WITH_RIGHTWARDS_ARROW_ABOVE = b'\xF0\x9F\x94\x9C' + TOP_WITH_UPWARDS_ARROW_ABOVE = b'\xF0\x9F\x94\x9D' + NO_ONE_UNDER_EIGHTEEN_SYMBOL = b'\xF0\x9F\x94\x9E' + KEYCAP_TEN = b'\xF0\x9F\x94\x9F' + INPUT_SYMBOL_FOR_LATIN_CAPITAL_LETTERS = b'\xF0\x9F\x94\xA0' + INPUT_SYMBOL_FOR_LATIN_SMALL_LETTERS = b'\xF0\x9F\x94\xA1' + INPUT_SYMBOL_FOR_NUMBERS = b'\xF0\x9F\x94\xA2' + INPUT_SYMBOL_FOR_SYMBOLS = b'\xF0\x9F\x94\xA3' + INPUT_SYMBOL_FOR_LATIN_LETTERS = b'\xF0\x9F\x94\xA4' + FIRE = b'\xF0\x9F\x94\xA5' + ELECTRIC_TORCH = b'\xF0\x9F\x94\xA6' + WRENCH = b'\xF0\x9F\x94\xA7' + HAMMER = b'\xF0\x9F\x94\xA8' + NUT_AND_BOLT = b'\xF0\x9F\x94\xA9' + HOCHO = b'\xF0\x9F\x94\xAA' + PISTOL = b'\xF0\x9F\x94\xAB' + CRYSTAL_BALL = b'\xF0\x9F\x94\xAE' + SIX_POINTED_STAR_WITH_MIDDLE_DOT = b'\xF0\x9F\x94\xAF' + JAPANESE_SYMBOL_FOR_BEGINNER = b'\xF0\x9F\x94\xB0' + TRIDENT_EMBLEM = b'\xF0\x9F\x94\xB1' + BLACK_SQUARE_BUTTON = b'\xF0\x9F\x94\xB2' + WHITE_SQUARE_BUTTON = b'\xF0\x9F\x94\xB3' + LARGE_RED_CIRCLE = b'\xF0\x9F\x94\xB4' + LARGE_BLUE_CIRCLE = b'\xF0\x9F\x94\xB5' + LARGE_ORANGE_DIAMOND = b'\xF0\x9F\x94\xB6' + LARGE_BLUE_DIAMOND = b'\xF0\x9F\x94\xB7' + SMALL_ORANGE_DIAMOND = b'\xF0\x9F\x94\xB8' + SMALL_BLUE_DIAMOND = b'\xF0\x9F\x94\xB9' + UP_POINTING_RED_TRIANGLE = b'\xF0\x9F\x94\xBA' + DOWN_POINTING_RED_TRIANGLE = b'\xF0\x9F\x94\xBB' + UP_POINTING_SMALL_RED_TRIANGLE = b'\xF0\x9F\x94\xBC' + DOWN_POINTING_SMALL_RED_TRIANGLE = b'\xF0\x9F\x94\xBD' + CLOCK_FACE_ONE_OCLOCK = b'\xF0\x9F\x95\x90' + CLOCK_FACE_TWO_OCLOCK = b'\xF0\x9F\x95\x91' + CLOCK_FACE_THREE_OCLOCK = b'\xF0\x9F\x95\x92' + CLOCK_FACE_FOUR_OCLOCK = b'\xF0\x9F\x95\x93' + CLOCK_FACE_FIVE_OCLOCK = b'\xF0\x9F\x95\x94' + CLOCK_FACE_SIX_OCLOCK = b'\xF0\x9F\x95\x95' + CLOCK_FACE_SEVEN_OCLOCK = b'\xF0\x9F\x95\x96' + CLOCK_FACE_EIGHT_OCLOCK = b'\xF0\x9F\x95\x97' + CLOCK_FACE_NINE_OCLOCK = b'\xF0\x9F\x95\x98' + CLOCK_FACE_TEN_OCLOCK = b'\xF0\x9F\x95\x99' + CLOCK_FACE_ELEVEN_OCLOCK = b'\xF0\x9F\x95\x9A' + CLOCK_FACE_TWELVE_OCLOCK = b'\xF0\x9F\x95\x9B' + MOUNT_FUJI = b'\xF0\x9F\x97\xBB' + TOKYO_TOWER = b'\xF0\x9F\x97\xBC' + STATUE_OF_LIBERTY = b'\xF0\x9F\x97\xBD' + SILHOUETTE_OF_JAPAN = b'\xF0\x9F\x97\xBE' + MOYAI = b'\xF0\x9F\x97\xBF' + GRINNING_FACE = b'\xF0\x9F\x98\x80' + SMILING_FACE_WITH_HALO = b'\xF0\x9F\x98\x87' + SMILING_FACE_WITH_HORNS = b'\xF0\x9F\x98\x88' + SMILING_FACE_WITH_SUNGLASSES = b'\xF0\x9F\x98\x8E' + NEUTRAL_FACE = b'\xF0\x9F\x98\x90' + EXPRESSIONLESS_FACE = b'\xF0\x9F\x98\x91' + CONFUSED_FACE = b'\xF0\x9F\x98\x95' + KISSING_FACE = b'\xF0\x9F\x98\x97' + KISSING_FACE_WITH_SMILING_EYES = b'\xF0\x9F\x98\x99' + FACE_WITH_STUCK_OUT_TONGUE = b'\xF0\x9F\x98\x9B' + WORRIED_FACE = b'\xF0\x9F\x98\x9F' + FROWNING_FACE_WITH_OPEN_MOUTH = b'\xF0\x9F\x98\xA6' + ANGUISHED_FACE = b'\xF0\x9F\x98\xA7' + GRIMACING_FACE = b'\xF0\x9F\x98\xAC' + FACE_WITH_OPEN_MOUTH = b'\xF0\x9F\x98\xAE' + HUSHED_FACE = b'\xF0\x9F\x98\xAF' + SLEEPING_FACE = b'\xF0\x9F\x98\xB4' + FACE_WITHOUT_MOUTH = b'\xF0\x9F\x98\xB6' + HELICOPTER = b'\xF0\x9F\x9A\x81' + STEAM_LOCOMOTIVE = b'\xF0\x9F\x9A\x82' + TRAIN = b'\xF0\x9F\x9A\x86' + LIGHT_RAIL = b'\xF0\x9F\x9A\x88' + TRAM = b'\xF0\x9F\x9A\x8A' + ONCOMING_BUS = b'\xF0\x9F\x9A\x8D' + TROLLEYBUS = b'\xF0\x9F\x9A\x8E' + MINIBUS = b'\xF0\x9F\x9A\x90' + ONCOMING_POLICE_CAR = b'\xF0\x9F\x9A\x94' + ONCOMING_TAXI = b'\xF0\x9F\x9A\x96' + ONCOMING_AUTOMOBILE = b'\xF0\x9F\x9A\x98' + ARTICULATED_LORRY = b'\xF0\x9F\x9A\x9B' + TRACTOR = b'\xF0\x9F\x9A\x9C' + MONORAIL = b'\xF0\x9F\x9A\x9D' + MOUNTAIN_RAILWAY = b'\xF0\x9F\x9A\x9E' + SUSPENSION_RAILWAY = b'\xF0\x9F\x9A\x9F' + MOUNTAIN_CABLEWAY = b'\xF0\x9F\x9A\xA0' + AERIAL_TRAMWAY = b'\xF0\x9F\x9A\xA1' + ROWBOAT = b'\xF0\x9F\x9A\xA3' + VERTICAL_TRAFFIC_LIGHT = b'\xF0\x9F\x9A\xA6' + PUT_LITTER_IN_ITS_PLACE_SYMBOL = b'\xF0\x9F\x9A\xAE' + DO_NOT_LITTER_SYMBOL = b'\xF0\x9F\x9A\xAF' + POTABLE_WATER_SYMBOL = b'\xF0\x9F\x9A\xB0' + NON_POTABLE_WATER_SYMBOL = b'\xF0\x9F\x9A\xB1' + NO_BICYCLES = b'\xF0\x9F\x9A\xB3' + BICYCLIST = b'\xF0\x9F\x9A\xB4' + MOUNTAIN_BICYCLIST = b'\xF0\x9F\x9A\xB5' + NO_PEDESTRIANS = b'\xF0\x9F\x9A\xB7' + CHILDREN_CROSSING = b'\xF0\x9F\x9A\xB8' + SHOWER = b'\xF0\x9F\x9A\xBF' + BATHTUB = b'\xF0\x9F\x9B\x81' + PASSPORT_CONTROL = b'\xF0\x9F\x9B\x82' + CUSTOMS = b'\xF0\x9F\x9B\x83' + BAGGAGE_CLAIM = b'\xF0\x9F\x9B\x84' + LEFT_LUGGAGE = b'\xF0\x9F\x9B\x85' + EARTH_GLOBE_EUROPE_AFRICA = b'\xF0\x9F\x8C\x8D' + EARTH_GLOBE_AMERICAS = b'\xF0\x9F\x8C\x8E' + GLOBE_WITH_MERIDIANS = b'\xF0\x9F\x8C\x90' + WAXING_CRESCENT_MOON_SYMBOL = b'\xF0\x9F\x8C\x92' + WANING_GIBBOUS_MOON_SYMBOL = b'\xF0\x9F\x8C\x96' + LAST_QUARTER_MOON_SYMBOL = b'\xF0\x9F\x8C\x97' + WANING_CRESCENT_MOON_SYMBOL = b'\xF0\x9F\x8C\x98' + NEW_MOON_WITH_FACE = b'\xF0\x9F\x8C\x9A' + LAST_QUARTER_MOON_WITH_FACE = b'\xF0\x9F\x8C\x9C' + FULL_MOON_WITH_FACE = b'\xF0\x9F\x8C\x9D' + SUN_WITH_FACE = b'\xF0\x9F\x8C\x9E' + EVERGREEN_TREE = b'\xF0\x9F\x8C\xB2' + DECIDUOUS_TREE = b'\xF0\x9F\x8C\xB3' + LEMON = b'\xF0\x9F\x8D\x8B' + PEAR = b'\xF0\x9F\x8D\x90' + BABY_BOTTLE = b'\xF0\x9F\x8D\xBC' + HORSE_RACING = b'\xF0\x9F\x8F\x87' + RUGBY_FOOTBALL = b'\xF0\x9F\x8F\x89' + EUROPEAN_POST_OFFICE = b'\xF0\x9F\x8F\xA4' + RAT = b'\xF0\x9F\x90\x80' + MOUSE = b'\xF0\x9F\x90\x81' + OX = b'\xF0\x9F\x90\x82' + WATER_BUFFALO = b'\xF0\x9F\x90\x83' + COW = b'\xF0\x9F\x90\x84' + TIGER = b'\xF0\x9F\x90\x85' + LEOPARD = b'\xF0\x9F\x90\x86' + RABBIT = b'\xF0\x9F\x90\x87' + CAT = b'\xF0\x9F\x90\x88' + DRAGON = b'\xF0\x9F\x90\x89' + CROCODILE = b'\xF0\x9F\x90\x8A' + WHALE = b'\xF0\x9F\x90\x8B' + RAM = b'\xF0\x9F\x90\x8F' + GOAT = b'\xF0\x9F\x90\x90' + ROOSTER = b'\xF0\x9F\x90\x93' + DOG = b'\xF0\x9F\x90\x95' + PIG = b'\xF0\x9F\x90\x96' + DROMEDARY_CAMEL = b'\xF0\x9F\x90\xAA' + BUSTS_IN_SILHOUETTE = b'\xF0\x9F\x91\xA5' + TWO_MEN_HOLDING_HANDS = b'\xF0\x9F\x91\xAC' + TWO_WOMEN_HOLDING_HANDS = b'\xF0\x9F\x91\xAD' + THOUGHT_BALLOON = b'\xF0\x9F\x92\xAD' + BANKNOTE_WITH_EURO_SIGN = b'\xF0\x9F\x92\xB6' + BANKNOTE_WITH_POUND_SIGN = b'\xF0\x9F\x92\xB7' + OPEN_MAILBOX_WITH_RAISED_FLAG = b'\xF0\x9F\x93\xAC' + OPEN_MAILBOX_WITH_LOWERED_FLAG = b'\xF0\x9F\x93\xAD' + POSTAL_HORN = b'\xF0\x9F\x93\xAF' + NO_MOBILE_PHONES = b'\xF0\x9F\x93\xB5' + TWISTED_RIGHTWARDS_ARROWS = b'\xF0\x9F\x94\x80' + CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS = b'\xF0\x9F\x94\x81' + CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS_WITH_CIRCLED_ONE_OVERLAY = b'\xF0\x9F\x94\x82' + ANTICLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS = b'\xF0\x9F\x94\x84' + LOW_BRIGHTNESS_SYMBOL = b'\xF0\x9F\x94\x85' + HIGH_BRIGHTNESS_SYMBOL = b'\xF0\x9F\x94\x86' + SPEAKER_WITH_CANCELLATION_STROKE = b'\xF0\x9F\x94\x87' + SPEAKER_WITH_ONE_SOUND_WAVE = b'\xF0\x9F\x94\x89' + BELL_WITH_CANCELLATION_STROKE = b'\xF0\x9F\x94\x95' + MICROSCOPE = b'\xF0\x9F\x94\xAC' + TELESCOPE = b'\xF0\x9F\x94\xAD' + CLOCK_FACE_ONE_THIRTY = b'\xF0\x9F\x95\x9C' + CLOCK_FACE_TWO_THIRTY = b'\xF0\x9F\x95\x9D' + CLOCK_FACE_THREE_THIRTY = b'\xF0\x9F\x95\x9E' + CLOCK_FACE_FOUR_THIRTY = b'\xF0\x9F\x95\x9F' + CLOCK_FACE_FIVE_THIRTY = b'\xF0\x9F\x95\xA0' + CLOCK_FACE_SIX_THIRTY = b'\xF0\x9F\x95\xA1' + CLOCK_FACE_SEVEN_THIRTY = b'\xF0\x9F\x95\xA2' + CLOCK_FACE_EIGHT_THIRTY = b'\xF0\x9F\x95\xA3' + CLOCK_FACE_NINE_THIRTY = b'\xF0\x9F\x95\xA4' + CLOCK_FACE_TEN_THIRTY = b'\xF0\x9F\x95\xA5' + CLOCK_FACE_ELEVEN_THIRTY = b'\xF0\x9F\x95\xA6' + CLOCK_FACE_TWELVE_THIRTY = b'\xF0\x9F\x95\xA7' diff --git a/lib/telegram/error.py b/lib/telegram/error.py new file mode 100644 index 00000000..f8b7e21f --- /dev/null +++ b/lib/telegram/error.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Error""" + +import re + + +class TelegramError(Exception): + """This object represents a Telegram Error.""" + + def __init__(self, message): + """ + Returns: + str: + """ + super(TelegramError, self).__init__() + + api_error = re.match(r'^Error: (?P.*)', message) + if api_error: + self.message = api_error.group('message').capitalize() + else: + self.message = message + + def __str__(self): + return '%s' % (self.message) diff --git a/lib/telegram/file.py b/lib/telegram/file.py new file mode 100644 index 00000000..9474d2ff --- /dev/null +++ b/lib/telegram/file.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram File""" + +from os.path import basename + +from telegram import TelegramObject +from telegram.utils.request import download as _download + + +class File(TelegramObject): + + """This object represents a Telegram File. + + Attributes: + file_id (str): + file_size (str): + file_path (str): + + Args: + file_id (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + file_size (Optional[int]): + file_path (Optional[str]): + """ + + def __init__(self, + file_id, + **kwargs): + # Required + self.file_id = str(file_id) + # Optionals + self.file_size = int(kwargs.get('file_size', 0)) + self.file_path = str(kwargs.get('file_path', '')) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.File: + """ + if not data: + return None + + return File(**data) + + def download(self, + custom_path=None): + """ + Args: + custom_path (str): + """ + url = self.file_path + + if custom_path: + filename = basename(custom_path) + else: + filename = basename(url) + + _download(url, filename) diff --git a/lib/telegram/forcereply.py b/lib/telegram/forcereply.py new file mode 100644 index 00000000..28057bee --- /dev/null +++ b/lib/telegram/forcereply.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram ForceReply""" + +from telegram import ReplyMarkup + + +class ForceReply(ReplyMarkup): + """This object represents a Telegram ForceReply. + + Attributes: + force_reply (bool): + selective (bool): + + Args: + force_reply (bool): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + selective (Optional[bool]): + """ + + def __init__(self, + force_reply=True, + **kwargs): + # Required + self.force_reply = bool(force_reply) + # Optionals + self.selective = bool(kwargs.get('selective', False)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.ForceReply: + """ + if not data: + return None + + return ForceReply(**data) diff --git a/lib/telegram/groupchat.py b/lib/telegram/groupchat.py new file mode 100644 index 00000000..f4d59723 --- /dev/null +++ b/lib/telegram/groupchat.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# pylint: disable=C0103,W0622 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram GroupChat""" + +from telegram import TelegramObject + + +class GroupChat(TelegramObject): + """This object represents a Telegram GroupChat. + + Attributes: + id (int): + title (str): + type (str): + + Args: + id (int): + title (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + type (Optional[str]): + """ + + def __init__(self, + id, + title, + **kwargs): + # Required + self.id = int(id) + self.title = title + # Optionals + self.type = kwargs.get('type', '') + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.GroupChat: + """ + if not data: + return None + + return GroupChat(**data) diff --git a/lib/telegram/inputfile.py b/lib/telegram/inputfile.py new file mode 100644 index 00000000..0946f972 --- /dev/null +++ b/lib/telegram/inputfile.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# pylint: disable=W0622,E0611 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram InputFile""" + +try: + from email.generator import _make_boundary as choose_boundary + from urllib.request import urlopen + from io import BufferedReader as file +except ImportError: + from mimetools import choose_boundary + from urllib2 import urlopen +import mimetypes +import os +import sys +import imghdr + +from telegram import TelegramError + +DEFAULT_MIME_TYPE = 'application/octet-stream' +USER_AGENT = 'Python Telegram Bot' \ + ' (https://github.com/leandrotoledo/python-telegram-bot)' + + +class InputFile(object): + """This object represents a Telegram InputFile.""" + + def __init__(self, + data): + self.data = data + self.boundary = choose_boundary() + + if 'audio' in data: + self.input_name = 'audio' + self.input_file = data.pop('audio') + if 'document' in data: + self.input_name = 'document' + self.input_file = data.pop('document') + if 'photo' in data: + self.input_name = 'photo' + self.input_file = data.pop('photo') + if 'sticker' in data: + self.input_name = 'sticker' + self.input_file = data.pop('sticker') + if 'video' in data: + self.input_name = 'video' + self.input_file = data.pop('video') + if 'voice' in data: + self.input_name = 'voice' + self.input_file = data.pop('voice') + if 'certificate' in data: + self.input_name = 'certificate' + self.input_file = data.pop('certificate') + + if isinstance(self.input_file, file): + self.input_file_content = self.input_file.read() + if 'filename' in data: + self.filename = self.data.pop('filename') + else: + self.filename = os.path.basename(self.input_file.name) + self.mimetype = mimetypes.guess_type(self.filename)[0] or \ + DEFAULT_MIME_TYPE + + if 'http' in self.input_file: + self.input_file_content = urlopen(self.input_file).read() + self.mimetype = InputFile.is_image(self.input_file_content) + self.filename = self.mimetype.replace('/', '.') + + @property + def headers(self): + """ + Returns: + str: + """ + return {'User-agent': USER_AGENT, + 'Content-type': self.content_type} + + @property + def content_type(self): + """ + Returns: + str: + """ + return 'multipart/form-data; boundary=%s' % self.boundary + + def to_form(self): + """ + Returns: + str: + """ + form = [] + form_boundary = '--' + self.boundary + + # Add data fields + for name, value in self.data.items(): + form.extend([ + form_boundary, + 'Content-Disposition: form-data; name="%s"' % name, + '', + str(value) + ]) + + # Add input_file to upload + form.extend([ + form_boundary, + 'Content-Disposition: form-data; name="%s"; filename="%s"' % ( + self.input_name, self.filename + ), + 'Content-Type: %s' % self.mimetype, + '', + self.input_file_content + ]) + + form.append('--' + self.boundary + '--') + form.append('') + + return InputFile._parse(form) + + @staticmethod + def _parse(form): + """ + Returns: + str: + """ + if sys.version_info > (3,): + # on Python 3 form needs to be byte encoded + encoded_form = [] + for item in form: + try: + encoded_form.append(item.encode()) + except AttributeError: + encoded_form.append(item) + + return b'\r\n'.join(encoded_form) + return '\r\n'.join(form) + + @staticmethod + def is_image(stream): + """Check if the content file is an image by analyzing its headers. + + Args: + stream (str): A str representing the content of a file. + + Returns: + str: The str mimetype of an image. + """ + image = imghdr.what(None, stream) + if image: + return 'image/%s' % image + + raise TelegramError('Could not parse file content') + + @staticmethod + def is_inputfile(data): + """Check if the request is a file request. + + Args: + data (str): A dict of (str, unicode) key/value pairs + + Returns: + bool + """ + if data: + file_types = ['audio', 'document', 'photo', 'sticker', 'video', + 'voice', 'certificate'] + file_type = [i for i in list(data.keys()) if i in file_types] + + if file_type: + file_content = data[file_type[0]] + + if file_type[0] == 'photo' or file_type[0] == 'document': + return isinstance(file_content, file) or \ + str(file_content).startswith('http') + + return isinstance(file_content, file) + + return False diff --git a/lib/telegram/location.py b/lib/telegram/location.py new file mode 100644 index 00000000..66c28fc5 --- /dev/null +++ b/lib/telegram/location.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Location""" + +from telegram import TelegramObject + + +class Location(TelegramObject): + """This object represents a Telegram Sticker. + + Attributes: + longitude (float): + latitude (float): + + Args: + longitude (float): + latitude (float): + """ + + def __init__(self, + longitude, + latitude): + # Required + self.longitude = float(longitude) + self.latitude = float(latitude) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Location: + """ + if not data: + return None + + return Location(**data) diff --git a/lib/telegram/message.py b/lib/telegram/message.py new file mode 100644 index 00000000..e77f3ea4 --- /dev/null +++ b/lib/telegram/message.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python +# pylint: disable=R0902,R0912,R0913 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Message""" + +from datetime import datetime +from time import mktime + +from telegram import (Audio, Contact, Document, GroupChat, Location, PhotoSize, + Sticker, TelegramObject, User, Video, Voice) + + +class Message(TelegramObject): + """This object represents a Telegram Message. + + Note: + * In Python `from` is a reserved word, use `from_user` instead. + + Attributes: + message_id (int): + from_user (:class:`telegram.User`): + date (:class:`datetime.datetime`): + forward_from (:class:`telegram.User`): + forward_date (:class:`datetime.datetime`): + reply_to_message (:class:`telegram.Message`): + text (str): + audio (:class:`telegram.Audio`): + document (:class:`telegram.Document`): + photo (List[:class:`telegram.PhotoSize`]): + sticker (:class:`telegram.Sticker`): + video (:class:`telegram.Video`): + voice (:class:`telegram.Voice`): + caption (str): + contact (:class:`telegram.Contact`): + location (:class:`telegram.Location`): + new_chat_participant (:class:`telegram.User`): + left_chat_participant (:class:`telegram.User`): + new_chat_title (str): + new_chat_photo (List[:class:`telegram.PhotoSize`]): + delete_chat_photo (bool): + group_chat_created (bool): + + Args: + message_id (int): + from_user (:class:`telegram.User`): + date (:class:`datetime.datetime`): + chat (:class:`telegram.User` or :class:`telegram.GroupChat`): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + forward_from (Optional[:class:`telegram.User`]): + forward_date (Optional[:class:`datetime.datetime`]): + reply_to_message (Optional[:class:`telegram.Message`]): + text (Optional[str]): + audio (Optional[:class:`telegram.Audio`]): + document (Optional[:class:`telegram.Document`]): + photo (Optional[List[:class:`telegram.PhotoSize`]]): + sticker (Optional[:class:`telegram.Sticker`]): + video (Optional[:class:`telegram.Video`]): + voice (Optional[:class:`telegram.Voice`]): + caption (Optional[str]): + contact (Optional[:class:`telegram.Contact`]): + location (Optional[:class:`telegram.Location`]): + new_chat_participant (Optional[:class:`telegram.User`]): + left_chat_participant (Optional[:class:`telegram.User`]): + new_chat_title (Optional[str]): + new_chat_photo (Optional[List[:class:`telegram.PhotoSize`]): + delete_chat_photo (Optional[bool]): + group_chat_created (Optional[bool]): + """ + + def __init__(self, + message_id, + from_user, + date, + chat, + **kwargs): + # Required + self.message_id = int(message_id) + self.from_user = from_user + self.date = date + self.chat = chat + # Optionals + self.forward_from = kwargs.get('forward_from') + self.forward_date = kwargs.get('forward_date') + self.reply_to_message = kwargs.get('reply_to_message') + self.text = kwargs.get('text', '') + self.audio = kwargs.get('audio') + self.document = kwargs.get('document') + self.photo = kwargs.get('photo') + self.sticker = kwargs.get('sticker') + self.video = kwargs.get('video') + self.voice = kwargs.get('voice') + self.caption = kwargs.get('caption', '') + self.contact = kwargs.get('contact') + self.location = kwargs.get('location') + self.new_chat_participant = kwargs.get('new_chat_participant') + self.left_chat_participant = kwargs.get('left_chat_participant') + self.new_chat_title = kwargs.get('new_chat_title', '') + self.new_chat_photo = kwargs.get('new_chat_photo') + self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False)) + self.group_chat_created = bool(kwargs.get('group_chat_created', False)) + + @property + def chat_id(self): + """int: Short for :attr:`Message.chat.id`""" + return self.chat.id + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Message: + """ + if not data: + return None + + data['from_user'] = User.de_json(data['from']) + data['date'] = datetime.fromtimestamp(data['date']) + if 'first_name' in data.get('chat', ''): + data['chat'] = User.de_json(data.get('chat')) + elif 'title' in data.get('chat', ''): + data['chat'] = GroupChat.de_json(data.get('chat')) + data['forward_from'] = \ + User.de_json(data.get('forward_from')) + data['forward_date'] = \ + Message._fromtimestamp(data.get('forward_date')) + data['reply_to_message'] = \ + Message.de_json(data.get('reply_to_message')) + data['audio'] = \ + Audio.de_json(data.get('audio')) + data['document'] = \ + Document.de_json(data.get('document')) + data['photo'] = \ + PhotoSize.de_list(data.get('photo')) + data['sticker'] = \ + Sticker.de_json(data.get('sticker')) + data['video'] = \ + Video.de_json(data.get('video')) + data['voice'] = \ + Voice.de_json(data.get('voice')) + data['contact'] = \ + Contact.de_json(data.get('contact')) + data['location'] = \ + Location.de_json(data.get('location')) + data['new_chat_participant'] = \ + User.de_json(data.get('new_chat_participant')) + data['left_chat_participant'] = \ + User.de_json(data.get('left_chat_participant')) + data['new_chat_photo'] = \ + PhotoSize.de_list(data.get('new_chat_photo')) + + return Message(**data) + + def __getitem__(self, item): + if item in self.__dict__.keys(): + return self.__dict__[item] + elif item == 'chat_id': + return self.chat.id + + def to_dict(self): + """ + Returns: + dict: + """ + data = super(Message, self).to_dict() + + # Required + data['from'] = data.pop('from_user') + data['date'] = self._totimestamp(self.date) + # Optionals + if self.forward_date: + data['forward_date'] = self._totimestamp(self.forward_date) + if self.photo: + data['photo'] = [p.to_dict() for p in self.photo] + if self.new_chat_photo: + data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo] + + return data + + @staticmethod + def _fromtimestamp(unixtime): + """ + Args: + unixtime (int): + + Returns: + datetime.datetime: + """ + if not unixtime: + return None + + return datetime.fromtimestamp(unixtime) + + @staticmethod + def _totimestamp(dt_obj): + """ + Args: + dt_obj (:class:`datetime.datetime`): + + Returns: + int: + """ + if not dt_obj: + return None + + try: + # Python 3.3+ + return int(dt_obj.timestamp()) + except AttributeError: + # Python 3 (< 3.3) and Python 2 + return int(mktime(dt_obj.timetuple())) diff --git a/lib/telegram/nullhandler.py b/lib/telegram/nullhandler.py new file mode 100644 index 00000000..50bd370e --- /dev/null +++ b/lib/telegram/nullhandler.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a logging NullHandler""" + +import logging + + +class NullHandler(logging.Handler): + """This object represents a logging NullHandler.""" + + def emit(self, record): + """ + Args: + record (str): + """ + pass diff --git a/lib/telegram/parsemode.py b/lib/telegram/parsemode.py new file mode 100644 index 00000000..897693ac --- /dev/null +++ b/lib/telegram/parsemode.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# pylint: disable=R0903 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Message Parse Modes""" + + +class ParseMode(object): + """This object represents a Telegram Message Parse Modes.""" + + MARKDOWN = 'Markdown' diff --git a/lib/telegram/photosize.py b/lib/telegram/photosize.py new file mode 100644 index 00000000..adc47649 --- /dev/null +++ b/lib/telegram/photosize.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram PhotoSize""" + +from telegram import TelegramObject + + +class PhotoSize(TelegramObject): + """This object represents a Telegram PhotoSize. + + Attributes: + file_id (str): + width (int): + height (int): + file_size (int): + + Args: + file_id (str): + width (int): + height (int): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + width, + height, + **kwargs): + # Required + self.file_id = file_id + self.width = int(width) + self.height = int(height) + # Optionals + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.PhotoSize: + """ + if not data: + return None + + return PhotoSize(**data) + + @staticmethod + def de_list(data): + """ + Args: + data (list): + + Returns: + List: + """ + if not data: + return [] + + photos = list() + for photo in data: + photos.append(PhotoSize.de_json(photo)) + + return photos diff --git a/lib/telegram/replykeyboardhide.py b/lib/telegram/replykeyboardhide.py new file mode 100644 index 00000000..f2cb80bb --- /dev/null +++ b/lib/telegram/replykeyboardhide.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram +ReplyKeyboardHide""" + +from telegram import ReplyMarkup + + +class ReplyKeyboardHide(ReplyMarkup): + """This object represents a Telegram ReplyKeyboardHide. + + Attributes: + hide_keyboard (bool): + selective (bool): + + Args: + hide_keyboard (bool): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + selective (Optional[bool]): + """ + + def __init__(self, + hide_keyboard=True, + **kwargs): + # Required + self.hide_keyboard = bool(hide_keyboard) + # Optionals + self.selective = bool(kwargs.get('selective', False)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.ReplyKeyboardHide: + """ + if not data: + return None + + return ReplyKeyboardHide(**data) diff --git a/lib/telegram/replykeyboardmarkup.py b/lib/telegram/replykeyboardmarkup.py new file mode 100644 index 00000000..39912ea3 --- /dev/null +++ b/lib/telegram/replykeyboardmarkup.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram +ReplyKeyboardMarkup""" + +from telegram import ReplyMarkup + + +class ReplyKeyboardMarkup(ReplyMarkup): + """This object represents a Telegram ReplyKeyboardMarkup. + + Attributes: + keyboard (List[List[str]]): + resize_keyboard (bool): + one_time_keyboard (bool): + selective (bool): + + Args: + keyboard (List[List[str]]): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + resize_keyboard (Optional[bool]): + one_time_keyboard (Optional[bool]): + selective (Optional[bool]): + """ + + def __init__(self, + keyboard, + **kwargs): + # Required + self.keyboard = keyboard + # Optionals + self.resize_keyboard = bool(kwargs.get('resize_keyboard', False)) + self.one_time_keyboard = bool(kwargs.get('one_time_keyboard', False)) + self.selective = bool(kwargs.get('selective', False)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.ReplyKeyboardMarkup: + """ + if not data: + return None + + return ReplyKeyboardMarkup(**data) diff --git a/lib/telegram/replymarkup.py b/lib/telegram/replymarkup.py new file mode 100644 index 00000000..1d086c89 --- /dev/null +++ b/lib/telegram/replymarkup.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""Base class for Telegram ReplyMarkup Objects""" + +from telegram import TelegramObject + + +class ReplyMarkup(TelegramObject): + """Base class for Telegram ReplyMarkup Objects""" + + @staticmethod + def de_json(data): + pass diff --git a/lib/telegram/sticker.py b/lib/telegram/sticker.py new file mode 100644 index 00000000..06089e35 --- /dev/null +++ b/lib/telegram/sticker.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Sticker""" + +from telegram import PhotoSize, TelegramObject + + +class Sticker(TelegramObject): + """This object represents a Telegram Sticker. + + Attributes: + file_id (str): + width (int): + height (int): + thumb (:class:`telegram.PhotoSize`): + file_size (int): + + Args: + file_id (str): + width (int): + height (int): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + thumb (Optional[:class:`telegram.PhotoSize`]): + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + width, + height, + **kwargs): + # Required + self.file_id = str(file_id) + self.width = int(width) + self.height = int(height) + # Optionals + self.thumb = kwargs.get('thumb') + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Sticker: + """ + if not data: + return None + + data['thumb'] = PhotoSize.de_json(data.get('thumb')) + + return Sticker(**data) diff --git a/lib/telegram/update.py b/lib/telegram/update.py new file mode 100644 index 00000000..3bccc7d2 --- /dev/null +++ b/lib/telegram/update.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Update""" + +from telegram import Message, TelegramObject + + +class Update(TelegramObject): + """This object represents a Telegram Update. + + Attributes: + update_id (int): + message (:class:`telegram.Message`): + + Args: + update_id (int): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + message (Optional[:class:`telegram.Message`]): + """ + def __init__(self, + update_id, + **kwargs): + # Required + self.update_id = int(update_id) + # Optionals + self.message = kwargs.get('message') + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Update: + """ + if not data: + return None + + data['message'] = Message.de_json(data['message']) + + return Update(**data) diff --git a/lib/telegram/user.py b/lib/telegram/user.py new file mode 100644 index 00000000..4fa88295 --- /dev/null +++ b/lib/telegram/user.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# pylint: disable=C0103,W0622 +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram User""" + +from telegram import TelegramObject + + +class User(TelegramObject): + """This object represents a Telegram User. + + Attributes: + id (int): + first_name (str): + last_name (str): + username (str): + type (str): + + Args: + id (int): + first_name (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + type (Optional[str]): + last_name (Optional[str]): + username (Optional[str]): + """ + + def __init__(self, + id, + first_name, + **kwargs): + # Required + self.id = int(id) + self.first_name = first_name + # Optionals + self.type = kwargs.get('type', '') + self.last_name = kwargs.get('last_name', '') + self.username = kwargs.get('username', '') + + @property + def name(self): + """str: """ + if self.username: + return '@%s' % self.username + if self.last_name: + return '%s %s' % (self.first_name, self.last_name) + return self.first_name + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.User: + """ + if not data: + return None + + return User(**data) diff --git a/lib/telegram/userprofilephotos.py b/lib/telegram/userprofilephotos.py new file mode 100644 index 00000000..bce623b3 --- /dev/null +++ b/lib/telegram/userprofilephotos.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram +UserProfilePhotos""" + +from telegram import PhotoSize, TelegramObject + + +class UserProfilePhotos(TelegramObject): + """This object represents a Telegram UserProfilePhotos. + + Attributes: + total_count (int): + photos (List[List[:class:`telegram.PhotoSize`]]): + + Args: + total_count (int): + photos (List[List[:class:`telegram.PhotoSize`]]): + """ + + def __init__(self, + total_count, + photos): + # Required + self.total_count = int(total_count) + self.photos = photos + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.UserProfilePhotos: + """ + if not data: + return None + + data['photos'] = [PhotoSize.de_list(photo) for photo in data['photos']] + + return UserProfilePhotos(**data) + + def to_dict(self): + """ + Returns: + dict: + """ + data = super(UserProfilePhotos, self).to_dict() + + data['photos'] = [] + for photo in self.photos: + data['photos'].append([x.to_dict() for x in photo]) + + return data diff --git a/lib/telegram/utils/__init__.py b/lib/telegram/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/telegram/utils/request.py b/lib/telegram/utils/request.py new file mode 100644 index 00000000..5347f9c6 --- /dev/null +++ b/lib/telegram/utils/request.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# pylint: disable=no-name-in-module,unused-import +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains methods to make POST and GET requests""" + +import json + +try: + from urllib.parse import urlencode + from urllib.request import urlopen, urlretrieve, Request + from urllib.error import HTTPError, URLError +except ImportError: + from urllib import urlencode, urlretrieve + from urllib2 import urlopen, Request + from urllib2 import HTTPError, URLError + +from telegram import (InputFile, TelegramError) + + +def _parse(json_data): + """Try and parse the JSON returned from Telegram and return an empty + dictionary if there is any error. + + Args: + url: + urllib.urlopen object + + Returns: + A JSON parsed as Python dict with results. + """ + data = json.loads(json_data.decode()) + + if not data.get('ok') and data.get('description'): + return data['description'] + + return data['result'] + + +def get(url): + """Request an URL. + Args: + url: + The web location we want to retrieve. + + Returns: + A JSON object. + """ + result = urlopen(url).read() + + return _parse(result) + + +def post(url, + data): + """Request an URL. + Args: + url: + The web location we want to retrieve. + data: + A dict of (str, unicode) key/value pairs. + + Returns: + A JSON object. + """ + try: + if InputFile.is_inputfile(data): + data = InputFile(data) + request = Request(url, + data=data.to_form(), + headers=data.headers) + else: + data = json.dumps(data) + request = Request(url, + data=data.encode(), + headers={'Content-Type': 'application/json'}) + + result = urlopen(request).read() + except HTTPError as error: + if error.getcode() == 403: + raise TelegramError('Unauthorized') + if error.getcode() == 502: + raise TelegramError('Bad Gateway') + + message = _parse(error.read()) + raise TelegramError(message) + + return _parse(result) + + +def download(url, + filename): + """Download a file by its URL. + Args: + url: + The web location we want to retrieve. + + filename: + The filename wihtin the path to download the file. + """ + + urlretrieve(url, filename) diff --git a/lib/telegram/video.py b/lib/telegram/video.py new file mode 100644 index 00000000..d6930633 --- /dev/null +++ b/lib/telegram/video.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Video""" + +from telegram import PhotoSize, TelegramObject + + +class Video(TelegramObject): + """This object represents a Telegram Video. + + Attributes: + file_id (str): + width (int): + height (int): + duration (int): + thumb (:class:`telegram.PhotoSize`): + mime_type (str): + file_size (int): + + Args: + file_id (str): + width (int): + height (int): + duration (int): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + thumb (Optional[:class:`telegram.PhotoSize`]): + mime_type (Optional[str]): + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + width, + height, + duration, + **kwargs): + # Required + self.file_id = str(file_id) + self.width = int(width) + self.height = int(height) + self.duration = int(duration) + # Optionals + self.thumb = kwargs.get('thumb') + self.mime_type = str(kwargs.get('mime_type', '')) + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Video: + """ + if not data: + return None + + data['thumb'] = PhotoSize.de_json(data.get('thumb')) + + return Video(**data) diff --git a/lib/telegram/voice.py b/lib/telegram/voice.py new file mode 100644 index 00000000..53dcdc44 --- /dev/null +++ b/lib/telegram/voice.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015 Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +"""This module contains a object that represents a Telegram Voice""" + +from telegram import TelegramObject + + +class Voice(TelegramObject): + """This object represents a Telegram Voice. + + Attributes: + file_id (str): + duration (int): + mime_type (str): + file_size (int): + + Args: + file_id (str): + **kwargs: Arbitrary keyword arguments. + + Keyword Args: + duration (Optional[int]): + mime_type (Optional[str]): + file_size (Optional[int]): + """ + + def __init__(self, + file_id, + **kwargs): + # Required + self.file_id = str(file_id) + # Optionals + self.duration = int(kwargs.get('duration', 0)) + self.mime_type = str(kwargs.get('mime_type', '')) + self.file_size = int(kwargs.get('file_size', 0)) + + @staticmethod + def de_json(data): + """ + Args: + data (str): + + Returns: + telegram.Voice: + """ + if not data: + return None + + return Voice(**data)