Merge pull request #54 from leandrotoledo/testing

Merge Testing branch
This commit is contained in:
Leandro Toledo 2015-09-04 17:59:37 -03:00
commit e693922fe8
42 changed files with 1509 additions and 647 deletions

View file

@ -4,9 +4,11 @@ python:
- "2.7"
- "3.3"
- "3.4"
- "pypy"
- "pypy3"
install:
- pip install coveralls
script:
coverage run telegram_test.py
coverage run tests/test_*.py
after_success:
coveralls

View file

@ -14,6 +14,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `JASON0916 <https://github.com/JASON0916>`_
- `JRoot3D <https://github.com/JRoot3D>`_
- `macrojames <https://github.com/macrojames>`_
- `njittam <https://github.com/njittam>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `wjt <https://github.com/wjt>`_

View file

@ -1,7 +1,4 @@
help:
@echo " clean remove unwanted stuff"
@echo " lint check style with flake8"
@echo " test run tests"
.PHONY: clean pep8 lint test
clean:
rm -fr build
@ -10,8 +7,18 @@ clean:
find . -name '*.pyo' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
pep8:
flake8 telegram
lint:
flake8 --doctests --max-complexity 10 telegram
pylint -E telegram
test:
python telegram_test.py
@- $(foreach TEST, $(wildcard tests/test_*.py), python $(TEST))
help:
@echo "Available targets:"
@echo "- clean Clean up the source directory"
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- test Run tests"

View file

@ -0,0 +1,89 @@
# There could be some unused imports
from inspect import getmembers, ismethod
import threading
import logging
import telegram
import time
from telegram import CommandHandlerWithHelp, CommandHandler
class ExampleCommandHandler(CommandHandlerWithHelp):
"""This is an example how to use a CommandHandlerWithHelp or just a CommandHandler.
If You want to use a CommandHandler it is very easy.
create a class which inherits a CommandHandler.
create a method in this class which start with 'command_' and takes 1 argument: 'update' (which comes directly from
getUpdate()).
If you inherit CommandHandlerWithHelp it also creates a nice /help for you.
"""
def __init__(self, bot): # only necessary for a WithHelp
super(ExampleCommandHandler, self).__init__(bot)
self._help_title = 'Welcome this is a help file!' # optional
self._help_before_list = """
Yeah here I explain some things about this bot.
and of course I can do this in Multiple lines.
""" # default is empty
self._help_list_title = ' These are the available commands:' # optional
self._help_after_list = ' These are some footnotes' # default is empty
self.is_reply = True # default is True
# only necessary if you want to override to default
def _command_not_found(self, update):
"""Inform the telegram user that the command was not found."""
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = "Sorry, I don't know how to do {command}.".format(command=update.message.text.split(' ')[0])
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
# creates /test command. This code gets called when a telegram user enters /test
def command_test(self, update):
""" Test if the server is online. """
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = 'Yeah, the server is online!'
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
# creates /parrot command
def command_parrot(self, update):
""" Says back what you say after the command"""
chat_id = update.message.chat.id
reply_to = update.message.message_id
send = update.message.text.split(' ')
message = update.message.text[len(send[0]):]
if len(send) == 1:
message = '...'
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
# creates /p command
def command_p(self, update):
"""Does the same as parrot."""
return self.command_parrot(update)
# this doesn't create a command.
def another_test(self, update):
""" This won't be called by the CommandHandler.
This is an example of a function that isn't a command in telegram.
Because it didn't start with 'command_'.
"""
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = 'Yeah, this is another test'
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
class Exampe2CommandHandler(CommandHandler):
"""
This is an example of a small working CommandHandler with only one command.
"""
def command_test(self, update):
""" Test if the server is online. """
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = 'Yeah, the server is online!'
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
if __name__ == '__main__':
import telegram
token = '' # use your own token here
Bot = telegram.Bot(token=token)
test_command_handler = ExampleCommandHandler(Bot)
test_command_handler.run()

View file

