mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-22 15:17:00 +01:00
commit
e693922fe8
42 changed files with 1509 additions and 647 deletions
|
@ -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
|
||||
|
|
|
@ -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>`_
|
||||
|
|
19
Makefile
19
Makefile
|
@ -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"
|
||||
|
|
89
examples/command_handler_example.py
Normal file
89
examples/command_handler_example.py
Normal 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()
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
117
telegram/bot.py
117
telegram/bot.py
|
@ -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}
|
||||
|
|
|
@ -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
174
telegram/command_handler.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
83
telegram/enchancedbot.py
Normal 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)
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
74
telegram/utils/botan.py
Normal 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))
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
@ -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()
|
||||
|
|
75
tests/test_command_handler.py
Normal file
75
tests/test_command_handler.py
Normal 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)
|
Loading…
Reference in a new issue