@ -16,14 +16,13 @@
# 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.7.1'
from .base import TelegramObject
from .user import User
from .message import Message
from .update import Update
from .groupchat import GroupChat
from .photosize import PhotoSize
from .audio import Audio
@ -39,10 +38,12 @@ from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup
from .replykeyboardhide import ReplyKeyboardHide
from .forcereply import ForceReply
from .inputfile import InputFile
from .error import TelegramError
from .inputfile import InputFile
from .nullhandler import NullHandler
from .emoji import Emoji
from .message import Message
from .update import Update
from .bot import Bot
__all__ = ['Bot', 'Emoji', 'TelegramError', 'InputFile', 'ReplyMarkup',

View file

@ -16,43 +16,57 @@
# 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,
performer=None,
title=None,
mime_type=None,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.duration = duration
self.performer = performer
self.title = title
self.mime_type = mime_type
self.file_size = file_size
self.duration = int(duration)
# Optionals
self.performer = kwargs.get('performer', '')
self.title = kwargs.get('title', '')
self.mime_type = kwargs.get('mime_type', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
return Audio(file_id=data.get('file_id', None),
duration=data.get('duration', None),
performer=data.get('performer', None),
title=data.get('title', None),
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
"""
Args:
data (str):
def to_dict(self):
data = {'file_id': self.file_id,
'duration': self.duration}
if self.performer:
data['performer'] = self.performer
if self.title:
data['title'] = self.title
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
Returns:
telegram.Audio:
"""
if not data:
return None
return Audio(**data)

View file

@ -16,13 +16,14 @@
# 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, abstractmethod
from abc import ABCMeta
class TelegramObject(object):
"""Base class for most telegram object"""
"""Base class for most telegram objects"""
__metaclass__ = ABCMeta
@ -34,11 +35,34 @@ class TelegramObject(object):
@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())
@abstractmethod
def to_dict(self):
return
"""
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

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,6 +17,7 @@
# 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 json
try:
@ -32,11 +34,27 @@ import logging
from telegram import (User, Message, Update, UserProfilePhotos, TelegramError,
ReplyMarkup, InputFile, TelegramObject, NullHandler)
h = NullHandler()
logging.getLogger(__name__).addHandler(h)
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,
@ -50,11 +68,17 @@ class Bot(TelegramObject):
self.bot = None
self.log = logging.getLogger(__name__)
self.logger = logging.getLogger(__name__)
def info(func):
"""
Returns:
"""
@functools.wraps(func)
def decorator(self, *args, **kwargs):
"""
decorator
"""
if not self.bot:
self.getMe()
@ -65,36 +89,48 @@ class Bot(TelegramObject):
@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):
logger.debug('Entering: %s' % func.__name__)
"""
decorator
"""
logger.debug('Entering: %s', func.__name__)
result = func(self, *args, **kwargs)
logger.debug(result)
logger.debug('Exiting: %s' % func.__name__)
logger.debug('Exiting: %s', func.__name__)
return result
return decorator
@ -105,8 +141,14 @@ class Bot(TelegramObject):
"""
@functools.wraps(func)
def decorator(self, *args, **kwargs):
"""
decorator
"""
url, data = func(self, *args, **kwargs)
if not kwargs.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
@ -118,8 +160,8 @@ class Bot(TelegramObject):
else:
data['reply_markup'] = reply_markup
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
json_data = Bot._requestUrl(url, 'POST', data=data)
data = Bot._parseAndCheckTelegram(json_data)
if data is True:
return data
@ -150,8 +192,7 @@ class Bot(TelegramObject):
chat_id,
text,
disable_web_page_preview=None,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send text messages.
Args:
@ -222,8 +263,7 @@ class Bot(TelegramObject):
chat_id,
photo,
caption=None,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send photos.
Args:
@ -265,8 +305,7 @@ class Bot(TelegramObject):
duration=None,
performer=None,
title=None,
reply_to_message_id=None,
reply_markup=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
@ -321,8 +360,7 @@ class Bot(TelegramObject):
def sendDocument(self,
chat_id,
document,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send general files.
Args:
@ -355,8 +393,7 @@ class Bot(TelegramObject):
def sendSticker(self,
chat_id,
sticker,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send .webp stickers.
Args:
@ -391,8 +428,7 @@ class Bot(TelegramObject):
video,
duration=None,
caption=None,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
@ -437,8 +473,7 @@ class Bot(TelegramObject):
chat_id,
voice,
duration=None,
reply_to_message_id=None,
reply_markup=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
@ -482,8 +517,7 @@ class Bot(TelegramObject):
chat_id,
latitude,
longitude,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send point on the map.
Args:
@ -617,16 +651,17 @@ class Bot(TelegramObject):
data = self._parseAndCheckTelegram(json_data)
if data:
self.log.info(
'Getting updates: %s' % [u['update_id'] for u in data])
self.logger.info(
'Getting updates: %s', [u['update_id'] for u in data])
else:
self.log.info('No new updates found.')
self.logger.info('No new updates found.')
return [Update.de_json(x) for x in data]
@log
def setWebhook(self,
webhook_url):
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
@ -643,15 +678,19 @@ class Bot(TelegramObject):
"""
url = '%s/setWebhook' % self.base_url
data = {'url': webhook_url}
data = {}
if webhook_url:
data['url'] = webhook_url
if certificate:
data['certificate'] = certificate
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
return True
return data
def _requestUrl(self,
url,
@staticmethod
def _requestUrl(url,
method,
data=None):
"""Request an URL.
@ -688,12 +727,12 @@ class Bot(TelegramObject):
url,
urlencode(data).encode()
).read()
except IOError as e:
raise TelegramError(str(e))
except HTTPError as e:
raise TelegramError(str(e))
except URLError as e:
raise TelegramError(str(e))
except IOError as e:
raise TelegramError(str(e))
if method == 'GET':
try:
@ -701,8 +740,8 @@ class Bot(TelegramObject):
except URLError as e:
raise TelegramError(str(e))
def _parseAndCheckTelegram(self,
json_data):
@staticmethod
def _parseAndCheckTelegram(json_data):
"""Try and parse the JSON returned from Telegram and return an empty
dictionary if there is any error.
@ -725,7 +764,15 @@ class Bot(TelegramObject):
return data['result']
@staticmethod
def de_json(data):
pass
def to_dict(self):
"""
Returns:
dict:
"""
data = {'id': self.id,
'username': self.username,
'first_name': self.username}

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,8 +17,12 @@
# 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'

174
telegram/command_handler.py Normal file
View file

@ -0,0 +1,174 @@
from inspect import getmembers, ismethod
import threading
import logging
import telegram
import time
logger = logging.getLogger(__name__)
__all__ = ['CommandHandler', 'CommandHandlerWithHelp']
class CommandHandler(object):
""" This handles incomming commands and gives an easy way to create commands.
How to use this:
create a new class which inherits this class or CommandHandlerWithHelp.
define new methods that start with 'command_' and then the command_name.
run run()
"""
def __init__(self, bot):
self.bot = bot # a telegram bot
self.isValidCommand = None # a function that returns a boolean and takes one agrument an update. if False is returned the the comaand is not executed.
def _get_command_func(self, command):
if command[0] == '/':
command = command[1:]
if hasattr(self, 'command_' + command):
return self.__getattribute__('command_' + command) # a function
else:
return None
def run(self, make_thread=True, last_update_id=None, thread_timeout=2, sleep=0.2):
"""Continuously check for commands and run the according method
Args:
make_thread:
if True make a thread for each command it found.
if False make run the code linearly
last_update:
the offset arg from getUpdates and is kept up to date within this function
thread_timeout:
The timeout on a thread. If a thread is alive after this period then try to join the thread in
the next loop.
"""
old_threads = []
while True:
time.sleep(sleep)
threads, last_update_id = self.run_once(make_thread=make_thread, last_update_id=last_update_id)
for t in threads:
t.start()
for t in old_threads:
threads.append(t)
old_threads = []
for t in threads:
t.join(timeout=thread_timeout)
if t.isAlive():
old_threads.append(t)
def run_once(self, make_thread=True, last_update_id=None):
""" Check the the messages for commands and make a Thread with the command or run the command depending on make_thread.
Args:
make_thread:
True: the function returns a list with threads. Which didn't start yet.
False: the function just runs the command it found and returns an empty list.
last_update_id:
the offset arg from getUpdates and is kept up to date within this function
Returns:
A tuple of two elements. The first element is a list with threads which didn't start yet or an empty list if
make_threads==False. The second element is the updated las_update_id
"""
bot_name = self.bot.getMe().username
threads = []
try:
updates = self.bot.getUpdates(offset=last_update_id)
except:
updates = []
for update in updates:
last_update_id = update.update_id + 1
message = update.message
if message.text[0] == '/':
command, username = message.text.split(' ')[0], bot_name
if '@' in command:
command, username = command.split('@')
if username == bot_name:
command_func = self._get_command_func(command)
if command_func is not None:
self.bot.sendChatAction(update.message.chat.id,telegram.ChatAction.TYPING)
if self.isValidCommand is None or self.isValidCommand(update):
if make_thread:
t = threading.Thread(target=command_func, args=(update,))
threads.append(t)
else:
command_func(update)
else:
self._command_not_found(update) # TODO this must be another function.
else:
if make_thread:
t = threading.Thread(target=self._command_not_found, args=(update,))
threads.append(t)
else:
self._command_not_valid(update)
return threads, last_update_id
def _command_not_valid(self, update):
"""Inform the telegram user that the command was not found.
Override this method if you want to do it another way then by sending the the text:
Sorry, I didn't understand the command: /command[@bot].
"""
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = "Sorry, the command was not authorised or valid: {command}.".format(command=update.message.text.split(' ')[0])
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
def _command_not_found(self, update):
"""Inform the telegram user that the command was not found.
Override this method if you want to do it another way then by sending the the text:
Sorry, I didn't understand the command: /command[@bot].
"""
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = "Sorry, I didn't understand the command: {command}.".format(command=update.message.text.split(' ')[0])
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)
class CommandHandlerWithHelp(CommandHandler):
""" This CommandHandler has a builtin /help. It grabs the text from the docstrings of command_ functions."""
def __init__(self, bot):
super(CommandHandlerWithHelp, self).__init__(bot)
self._help_title = 'Welcome to {name}.'.format(name=self.bot.getMe().username) # the title of help
self._help_before_list = '' # text with information about the bot
self._help_after_list = '' # a footer
self._help_list_title = 'These are the commands:' # the title of the list
self.is_reply = True
self.command_start = self.command_help
def _generate_help(self):
""" Generate a string which can be send as a help file.
This function generates a help file from all the docstrings from the commands.
so docstrings of methods that start with command_ should explain what a command does and how a to use the
command to the telegram user.
"""
command_functions = [attr[1] for attr in getmembers(self, predicate=ismethod) if attr[0][:8] == 'command_']
help_message = self._help_title + '\n\n'
help_message += self._help_before_list + '\n\n'
help_message += self._help_list_title + '\n'
for command_function in command_functions:
if command_function.__doc__ is not None:
help_message += ' /' + command_function.__name__[8:] + ' - ' + command_function.__doc__ + '\n'
else:
help_message += ' /' + command_function.__name__[8:] + ' - ' + '\n'
help_message += '\n'
help_message += self._help_after_list
return help_message
def _command_not_found(self, update):
"""Inform the telegram user that the command was not found."""
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = 'Sorry, I did not understand the command: {command}. Please see /help for all available commands'
if self.is_reply:
self.bot.sendMessage(chat_id, message.format(command=update.message.text.split(' ')[0]),
reply_to_message_id=reply_to)
else:
self.bot.sendMessage(chat_id, message.format(command=update.message.text.split(' ')[0]))
def command_help(self, update):
""" The help file. """
chat_id = update.message.chat.id
reply_to = update.message.message_id
message = self._generate_help()
self.bot.sendMessage(chat_id, message, reply_to_message_id=reply_to)

View file

@ -16,33 +16,51 @@
# 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,
last_name=None,
user_id=None):
**kwargs):
# Required
self.phone_number = phone_number
self.first_name = first_name
self.last_name = last_name
self.user_id = user_id
# Optionals
self.last_name = kwargs.get('last_name', '')
self.user_id = int(kwargs.get('user_id', 0))
@staticmethod
def de_json(data):
return Contact(phone_number=data.get('phone_number', None),
first_name=data.get('first_name', None),
last_name=data.get('last_name', None),
user_id=data.get('user_id', None))
"""
Args:
data (str):
def to_dict(self):
data = {'phone_number': self.phone_number,
'first_name': self.first_name}
if self.last_name:
data['last_name'] = self.last_name
if self.user_id:
data['user_id'] = self.user_id
return data
Returns:
telegram.Contact:
"""
if not data:
return None
return Contact(**data)

View file

@ -16,45 +16,53 @@
# 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 TelegramObject
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,
thumb=None,
file_name=None,
mime_type=None,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.thumb = thumb
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
# Optionals
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name', '')
self.mime_type = kwargs.get('mime_type', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Document(file_id=data.get('file_id', None),
thumb=thumb,
file_name=data.get('file_name', None),
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
Returns:
telegram.Document:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id}
if self.thumb:
data['thumb'] = self.thumb.to_dict()
if self.file_name:
data['file_name'] = self.file_name
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
return Document(**data)

View file

@ -1,5 +1,6 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -17,8 +18,12 @@
# 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'
@ -155,16 +160,26 @@ class Emoji(object):
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'
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'

83
telegram/enchancedbot.py Normal file
View file

@ -0,0 +1,83 @@
import telegram
class NoSuchCommandException(BaseException):
pass
class CommandDispatcher:
def __init__(self,):
self.commands = list()
self.default = None
def addCommand(self, command, callback):
self.commands.append((command, callback))
def setDefault(self, callback):
self.default = callback
def dispatch(self, update):
if hasattr(update.message, 'text'):
text = update.message.text
else:
text = ''
user_id = update.message.from_user.id
com = text.split('@')[0]
for command, callback in self.commands:
if com == command:
return callback(command, user_id)
if self.default is not None:
return self.default(text, user_id)
else:
raise NoSuchCommandException()
class EnhancedBot(telegram.Bot):
"""The Bot class with command dispatcher added.
>>> bot = EnhancedBot(token=TOKEN)
>>> @bot.command('/start')
... def start(command, user_id):
... # should return a tuple: (text, reply_id, custom_keyboard)
... return ("Hello, there! Your id is {}".format(user_id), None, None)
>>> while True:
... bot.processUpdates()
... time.sleep(3)
"""
def __init__(self, token):
self.dispatcher = CommandDispatcher()
telegram.Bot.__init__(self, token=token)
self.offset = 0 #id of the last processed update
def command(self, *names, default=False):
"""Decorator for adding callbacks for commands."""
def inner_command(callback):
for name in names:
self.dispatcher.addCommand(name, callback)
if default:
self.dispatcher.setDefault(callback)
return callback # doesn't touch the callback, so we can use it
return inner_command
def processUpdates(self):
updates = self.getUpdates(offset=self.offset)
for update in updates:
print('processing update: {}'.format(str(update.to_dict())))
self.offset = update.update_id + 1
if not hasattr(update, 'message'):
continue
try:
answer, reply_to, reply_markup = self.dispatcher.dispatch(update)
except Exception as e:
print('error occured') # TODO logging
print(update.to_dict())
raise e
if answer is not None:
self.sendMessage(chat_id=update.message.chat_id,
text=answer,
reply_to_message_id=reply_to,
reply_markup=reply_markup)

View file

@ -16,11 +16,16 @@
# 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"""
class TelegramError(Exception):
"""Base class for Telegram errors."""
"""This object represents a Telegram Error."""
@property
def message(self):
'''Returns the first argument used to construct this error.'''
"""
Returns:
str:
"""
return self.args[0]

View file

@ -16,24 +16,45 @@
# 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,
selective=None):
self.force_reply = force_reply
self.selective = selective
**kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data):
return ForceReply(force_reply=data.get('force_reply', None),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'force_reply': self.force_reply}
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ForceReply:
"""
if not data:
return None
return ForceReply(**data)

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,23 +17,40 @@
# 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):
Args:
id (int):
title (str):
"""
def __init__(self,
id,
title):
self.id = id
# Required
self.id = int(id)
self.title = title
@staticmethod
def de_json(data):
return GroupChat(id=data.get('id', None),
title=data.get('title', None))
"""
Args:
data (str):
def to_dict(self):
data = {'id': self.id,
'title': self.title}
return data
Returns:
telegram.GroupChat:
"""
if not data:
return None
return GroupChat(**data)

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,6 +17,7 @@
# 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
@ -29,7 +31,7 @@ import os
import sys
import imghdr
from .error import TelegramError
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot' \
@ -37,6 +39,8 @@ USER_AGENT = 'Python Telegram Bot' \
class InputFile(object):
"""This object represents a Telegram InputFile."""
def __init__(self,
data):
self.data = data
@ -57,6 +61,9 @@ class InputFile(object):
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()
@ -71,14 +78,26 @@ class InputFile(object):
@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
@ -105,9 +124,14 @@ class InputFile(object):
form.append('--' + self.boundary + '--')
form.append('')
return self._parse(form)
return InputFile._parse(form)
def _parse(self, form):
@staticmethod
def _parse(form):
"""
Returns:
str:
"""
if sys.version_info > (3,):
# on Python 3 form needs to be byte encoded
encoded_form = []
@ -125,11 +149,10 @@ class InputFile(object):
"""Check if the content file is an image by analyzing its headers.
Args:
stream:
A str representing the content of a file.
stream (str): A str representing the content of a file.
Returns:
The str mimetype of an image.
str: The str mimetype of an image.
"""
image = imghdr.what(None, stream)
if image:
@ -142,8 +165,7 @@ class InputFile(object):
"""Check if the request is a file request.
Args:
data:
A dict of (str, unicode) key/value pairs
data (str): A dict of (str, unicode) key/value pairs
Returns:
bool

View file

@ -16,23 +16,40 @@
# 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):
self.longitude = longitude
self.latitude = latitude
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)
@staticmethod
def de_json(data):
return Location(longitude=data.get('longitude', None),
latitude=data.get('latitude', None))
"""
Args:
data (str):
def to_dict(self):
data = {'longitude': self.longitude,
'latitude': self.latitude}
return data
Returns:
telegram.Location:
"""
if not data:
return None
return Location(**data)

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,251 +17,209 @@
# 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 telegram import TelegramObject
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,
forward_from=None,
forward_date=None,
reply_to_message=None,
text=None,
audio=None,
document=None,
photo=None,
sticker=None,
video=None,
voice=None,
caption=None,
contact=None,
location=None,
new_chat_participant=None,
left_chat_participant=None,
new_chat_title=None,
new_chat_photo=None,
delete_chat_photo=None,
group_chat_created=None):
self.message_id = message_id
**kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
self.date = date
self.chat = chat
self.forward_from = forward_from
self.forward_date = forward_date
self.reply_to_message = reply_to_message
self.text = text
self.audio = audio
self.document = document
self.photo = photo
self.sticker = sticker
self.video = video
self.voice = voice
self.caption = caption
self.contact = contact
self.location = location
self.new_chat_participant = new_chat_participant
self.left_chat_participant = left_chat_participant
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
self.delete_chat_photo = delete_chat_photo
self.group_chat_created = group_chat_created
# 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):
if 'from' in data: # from is a reserved word, use from_user instead.
from telegram import User
from_user = User.de_json(data['from'])
else:
from_user = None
"""
Args:
data (str):
if 'date' in data:
date = datetime.fromtimestamp(data['date'])
else:
date = None
Returns:
telegram.Message:
"""
if not data:
return None
if 'chat' in data:
if 'first_name' in data['chat']:
from telegram import User
chat = User.de_json(data['chat'])
if 'title' in data['chat']:
from telegram import GroupChat
chat = GroupChat.de_json(data['chat'])
else:
chat = 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'))
if 'forward_from' in data:
from telegram import User
forward_from = User.de_json(data['forward_from'])
else:
forward_from = None
if 'forward_date' in data:
forward_date = datetime.fromtimestamp(data['forward_date'])
else:
forward_date = None
if 'reply_to_message' in data:
reply_to_message = Message.de_json(data['reply_to_message'])
else:
reply_to_message = None
if 'audio' in data:
from telegram import Audio
audio = Audio.de_json(data['audio'])
else:
audio = None
if 'document' in data:
from telegram import Document
document = Document.de_json(data['document'])
else:
document = None
if 'photo' in data:
from telegram import PhotoSize
photo = [PhotoSize.de_json(x) for x in data['photo']]
else:
photo = None
if 'sticker' in data:
from telegram import Sticker
sticker = Sticker.de_json(data['sticker'])
else:
sticker = None
if 'video' in data:
from telegram import Video
video = Video.de_json(data['video'])
else:
video = None
if 'voice' in data:
from telegram import Voice
voice = Voice.de_json(data['voice'])
else:
voice = None
if 'contact' in data:
from telegram import Contact
contact = Contact.de_json(data['contact'])
else:
contact = None
if 'location' in data:
from telegram import Location
location = Location.de_json(data['location'])
else:
location = None
if 'new_chat_participant' in data:
from telegram import User
new_chat_participant = User.de_json(data['new_chat_participant'])
else:
new_chat_participant = None
if 'left_chat_participant' in data:
from telegram import User
left_chat_participant = User.de_json(data['left_chat_participant'])
else:
left_chat_participant = None
if 'new_chat_photo' in data:
from telegram import PhotoSize
new_chat_photo = \
[PhotoSize.de_json(x) for x in data['new_chat_photo']]
else:
new_chat_photo = None
return Message(message_id=data.get('message_id', None),
from_user=from_user,
date=date,
chat=chat,
forward_from=forward_from,
forward_date=forward_date,
reply_to_message=reply_to_message,
text=data.get('text', ''),
audio=audio,
document=document,
photo=photo,
sticker=sticker,
video=video,
voice=voice,
caption=data.get('caption', ''),
contact=contact,
location=location,
new_chat_participant=new_chat_participant,
left_chat_participant=left_chat_participant,
new_chat_title=data.get('new_chat_title', None),
new_chat_photo=new_chat_photo,
delete_chat_photo=data.get('delete_chat_photo', None),
group_chat_created=data.get('group_chat_created', None))
return Message(**data)
def to_dict(self):
data = {'message_id': self.message_id,
'from': self.from_user.to_dict(),
'chat': self.chat.to_dict()}
try:
# Python 3.3+ supports .timestamp()
data['date'] = int(self.date.timestamp())
"""
Returns:
dict:
"""
data = super(Message, self).to_dict()
if self.forward_date:
data['forward_date'] = int(self.forward_date.timestamp())
except AttributeError:
# _totimestamp() for Python 3 (< 3.3) and Python 2
data['date'] = self._totimestamp(self.date)
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.forward_from:
data['forward_from'] = self.forward_from.to_dict()
if self.reply_to_message:
data['reply_to_message'] = self.reply_to_message.to_dict()
if self.text:
data['text'] = self.text
if self.audio:
data['audio'] = self.audio.to_dict()
if self.document:
data['document'] = self.document.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.sticker:
data['sticker'] = self.sticker.to_dict()
if self.video:
data['video'] = self.video.to_dict()
if self.voice:
data['voice'] = self.voice.to_dict()
if self.caption:
data['caption'] = self.caption
if self.contact:
data['contact'] = self.contact.to_dict()
if self.location:
data['location'] = self.location.to_dict()
if self.new_chat_participant:
data['new_chat_participant'] = self.new_chat_participant.to_dict()
if self.left_chat_participant:
data['left_chat_participant'] = \
self.left_chat_participant.to_dict()
if self.new_chat_title:
data['new_chat_title'] = self.new_chat_title
if self.new_chat_photo:
data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo]
if self.delete_chat_photo:
data['delete_chat_photo'] = self.delete_chat_photo
if self.group_chat_created:
data['group_chat_created'] = self.group_chat_created
return data
@staticmethod
def _totimestamp(dt):
return int(mktime(dt.timetuple()))
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()))

View file

@ -16,10 +16,17 @@
# 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

View file

@ -16,32 +16,70 @@
# 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,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.width = width
self.height = height
self.file_size = file_size
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
return PhotoSize(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
file_size=data.get('file_size', None))
"""
Args:
data (str):
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height}
if self.file_size:
data['file_size'] = self.file_size
return data
Returns:
telegram.PhotoSize:
"""
if not data:
return None
return PhotoSize(**data)
@staticmethod
def de_list(data):
"""
Args:
data (list):
Returns:
List<telegram.PhotoSize>:
"""
if not data:
return []
photos = list()
for photo in data:
photos.append(PhotoSize.de_json(photo))
return photos

View file

@ -16,24 +16,45 @@
# 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,
selective=None):
self.hide_keyboard = hide_keyboard
self.selective = selective
**kwargs):
# Required
self.hide_keyboard = bool(hide_keyboard)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data):
return ReplyKeyboardHide(hide_keyboard=data.get('hide_keyboard', None),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'hide_keyboard': self.hide_keyboard}
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ReplyKeyboardHide:
"""
if not data:
return None
return ReplyKeyboardHide(**data)

View file

@ -16,38 +16,51 @@
# 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,
resize_keyboard=None,
one_time_keyboard=None,
selective=None):
**kwargs):
# Required
self.keyboard = keyboard
self.resize_keyboard = resize_keyboard
self.one_time_keyboard = one_time_keyboard
self.selective = selective
# 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):
return ReplyKeyboardMarkup(keyboard=data.get('keyboard', None),
resize_keyboard=data.get(
'resize_keyboard', None
),
one_time_keyboard=data.get(
'one_time_keyboard', None
),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'keyboard': self.keyboard}
if self.resize_keyboard:
data['resize_keyboard'] = self.resize_keyboard
if self.one_time_keyboard:
data['one_time_keyboard'] = self.one_time_keyboard
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ReplyKeyboardMarkup:
"""
if not data:
return None
return ReplyKeyboardMarkup(**data)

View file

@ -16,9 +16,17 @@
# 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):
pass
"""Base class for Telegram ReplyMarkup Objects"""
@staticmethod
def de_json(data):
pass
def to_dict(self):
pass

View file

@ -16,42 +16,55 @@
# 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 TelegramObject
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,
thumb=None,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.width = width
self.height = height
self.thumb = thumb
self.file_size = file_size
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):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Sticker(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
thumb=thumb,
file_size=data.get('file_size', None))
Returns:
telegram.Sticker:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height,
'thumb': self.thumb.to_dict()}
if self.file_size:
data['file_size'] = self.file_size
return data
return Sticker(**data)

View file

@ -16,30 +16,45 @@
# 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 TelegramObject
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,
message=None):
**kwargs):
# Required
self.update_id = update_id
self.message = message
# Optionals
self.message = kwargs.get('message')
@staticmethod
def de_json(data):
if 'message' in data:
from telegram import Message
message = Message.de_json(data['message'])
else:
message = None
"""
Args:
data (str):
return Update(update_id=data.get('update_id', None),
message=message)
Returns:
telegram.Update:
"""
if not data:
return None
def to_dict(self):
data = {'update_id': self.update_id}
if self.message:
data['message'] = self.message.to_dict()
return data
data['message'] = Message.de_json(data['message'])
return Update(**data)

View file

@ -1,4 +1,5 @@
#!/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 <leandrotoeldodesouza@gmail.com>
@ -16,23 +17,44 @@
# 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 Sticker.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
Args:
id (int):
first_name (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
last_name (Optional[str]):
username (Optional[str]):
"""
def __init__(self,
id,
first_name,
last_name=None,
username=None):
self.id = id
**kwargs):
# Required
self.id = int(id)
self.first_name = first_name
self.last_name = last_name
self.username = username
# Optionals
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:
@ -41,16 +63,14 @@ class User(TelegramObject):
@staticmethod
def de_json(data):
return User(id=data.get('id', None),
first_name=data.get('first_name', None),
last_name=data.get('last_name', None),
username=data.get('username', None))
"""
Args:
data (str):
def to_dict(self):
data = {'id': self.id,
'first_name': self.first_name}
if self.last_name:
data['last_name'] = self.last_name
if self.username:
data['username'] = self.username
return data
Returns:
telegram.User:
"""
if not data:
return None
return User(**data)

View file

@ -16,36 +16,56 @@
# 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 TelegramObject
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):
self.total_count = total_count
# Required
self.total_count = int(total_count)
self.photos = photos
@staticmethod
def de_json(data):
if 'photos' in data:
from telegram import PhotoSize
photos = []
for photo in data['photos']:
photos.append([PhotoSize.de_json(x) for x in photo])
else:
photos = None
"""
Args:
data (str):
return UserProfilePhotos(total_count=data.get('total_count', None),
photos=photos)
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):
data = {}
if self.total_count:
data['total_count'] = self.total_count
if self.photos:
data['photos'] = []
for photo in self.photos:
data['photos'].append([x.to_dict() for x in photo])
"""
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

74
telegram/utils/botan.py Normal file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
#
# 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/].
import json
try:
from urllib.parse import urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError, URLError
except ImportError:
from urllib import urlencode
from urllib2 import urlopen, Request
from urllib2 import HTTPError, URLError
DEFAULT_BASE_URL = \
'https://api.botan.io/track?token=%(token)&uid=%(uid)&name=%(name)'
USER_AGENT = 'Python Telegram Bot' \
' (https://github.com/leandrotoledo/python-telegram-bot)'
CONTENT_TYPE = 'application/json'
class Botan(Object):
def __init__(self,
token,
base_url=None):
self.token = token
if base_url is None:
self.base_url = DEFAULT_BASE_URL % {'token': self.token}
else:
self.base_url = base_url % {'token': self.token}
def track(self,
uid,
text,
name = 'Message'):
url = self.base_url % {'uid': uid,
'message': text,
'name': name}
self._post(url, message)
def _post(self,
url,
data):
headers = {'User-agent': USER_AGENT,
'Content-type': CONTENT_TYPE}
try:
request = Request(
url,
data=urlencode(data).encode(),
headers=headers
)
return urlopen(request).read()
except IOError as e:
raise TelegramError(str(e))
except HTTPError as e:
raise TelegramError(str(e))

View file

@ -16,52 +16,62 @@
# 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 TelegramObject
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,
thumb=None,
mime_type=None,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.width = width
self.height = height
self.duration = duration
self.thumb = thumb
self.mime_type = mime_type
self.file_size = file_size
self.width = int(width)
self.height = int(height)
self.duration = int(duration)
# Optionals
self.thumb = kwargs.get('thumb')
self.mime_type = kwargs.get('mime_type', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Video(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
duration=data.get('duration', None),
thumb=thumb,
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
Returns:
telegram.Video:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height,
'duration': self.duration}
if self.thumb:
data['thumb'] = self.thumb.to_dict()
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
return Video(**data)

View file

@ -16,34 +16,50 @@
# 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,
duration=None,
mime_type=None,
file_size=None):
**kwargs):
# Required
self.file_id = file_id
self.duration = duration
self.mime_type = mime_type
self.file_size = file_size
# Optionals
self.duration = int(kwargs.get('duration', 0))
self.mime_type = kwargs.get('mime_type', '')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
return Voice(file_id=data.get('file_id', None),
duration=data.get('duration', None),
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
"""
Args:
data (str):
def to_dict(self):
data = {'file_id': self.file_id}
if self.duration:
data['duration'] = self.duration
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
Returns:
telegram.Voice:
"""
if not data:
return None
return Voice(**data)

View file

@ -1,30 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
#
# 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/].
import logging
import unittest
from tests.test_bot import BotTest
if __name__ == '__main__':
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
testsuite = unittest.TestLoader().loadTestsFromTestCase(BotTest)
unittest.TextTestRunner(verbosity=1).run(testsuite)

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -19,6 +19,8 @@
import os
import sys
sys.path.append('.')
import json
import telegram
import unittest
@ -36,195 +38,225 @@ class BotTest(unittest.TestCase):
def setUp(self):
bot = telegram.Bot(token=os.environ.get('TOKEN'))
chat_id = os.environ.get('CHAT_ID')
self._bot = bot
self._chat_id = chat_id
print('Testing the Bot API class')
def testGetMe(self):
'''Test the telegram.Bot getMe method'''
print('Testing getMe')
bot = self._bot.getMe()
self.assertTrue(self.is_json(bot.to_json()))
self.assertEqual(120405045, bot.id)
self.assertEqual('Toledo\'s Palace Bot', bot.first_name)
self.assertEqual(None, bot.last_name)
self.assertEqual('ToledosPalaceBot', bot.username)
self.assertEqual(bot.id, 120405045)
self.assertEqual(bot.first_name, 'Toledo\'s Palace Bot')
self.assertEqual(bot.last_name, '')
self.assertEqual(bot.username, 'ToledosPalaceBot')
self.assertEqual(bot.name, '@ToledosPalaceBot')
def testSendMessage(self):
'''Test the telegram.Bot sendMessage method'''
print('Testing sendMessage')
message = self._bot.sendMessage(chat_id=12173560,
message = self._bot.sendMessage(chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей')
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(u'Моё судно на воздушной подушке полно угрей', message.text)
self.assertIsInstance(message.date, datetime)
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
def testGetUpdates(self):
'''Test the telegram.Bot getUpdates method'''
print('Testing getUpdates')
updates = self._bot.getUpdates()
self.assertTrue(self.is_json(updates[0].to_json()))
self.assertIsInstance(updates[0], telegram.Update)
if updates:
self.assertTrue(self.is_json(updates[0].to_json()))
self.assertTrue(isinstance(updates[0], telegram.Update))
def testForwardMessage(self):
'''Test the telegram.Bot forwardMessage method'''
print('Testing forwardMessage')
message = self._bot.forwardMessage(chat_id=12173560,
from_chat_id=12173560,
message = self._bot.forwardMessage(chat_id=self._chat_id,
from_chat_id=self._chat_id,
message_id=138)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual('Oi', message.text)
self.assertEqual('leandrotoledo', message.forward_from.username)
self.assertIsInstance(message.forward_date, datetime)
self.assertEqual(message.text, 'Oi')
self.assertEqual(message.forward_from.username, 'leandrotoledo')
self.assertTrue(isinstance(message.forward_date, datetime))
def testSendPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing sendPhoto - File')
message = self._bot.sendPhoto(photo=open('tests/telegram.png', 'rb'),
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(1451, message.photo[0].file_size)
self.assertEqual('testSendPhoto', message.caption)
self.assertEqual(message.photo[0].file_size, 1451)
self.assertEqual(message.caption, 'testSendPhoto')
def testResendPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing sendPhoto - Resend')
message = self._bot.sendPhoto(photo='AgADAQADr6cxGzU8LQe6q0dMJD2rHYkP2ykABFymiQqJgjxRGGMAAgI',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual('AgADAQADr6cxGzU8LQe6q0dMJD2rHYkP2ykABFymiQqJgjxRGGMAAgI', message.photo[0].file_id)
self.assertEqual(message.photo[0].file_id, 'AgADAQADr6cxGzU8LQe6q0dMJD2rHYkP2ykABFymiQqJgjxRGGMAAgI')
def testSendJPGURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendJPGURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(822, message.photo[0].file_size)
self.assertEqual(message.photo[0].file_size, 822)
def testSendPNGURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendPNGURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.png&text=telegram',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(684, message.photo[0].file_size)
self.assertEqual(message.photo[0].file_size, 684)
def testSendGIFURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing testSendGIFURLPhoto - URL')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(684, message.photo[0].file_size)
self.assertEqual(message.photo[0].file_size, 684)
def testSendAudio(self):
'''Test the telegram.Bot sendAudio method'''
print('Testing sendAudio - File')
message = self._bot.sendAudio(audio=open('tests/telegram.mp3', 'rb'),
chat_id=12173560,
message = self._bot.sendAudio(audio=open('tests/data/telegram.mp3', 'rb'),
chat_id=self._chat_id,
performer='Leandro Toledo',
title='Teste')
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(28232, message.audio.file_size)
self.assertEqual('Leandro Toledo', message.audio.performer)
self.assertEqual('Teste', message.audio.title)
self.assertEqual(message.audio.file_size, 28232)
self.assertEqual(message.audio.performer, 'Leandro Toledo')
self.assertEqual(message.audio.title, 'Teste')
def testResendAudio(self):
'''Test the telegram.Bot sendAudio method'''
print('Testing sendAudio - Resend')
message = self._bot.sendAudio(audio='BQADAQADwwcAAjU8LQdBRsl3_qD2TAI',
chat_id=12173560,
chat_id=self._chat_id,
performer='Leandro Toledo', # Bug #39
title='Teste') # Bug #39
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual('BQADAQADwwcAAjU8LQdBRsl3_qD2TAI', message.audio.file_id)
self.assertEqual(message.audio.file_id, 'BQADAQADwwcAAjU8LQdBRsl3_qD2TAI')
def testSendVoice(self):
'''Test the telegram.Bot sendVoice method'''
print('Testing sendVoice - File')
message = self._bot.sendVoice(voice=open('tests/telegram.ogg', 'rb'),
chat_id=12173560)
message = self._bot.sendVoice(voice=open('tests/data/telegram.ogg', 'rb'),
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(9199, message.voice.file_size)
self.assertEqual(message.voice.file_size, 9199)
def testResendVoice(self):
'''Test the telegram.Bot sendVoice method'''
print('Testing sendVoice - Resend')
message = self._bot.sendVoice(voice='AwADAQADIQEAAvjAuQABSAXg_GhkhZcC',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual('AwADAQADIQEAAvjAuQABSAXg_GhkhZcC', message.voice.file_id)
self.assertEqual(message.voice.file_id, 'AwADAQADIQEAAvjAuQABSAXg_GhkhZcC')
def testSendDocument(self):
'''Test the telegram.Bot sendDocument method'''
print('Testing sendDocument - File')
message = self._bot.sendDocument(document=open('tests/telegram.png', 'rb'),
chat_id=12173560)
message = self._bot.sendDocument(document=open('tests/data/telegram.png', 'rb'),
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(12948, message.document.file_size)
self.assertEqual(message.document.file_size, 12948)
def testSendGIFURLDocument(self):
'''Test the telegram.Bot sendDocument method'''
print('Testing sendDocument - File')
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(684, message.photo[0].file_size)
self.assertEqual(message.photo[0].file_size, 684)
def testResendDocument(self):
'''Test the telegram.Bot sendDocument method'''
print('Testing sendDocument - Resend')
message = self._bot.sendDocument(document='BQADAQADHAADNTwtBxZxUGKyxYbYAg',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual('BQADAQADHAADNTwtBxZxUGKyxYbYAg', message.document.file_id)
self.assertEqual(message.document.file_id, 'BQADAQADHAADNTwtBxZxUGKyxYbYAg')
def testSendVideo(self):
'''Test the telegram.Bot sendVideo method'''
print('Testing sendVideo - File')
message = self._bot.sendVideo(video=open('tests/telegram.mp4', 'rb'),
message = self._bot.sendVideo(video=open('tests/data/telegram.mp4', 'rb'),
caption='testSendVideo',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(326534, message.video.file_size)
self.assertEqual('testSendVideo', message.caption)
self.assertEqual(message.video.file_size, 326534)
self.assertEqual(message.caption, 'testSendVideo')
def testResendVideo(self):
'''Test the telegram.Bot sendVideo method'''
print('Testing sendVideo - Resend')
message = self._bot.sendVideo(video='BAADAQADIgEAAvjAuQABOuTB937fPTgC',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(4, message.video.duration)
self.assertEqual(message.video.duration, 4)
def testResendSticker(self):
'''Test the telegram.Bot sendSticker method'''
print('Testing sendSticker - Resend')
message = self._bot.sendSticker(sticker='BQADAQADHAADyIsGAAFZfq1bphjqlgI',
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(39518, message.sticker.file_size)
self.assertEqual(message.sticker.file_size, 39518)
def testSendLocation(self):
'''Test the telegram.Bot sendLocation method'''
print('Testing sendLocation')
message = self._bot.sendLocation(latitude=-23.558873,
longitude=-46.659732,
chat_id=12173560)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(-23.558873, message.location.latitude)
self.assertEqual(-46.659732, message.location.longitude)
self.assertEqual(message.location.latitude, -23.558873)
self.assertEqual(message.location.longitude, -46.659732)
def testSendChatAction(self):
'''Test the telegram.Bot sendChatAction method'''
print('Testing sendChatAction - ChatAction.TYPING')
self._bot.sendChatAction(action=telegram.ChatAction.TYPING,
chat_id=12173560)
chat_id=self._chat_id)
def testGetUserProfilePhotos(self):
'''Test the telegram.Bot getUserProfilePhotos method'''
print('Testing getUserProfilePhotos')
upf = self._bot.getUserProfilePhotos(user_id=12173560)
upf = self._bot.getUserProfilePhotos(user_id=self._chat_id)
self.assertTrue(self.is_json(upf.to_json()))
self.assertEqual(6547, upf.photos[0][0].file_size)
self.assertEqual(upf.photos[0][0].file_size, 6547)
unittest.main()

View file

@ -0,0 +1,75 @@
import sys
sys.path.append('.')
import telegram
import unittest
import unittest.mock
from telegram.command_handler import CommandHandler, CommandHandlerWithHelp
class CommandHandlerTmp(CommandHandler):
def __init__(self, *args, **kwargs):
super(CommandHandlerTmp, self).__init__(*args, **kwargs)
self.output = None
def command_test(self, update):
self.output = 1
class CommandHandlerTmp2(CommandHandlerWithHelp):
def __init__(self, *args, **kwargs):
super(CommandHandlerTmp2, self).__init__(*args, **kwargs)
self.output_test = None
def command_test(self, update):
self.output_test = 1
def fake_getUpdates(*args, **kwargs):
from_user = telegram.User(id=42, first_name='hello')
message = telegram.Message(message_id=42, from_user=from_user, date=None, chat=from_user, text='/test')
update = telegram.Update(update_id=42, message=message)
return [update]
output_fsm = None
def fake_sendMessage(chat_id, message, *args, **kwargs):
global output_fsm
output_fsm = (chat_id, message)
return telegram.Message(43, 123, 000000, telegram.User(chat_id, 'test'), text=message)
class CommandHandlerTest(unittest.TestCase):
def setUp(self):
self.bot = unittest.mock.MagicMock()
self.bot.getUpdates = fake_getUpdates
self.bot.sendMessage = fake_sendMessage
def test_get_command_func(self):
CH = CommandHandlerTmp(self.bot)
self.assertEqual(CH.command_test, CH._get_command_func('test'))
self.assertEqual(CH.command_test, CH._get_command_func('/test'))
self.assertEqual(None, CH._get_command_func('this function does not exsist'))
def test_run_once(self):
CH = CommandHandlerTmp(self.bot)
self.assertEqual(CH.output, None)
threads, last_update = CH.run_once(make_thread=True)
for t in threads:
t.start()
for t in threads:
t.join()
self.assertEqual(CH.output, 1)
def test_run(self):
pass # TODO implement test
def test__command_not_found(self):
CH = CommandHandlerTmp(self.bot)
CH._command_not_found(self.bot.getUpdates()[0])
self.assertEqual(output_fsm, (42, "Sorry, I didn't understand the command: /test."))
if __name__ == '__main__':
import sys
unittest.main(sys.argv)