Added Telegram integration
This commit is contained in:
parent
5948784677
commit
753694a865
12 changed files with 1229 additions and 28 deletions
|
@ -231,6 +231,8 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
||||||
'PyOBEX',
|
'PyOBEX',
|
||||||
'todoist',
|
'todoist',
|
||||||
'trello',
|
'trello',
|
||||||
|
'telegram',
|
||||||
|
'telegram.ext',
|
||||||
]
|
]
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
|
0
platypush/backend/chat/__init__.py
Normal file
0
platypush/backend/chat/__init__.py
Normal file
69
platypush/backend/chat/telegram.py
Normal file
69
platypush/backend/chat/telegram.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from platypush.backend import Backend
|
||||||
|
from platypush.context import get_plugin
|
||||||
|
from platypush.message.event.chat.telegram import NewMessageEvent, NewCommandMessageEvent
|
||||||
|
from platypush.plugins.chat.telegram import ChatTelegramPlugin
|
||||||
|
|
||||||
|
|
||||||
|
class ChatTelegramBackend(Backend):
|
||||||
|
"""
|
||||||
|
Telegram bot that listens for messages and updates.
|
||||||
|
|
||||||
|
Triggers:
|
||||||
|
|
||||||
|
* :class:`platypush.message.event.chat.telegram.NewMessageEvent` when a new message is received.
|
||||||
|
* :class:`platypush.message.event.chat.telegram.NewCommandMessageEvent` when a new command message is received.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* The :class:`platypush.plugins.chat.telegram.ChatTelegramPlugin` plugin configured
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, commands: Optional[List[str]] = None, **kwargs):
|
||||||
|
"""
|
||||||
|
:param commands: Optional list of commands to be registered on the bot (e.g. 'start', 'stop', 'help' etc.).
|
||||||
|
When you send e.g. '/start' to the bot conversation then a
|
||||||
|
:class:`platypush.message.event.chat.telegram.NewCommandMessageEvent` will be triggered instead of a
|
||||||
|
:class:`platypush.message.event.chat.telegram.NewMessageEvent` event.
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.commands = commands or []
|
||||||
|
self._plugin: ChatTelegramPlugin = get_plugin('chat.telegram')
|
||||||
|
|
||||||
|
def _msg_hook(self):
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
def hook(update, context):
|
||||||
|
self.bus.post(NewMessageEvent(chat_id=update.effective_chat.id,
|
||||||
|
message=self._plugin.parse_msg(update.effective_message).output,
|
||||||
|
user=self._plugin.parse_user(update.effective_user).output))
|
||||||
|
return hook
|
||||||
|
|
||||||
|
def _command_hook(self, cmd):
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
def hook(update, context):
|
||||||
|
self.bus.post(NewCommandMessageEvent(command=cmd,
|
||||||
|
chat_id=update.effective_chat.id,
|
||||||
|
message=self._plugin.parse_msg(update.effective_message).output,
|
||||||
|
user=self._plugin.parse_user(update.effective_user).output))
|
||||||
|
|
||||||
|
return hook
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# noinspection PyPackageRequirements
|
||||||
|
from telegram.ext import CommandHandler, MessageHandler, Filters
|
||||||
|
|
||||||
|
super().run()
|
||||||
|
telegram = self._plugin.get_telegram()
|
||||||
|
dispatcher = telegram.dispatcher
|
||||||
|
dispatcher.add_handler(MessageHandler(Filters.text, self._msg_hook()))
|
||||||
|
|
||||||
|
for cmd in self.commands:
|
||||||
|
dispatcher.add_handler(CommandHandler(cmd, self._command_hook(cmd)))
|
||||||
|
|
||||||
|
self.logger.info('Initialized Telegram backend')
|
||||||
|
telegram.start_polling()
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
0
platypush/message/event/chat/__init__.py
Normal file
0
platypush/message/event/chat/__init__.py
Normal file
25
platypush/message/event/chat/telegram.py
Normal file
25
platypush/message/event/chat/telegram.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from platypush.message.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramEvent(Event):
|
||||||
|
def __init__(self, *args, chat_id: int, **kwargs):
|
||||||
|
super().__init__(*args, chat_id=chat_id, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class NewMessageEvent(TelegramEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a new message is received by the Telegram bot.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, message, user, **kwargs):
|
||||||
|
super().__init__(*args, message=message, user=user, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class NewCommandMessageEvent(NewMessageEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a new message is received by the Telegram bot.
|
||||||
|
"""
|
||||||
|
def __init__(self, command: str, *args, **kwargs):
|
||||||
|
super().__init__(*args, command=command, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
0
platypush/message/response/chat/__init__.py
Normal file
0
platypush/message/response/chat/__init__.py
Normal file
164
platypush/message/response/chat/telegram.py
Normal file
164
platypush/message/response/chat/telegram.py
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from platypush.message.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramMessageResponse(Response):
|
||||||
|
def __init__(self,
|
||||||
|
message_id: int,
|
||||||
|
chat_id: int,
|
||||||
|
creation_date: Optional[datetime.datetime],
|
||||||
|
chat_username: Optional[str] = None,
|
||||||
|
chat_firstname: Optional[str] = None,
|
||||||
|
chat_lastname: Optional[str] = None,
|
||||||
|
from_user_id: Optional[int] = None,
|
||||||
|
from_username: Optional[str] = None,
|
||||||
|
from_firstname: Optional[str] = None,
|
||||||
|
from_lastname: Optional[str] = None,
|
||||||
|
text: Optional[str] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
edit_date: Optional[datetime.datetime] = None,
|
||||||
|
forward_date: Optional[datetime.datetime] = None,
|
||||||
|
forward_from_message_id: Optional[int] = None,
|
||||||
|
photo_file_id: Optional[str] = None,
|
||||||
|
photo_file_size: Optional[int] = None,
|
||||||
|
photo_width: Optional[int] = None,
|
||||||
|
photo_height: Optional[int] = None,
|
||||||
|
document_file_id: Optional[str] = None,
|
||||||
|
document_file_name: Optional[str] = None,
|
||||||
|
document_file_size: Optional[str] = None,
|
||||||
|
document_mime_type: Optional[str] = None,
|
||||||
|
audio_file_id: Optional[str] = None,
|
||||||
|
audio_file_size: Optional[str] = None,
|
||||||
|
audio_mime_type: Optional[str] = None,
|
||||||
|
audio_performer: Optional[str] = None,
|
||||||
|
audio_title: Optional[str] = None,
|
||||||
|
audio_duration: Optional[str] = None,
|
||||||
|
location_latitude: Optional[float] = None,
|
||||||
|
location_longitude: Optional[float] = None,
|
||||||
|
contact_phone_number: Optional[str] = None,
|
||||||
|
contact_first_name: Optional[str] = None,
|
||||||
|
contact_last_name: Optional[str] = None,
|
||||||
|
contact_user_id: Optional[int] = None,
|
||||||
|
contact_vcard: Optional[str] = None,
|
||||||
|
video_file_id: Optional[str] = None,
|
||||||
|
video_file_size: Optional[int] = None,
|
||||||
|
video_width: Optional[int] = None,
|
||||||
|
video_height: Optional[int] = None,
|
||||||
|
video_mime_type: Optional[str] = None,
|
||||||
|
video_duration: Optional[str] = None,
|
||||||
|
link: Optional[str] = None,
|
||||||
|
media_group_id: Optional[int] = None,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, output={
|
||||||
|
'message_id': message_id,
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'chat_username': chat_username,
|
||||||
|
'chat_firstname': chat_firstname,
|
||||||
|
'chat_lastname': chat_lastname,
|
||||||
|
'from_user_id': from_user_id,
|
||||||
|
'from_username': from_username,
|
||||||
|
'from_firstname': from_firstname,
|
||||||
|
'from_lastname': from_lastname,
|
||||||
|
'text': text,
|
||||||
|
'caption': caption,
|
||||||
|
'creation_date': creation_date,
|
||||||
|
'edit_date': edit_date,
|
||||||
|
'forward_from_message_id': forward_from_message_id,
|
||||||
|
'forward_date': forward_date,
|
||||||
|
'photo_file_id': photo_file_id,
|
||||||
|
'photo_file_size': photo_file_size,
|
||||||
|
'photo_width': photo_width,
|
||||||
|
'photo_height': photo_height,
|
||||||
|
'document_file_id': document_file_id,
|
||||||
|
'document_file_name': document_file_name,
|
||||||
|
'document_file_size': document_file_size,
|
||||||
|
'document_mime_type': document_mime_type,
|
||||||
|
'audio_file_id': audio_file_id,
|
||||||
|
'audio_file_size': audio_file_size,
|
||||||
|
'audio_performer': audio_performer,
|
||||||
|
'audio_title': audio_title,
|
||||||
|
'audio_duration': audio_duration,
|
||||||
|
'audio_mime_type': audio_mime_type,
|
||||||
|
'video_file_id': video_file_id,
|
||||||
|
'video_file_size': video_file_size,
|
||||||
|
'video_width': video_width,
|
||||||
|
'video_height': video_height,
|
||||||
|
'video_duration': video_duration,
|
||||||
|
'video_mime_type': video_mime_type,
|
||||||
|
'link': link,
|
||||||
|
'location_latitude': location_latitude,
|
||||||
|
'location_longitude': location_longitude,
|
||||||
|
'contact_phone_number': contact_phone_number,
|
||||||
|
'contact_first_name': contact_first_name,
|
||||||
|
'contact_last_name': contact_last_name,
|
||||||
|
'contact_user_id': contact_user_id,
|
||||||
|
'contact_vcard': contact_vcard,
|
||||||
|
'media_group_id': media_group_id,
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramFileResponse(Response):
|
||||||
|
def __init__(self,
|
||||||
|
file_id: str,
|
||||||
|
file_path: str,
|
||||||
|
file_size: int,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, output={
|
||||||
|
'file_id': file_id,
|
||||||
|
'file_path': file_path,
|
||||||
|
'file_size': file_size,
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramChatResponse(Response):
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
|
def __init__(self,
|
||||||
|
chat_id: int,
|
||||||
|
link: str,
|
||||||
|
username: str,
|
||||||
|
invite_link: Optional[str],
|
||||||
|
title: Optional[str] = None,
|
||||||
|
description: Optional[str] = None,
|
||||||
|
type: Optional[str] = None,
|
||||||
|
first_name: Optional[str] = None,
|
||||||
|
last_name: Optional[str] = None,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, output={
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'link': link,
|
||||||
|
'invite_link': invite_link,
|
||||||
|
'username': username,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'type': type,
|
||||||
|
'first_name': first_name,
|
||||||
|
'last_name': last_name,
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramUserResponse(Response):
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
|
def __init__(self,
|
||||||
|
user_id: int,
|
||||||
|
username: str,
|
||||||
|
is_bot: bool,
|
||||||
|
first_name: str,
|
||||||
|
last_name: Optional[str] = None,
|
||||||
|
language_code: Optional[str] = None,
|
||||||
|
link: Optional[str] = None,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, output={
|
||||||
|
'user_id': user_id,
|
||||||
|
'username': username,
|
||||||
|
'is_bot': is_bot,
|
||||||
|
'link': link,
|
||||||
|
'language_code': language_code,
|
||||||
|
'first_name': first_name,
|
||||||
|
'last_name': last_name,
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
0
platypush/plugins/chat/__init__.py
Normal file
0
platypush/plugins/chat/__init__.py
Normal file
914
platypush/plugins/chat/telegram.py
Normal file
914
platypush/plugins/chat/telegram.py
Normal file
|
@ -0,0 +1,914 @@
|
||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
|
from threading import RLock
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
# noinspection PyPackageRequirements
|
||||||
|
from telegram.ext import Updater
|
||||||
|
# noinspection PyPackageRequirements
|
||||||
|
from telegram.message import Message as TelegramMessage
|
||||||
|
# noinspection PyPackageRequirements
|
||||||
|
from telegram.user import User as TelegramUser
|
||||||
|
|
||||||
|
from platypush.message.response.chat.telegram import TelegramMessageResponse, TelegramFileResponse, \
|
||||||
|
TelegramChatResponse, TelegramUserResponse
|
||||||
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
class Resource:
|
||||||
|
def __init__(self, file_id: Optional[int] = None, url: Optional[str] = None, path: Optional[str] = None):
|
||||||
|
assert file_id or url or path, 'You need to specify either file_id, url or path'
|
||||||
|
self.file_id = file_id
|
||||||
|
self.url = url
|
||||||
|
self.path = path
|
||||||
|
self._file = None
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if self.path:
|
||||||
|
self._file = open(os.path.abspath(os.path.expanduser(self.path)), 'rb')
|
||||||
|
return self._file
|
||||||
|
|
||||||
|
return self.file_id or self.url
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if self._file:
|
||||||
|
self._file.close()
|
||||||
|
|
||||||
|
|
||||||
|
class ChatTelegramPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin to programmatically send Telegram messages through a Telegram bot. In order to send messages to contacts,
|
||||||
|
groups or channels you'll first need to register a bot. To do so:
|
||||||
|
|
||||||
|
1. Open a Telegram conversation with the `@BotFather <https://telegram.me/BotFather>`_.
|
||||||
|
2. Send ``/start`` followed by ``/newbot``. Choose a display name and a username for your bot.
|
||||||
|
3. Copy the provided API token in the configuration of this plugin.
|
||||||
|
4. Open a conversation with your newly created bot.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **python-telegram-bot** (``pip install python-telegram-bot``)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, api_token: str, **kwargs):
|
||||||
|
"""
|
||||||
|
:param api_token: API token as returned by the `@BotFather <https://telegram.me/BotFather>`_
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
self._api_token = api_token
|
||||||
|
self._telegram_lock = RLock()
|
||||||
|
self._telegram: Optional[Updater] = None
|
||||||
|
|
||||||
|
def get_telegram(self) -> Updater:
|
||||||
|
with self._telegram_lock:
|
||||||
|
if self._telegram:
|
||||||
|
return self._telegram
|
||||||
|
|
||||||
|
self._telegram = Updater(self._api_token, use_context=True)
|
||||||
|
return self._telegram
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_msg(msg: TelegramMessage) -> TelegramMessageResponse:
|
||||||
|
return TelegramMessageResponse(
|
||||||
|
message_id=msg.message_id,
|
||||||
|
chat_id=msg.chat_id,
|
||||||
|
chat_username=msg.chat.username,
|
||||||
|
chat_firstname=msg.chat.first_name,
|
||||||
|
chat_lastname=msg.chat.last_name,
|
||||||
|
from_user_id=msg.from_user.id if msg.from_user else None,
|
||||||
|
from_username=msg.from_user.username if msg.from_user else None,
|
||||||
|
from_firstname=msg.from_user.first_name if msg.from_user else None,
|
||||||
|
from_lastname=msg.from_user.last_name if msg.from_user else None,
|
||||||
|
text=msg.text,
|
||||||
|
caption=msg.caption,
|
||||||
|
creation_date=msg.date,
|
||||||
|
edit_date=msg.edit_date,
|
||||||
|
forward_date=msg.forward_date,
|
||||||
|
forward_from_message_id=msg.forward_from_message_id,
|
||||||
|
photo_file_id=msg.photo[0].file_id if msg.photo else None,
|
||||||
|
photo_file_size=msg.photo[0].file_size if msg.photo else None,
|
||||||
|
photo_width=msg.photo[0].width if msg.photo else None,
|
||||||
|
photo_height=msg.photo[0].height if msg.photo else None,
|
||||||
|
document_file_id=msg.document.file_id if msg.document else None,
|
||||||
|
document_file_name=msg.document.file_name if msg.document else None,
|
||||||
|
document_file_size=msg.document.file_size if msg.document else None,
|
||||||
|
document_mime_type=msg.document.mime_type if msg.document else None,
|
||||||
|
audio_file_id=msg.audio.file_id if msg.audio else None,
|
||||||
|
audio_file_size=msg.audio.file_size if msg.audio else None,
|
||||||
|
audio_performer=msg.audio.performer if msg.audio else None,
|
||||||
|
audio_title=msg.audio.title if msg.audio else None,
|
||||||
|
audio_duration=msg.audio.duration if msg.audio else None,
|
||||||
|
audio_mime_type=msg.audio.mime_type if msg.audio else None,
|
||||||
|
video_file_id=msg.video.file_id if msg.video else None,
|
||||||
|
video_file_size=msg.video.file_size if msg.video else None,
|
||||||
|
video_duration=msg.video.duration if msg.video else None,
|
||||||
|
video_width=msg.video.width if msg.video else None,
|
||||||
|
video_height=msg.video.height if msg.video else None,
|
||||||
|
video_mime_type=msg.video.mime_type if msg.video else None,
|
||||||
|
location_latitude=msg.location.latitude if msg.location else None,
|
||||||
|
location_longitude=msg.location.longitude if msg.location else None,
|
||||||
|
contact_phone_number=msg.contact.phone_number if msg.contact else None,
|
||||||
|
contact_first_name=msg.contact.first_name if msg.contact else None,
|
||||||
|
contact_last_name=msg.contact.last_name if msg.contact else None,
|
||||||
|
contact_user_id=msg.contact.user_id if msg.contact else None,
|
||||||
|
contact_vcard=msg.contact.vcard if msg.contact else None,
|
||||||
|
link=msg.link,
|
||||||
|
media_group_id=msg.media_group_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_user(user: TelegramUser) -> TelegramUserResponse:
|
||||||
|
return TelegramUserResponse(
|
||||||
|
user_id=user.id,
|
||||||
|
username=user.username,
|
||||||
|
is_bot=user.is_bot,
|
||||||
|
first_name=user.first_name,
|
||||||
|
last_name=user.last_name,
|
||||||
|
language_code=user.language_code,
|
||||||
|
link=user.link
|
||||||
|
)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_message(self, chat_id: Union[str, int], text: str, parse_mode: Optional[str] = None,
|
||||||
|
disable_web_page_preview: bool = False, disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a message to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param text: Text to be sent.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_web_page_preview: If True then web previews for URLs will be disabled.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
msg = telegram.bot.send_message(chat_id=chat_id,
|
||||||
|
text=text,
|
||||||
|
parse_mode=parse_mode,
|
||||||
|
disable_web_page_preview=disable_web_page_preview,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_photo(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a picture to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_photo(chat_id=chat_id,
|
||||||
|
photo=resource,
|
||||||
|
caption=caption,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout, parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_audio(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
performer: Optional[str] = None,
|
||||||
|
title: Optional[str] = None,
|
||||||
|
duration: Optional[float] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send audio to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param performer: Optional audio performer.
|
||||||
|
:param title: Optional audio title.
|
||||||
|
:param duration: Duration of the audio in seconds.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_audio(chat_id=chat_id,
|
||||||
|
audio=resource,
|
||||||
|
caption=caption,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
performer=performer,
|
||||||
|
title=title,
|
||||||
|
duration=duration,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout,
|
||||||
|
parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_document(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
filename: Optional[str] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a document to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param filename: Name of the file as it will be shown in Telegram.
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_document(chat_id=chat_id,
|
||||||
|
document=resource,
|
||||||
|
filename=filename,
|
||||||
|
caption=caption,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout,
|
||||||
|
parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_video(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
duration: Optional[int] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a video to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param duration: Duration in seconds.
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param width: Video width.
|
||||||
|
:param height: Video height.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_video(chat_id=chat_id,
|
||||||
|
video=resource,
|
||||||
|
duration=duration,
|
||||||
|
caption=caption,
|
||||||
|
width=width,
|
||||||
|
height=height,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout,
|
||||||
|
parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_animation(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
duration: Optional[int] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send an animation (GIF or H.264/MPEG-4 AVC video without sound) to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param duration: Duration in seconds.
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param width: Video width.
|
||||||
|
:param height: Video height.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_animation(chat_id=chat_id,
|
||||||
|
animation=resource,
|
||||||
|
duration=duration,
|
||||||
|
caption=caption,
|
||||||
|
width=width,
|
||||||
|
height=height,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout,
|
||||||
|
parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_voice(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
duration: Optional[float] = None,
|
||||||
|
parse_mode: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send audio to a chat as a voice file. For this to work, your audio must be in an .ogg file encoded with OPUS
|
||||||
|
(other formats may be sent as Audio or Document).
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param caption: Optional caption for the picture.
|
||||||
|
:param duration: Duration of the voice in seconds.
|
||||||
|
:param parse_mode: Set to 'Markdown' or 'HTML' to send either Markdown or HTML content.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_voice(chat_id=chat_id,
|
||||||
|
voice=resource,
|
||||||
|
caption=caption,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
duration=duration,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout, parse_mode=parse_mode)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_video_note(self, chat_id: Union[str, int],
|
||||||
|
file_id: Optional[int] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
path: Optional[str] = None,
|
||||||
|
duration: Optional[int] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a video note to a chat. As of v.4.0, Telegram clients support rounded square mp4 videos of up to
|
||||||
|
1 minute long.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param file_id: Set it if the file already exists on Telegram servers and has a file_id.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param url: Set it if you want to send a file from a remote URL.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param path: Set it if you want to send a file from the local filesystem.
|
||||||
|
Note that you'll have to specify either ``file_id``, ``url`` or ``path``.
|
||||||
|
|
||||||
|
:param duration: Duration in seconds.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(file_id=file_id, url=url, path=path) as resource:
|
||||||
|
msg = telegram.bot.send_video_note(chat_id=chat_id,
|
||||||
|
video=resource,
|
||||||
|
duration=duration,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_location(self, chat_id: Union[str, int],
|
||||||
|
latitude: float,
|
||||||
|
longitude: float,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a location to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param latitude: Latitude
|
||||||
|
:param longitude: Longitude
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
msg = telegram.bot.send_location(chat_id=chat_id,
|
||||||
|
latitude=latitude,
|
||||||
|
longitude=longitude,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_venue(self, chat_id: Union[str, int],
|
||||||
|
latitude: float,
|
||||||
|
longitude: float,
|
||||||
|
title: str,
|
||||||
|
address: str,
|
||||||
|
foursquare_id: Optional[str] = None,
|
||||||
|
foursquare_type: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send the address of a venue to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param latitude: Latitude
|
||||||
|
:param longitude: Longitude
|
||||||
|
:param title: Venue name.
|
||||||
|
:param address: Venue address.
|
||||||
|
:param foursquare_id: Foursquare ID.
|
||||||
|
:param foursquare_type: Foursquare type.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
msg = telegram.bot.send_venue(chat_id=chat_id,
|
||||||
|
latitude=latitude,
|
||||||
|
longitude=longitude,
|
||||||
|
title=title,
|
||||||
|
address=address,
|
||||||
|
foursquare_id=foursquare_id,
|
||||||
|
foursquare_type=foursquare_type,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send_contact(self, chat_id: Union[str, int],
|
||||||
|
phone_number: str,
|
||||||
|
first_name: str,
|
||||||
|
last_name: Optional[str] = None,
|
||||||
|
vcard: Optional[str] = None,
|
||||||
|
disable_notification: bool = False,
|
||||||
|
reply_to_message_id: Optional[int] = None,
|
||||||
|
timeout: int = 20) -> TelegramMessageResponse:
|
||||||
|
"""
|
||||||
|
Send a contact to a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param phone_number: Phone number.
|
||||||
|
:param first_name: First name.
|
||||||
|
:param last_name: Last name.
|
||||||
|
:param vcard: Additional contact info in vCard format (0-2048 bytes).
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param reply_to_message_id: If set then the message will be sent as a response to the specified message.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
msg = telegram.bot.send_contact(chat_id=chat_id,
|
||||||
|
phone_number=phone_number,
|
||||||
|
first_name=first_name,
|
||||||
|
last_name=last_name,
|
||||||
|
vcard=vcard,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
reply_to_message_id=reply_to_message_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
return self.parse_msg(msg)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def get_file(self, file_id: str, timeout: int = 20) -> TelegramFileResponse:
|
||||||
|
"""
|
||||||
|
Get the info and URL of an uploaded file by file_id.
|
||||||
|
|
||||||
|
:param file_id: File ID.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds).
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
file = telegram.bot.get_file(file_id, timeout=timeout)
|
||||||
|
return TelegramFileResponse(file_id=file.file_id, file_path=file.file_path, file_size=file.file_size)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def get_chat(self, chat_id: Union[int, str], timeout: int = 20) -> TelegramChatResponse:
|
||||||
|
"""
|
||||||
|
Get the info about a Telegram chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID.
|
||||||
|
:param timeout: Upload timeout (default: 20 seconds).
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
chat = telegram.bot.get_chat(chat_id, timeout=timeout)
|
||||||
|
return TelegramChatResponse(chat_id=chat.id,
|
||||||
|
link=chat.link,
|
||||||
|
username=chat.username,
|
||||||
|
invite_link=chat.invite_link,
|
||||||
|
title=chat.title,
|
||||||
|
description=chat.description,
|
||||||
|
type=chat.type,
|
||||||
|
first_name=chat.first_name,
|
||||||
|
last_name=chat.last_name)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def kick_chat_member(self, chat_id: Union[str, int],
|
||||||
|
user_id: int,
|
||||||
|
until_date: Optional[datetime.datetime] = None,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Kick a user from a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param user_id: Unique user ID.
|
||||||
|
:param until_date: End date for the ban.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.kick_chat_member(
|
||||||
|
chat_id=chat_id,
|
||||||
|
user_id=user_id,
|
||||||
|
until_date=until_date,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def unban_chat_member(self, chat_id: Union[str, int],
|
||||||
|
user_id: int,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Lift the ban from a chat member.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param user_id: Unique user ID.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.unban_chat_member(
|
||||||
|
chat_id=chat_id,
|
||||||
|
user_id=user_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def promote_chat_member(self, chat_id: Union[str, int],
|
||||||
|
user_id: int,
|
||||||
|
can_change_info: Optional[bool] = None,
|
||||||
|
can_post_messages: Optional[bool] = None,
|
||||||
|
can_edit_messages: Optional[bool] = None,
|
||||||
|
can_delete_messages: Optional[bool] = None,
|
||||||
|
can_invite_users: Optional[bool] = None,
|
||||||
|
can_restrict_members: Optional[bool] = None,
|
||||||
|
can_promote_members: Optional[bool] = None,
|
||||||
|
can_pin_messages: Optional[bool] = None,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Promote or demote a member.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param user_id: Unique user ID.
|
||||||
|
:param can_change_info: Pass True if the user can change channel info.
|
||||||
|
:param can_post_messages: Pass True if the user can post messages.
|
||||||
|
:param can_edit_messages: Pass True if the user can edit messages.
|
||||||
|
:param can_delete_messages: Pass True if the user can delete messages.
|
||||||
|
:param can_invite_users: Pass True if the user can invite other users to the channel/group.
|
||||||
|
:param can_restrict_members: Pass True if the user can restrict the permissions of other users.
|
||||||
|
:param can_promote_members: Pass True if the user can promote mebmers.
|
||||||
|
:param can_pin_messages: Pass True if the user can pin messages.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.promote_chat_member(
|
||||||
|
chat_id=chat_id,
|
||||||
|
user_id=user_id,
|
||||||
|
can_change_info=can_change_info,
|
||||||
|
can_post_messages=can_post_messages,
|
||||||
|
can_edit_messages=can_edit_messages,
|
||||||
|
can_delete_messages=can_delete_messages,
|
||||||
|
can_invite_users=can_invite_users,
|
||||||
|
can_restrict_members=can_restrict_members,
|
||||||
|
can_promote_members=can_promote_members,
|
||||||
|
can_pin_messages=can_pin_messages,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_chat_title(self, chat_id: Union[str, int],
|
||||||
|
title: str,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Set the title of a channel/group.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param title: New chat title.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.set_chat_title(
|
||||||
|
chat_id=chat_id,
|
||||||
|
description=title,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_chat_description(self, chat_id: Union[str, int],
|
||||||
|
description: str,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Set the description of a channel/group.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param description: New chat description.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.set_chat_description(
|
||||||
|
chat_id=chat_id,
|
||||||
|
description=description,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_chat_photo(self, chat_id: Union[str, int],
|
||||||
|
path: str,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Set the photo of a channel/group.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param path: Path of the new image.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
|
||||||
|
with Resource(path=path) as resource:
|
||||||
|
telegram.bot.set_chat_photo(
|
||||||
|
chat_id=chat_id,
|
||||||
|
photo=resource,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def delete_chat_photo(self, chat_id: Union[str, int],
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Delete the photo of a channel/group.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.delete_chat_photo(
|
||||||
|
chat_id=chat_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def pin_chat_message(self, chat_id: Union[str, int],
|
||||||
|
message_id: int,
|
||||||
|
disable_notification: Optional[bool] = None,
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Pin a message in a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param message_id: Message ID.
|
||||||
|
:param disable_notification: If True then no notification will be sent to the users.
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.pin_chat_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
message_id=message_id,
|
||||||
|
disable_notification=disable_notification,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def unpin_chat_message(self, chat_id: Union[str, int],
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Unpin the message of a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.unpin_chat_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def leave_chat(self, chat_id: Union[str, int],
|
||||||
|
timeout: int = 20):
|
||||||
|
"""
|
||||||
|
Leave a chat.
|
||||||
|
|
||||||
|
:param chat_id: Chat ID. Can be either a numerical ID or a unique identifier in the format ``@channelname``.
|
||||||
|
In order to get your own Telegram chat_id open a conversation with
|
||||||
|
`@IDBot <https://telegram.me/IDBot>`_ and type ``/start`` followed by ``/getid``. Similar procedures
|
||||||
|
also exist to get a group or channel chat_id - just Google for "Telegram get channel/group chat_id".
|
||||||
|
|
||||||
|
:param timeout: Request timeout (default: 20 seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
|
telegram = self.get_telegram()
|
||||||
|
telegram.bot.leave_chat(
|
||||||
|
chat_id=chat_id,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
|
@ -1,3 +1,4 @@
|
||||||
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
|
@ -167,4 +168,19 @@ class HttpRequestPlugin(Plugin):
|
||||||
|
|
||||||
return self._exec(method='options', url=url, **kwargs)
|
return self._exec(method='options', url=url, **kwargs)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def download(self, url: str, path: str, **kwargs):
|
||||||
|
"""
|
||||||
|
Locally download the content of a remote URL.
|
||||||
|
|
||||||
|
:param url: URL to be downloaded.
|
||||||
|
:param path: Path where the content will be downloaded on the local filesystem - must be a file name.
|
||||||
|
"""
|
||||||
|
path = os.path.abspath(os.path.expanduser(path))
|
||||||
|
content = self._exec(method='get', url=url, output='binary', **kwargs)
|
||||||
|
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -201,3 +201,8 @@ croniter
|
||||||
# Support for keyboard/mouse plugin
|
# Support for keyboard/mouse plugin
|
||||||
# pyuserinput
|
# pyuserinput
|
||||||
|
|
||||||
|
# Support for Buienradar weather forecast
|
||||||
|
# buienradar
|
||||||
|
|
||||||
|
# Support for Telegram integration
|
||||||
|
# python-telegram-bot
|
||||||
|
|
56
setup.py
56
setup.py
|
@ -19,6 +19,7 @@ class WebBuildCommand(distutils.cmd.Command):
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_css_files(cls):
|
def generate_css_files(cls):
|
||||||
try:
|
try:
|
||||||
|
# noinspection PyPackageRequirements
|
||||||
from scss import Compiler
|
from scss import Compiler
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print('pyScss module not found: {}. You will have to generate ' +
|
print('pyScss module not found: {}. You will have to generate ' +
|
||||||
|
@ -26,9 +27,9 @@ class WebBuildCommand(distutils.cmd.Command):
|
||||||
return
|
return
|
||||||
|
|
||||||
print('Building CSS files')
|
print('Building CSS files')
|
||||||
base_path = path(os.path.join('platypush','backend','http','static','css'))
|
base_path = path(os.path.join('platypush', 'backend', 'http', 'static', 'css'))
|
||||||
input_path = path(os.path.join(base_path,'source'))
|
input_path = path(os.path.join(base_path, 'source'))
|
||||||
output_path = path(os.path.join(base_path,'dist'))
|
output_path = path(os.path.join(base_path, 'dist'))
|
||||||
|
|
||||||
for root, dirs, files in os.walk(input_path, followlinks=True):
|
for root, dirs, files in os.walk(input_path, followlinks=True):
|
||||||
scss_file = os.path.join(root, 'index.scss')
|
scss_file = os.path.join(root, 'index.scss')
|
||||||
|
@ -59,7 +60,6 @@ class WebBuildCommand(distutils.cmd.Command):
|
||||||
self.generate_css_files()
|
self.generate_css_files()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BuildCommand(build):
|
class BuildCommand(build):
|
||||||
def run(self):
|
def run(self):
|
||||||
build.run(self)
|
build.run(self)
|
||||||
|
@ -75,8 +75,10 @@ def readfile(fname):
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
def pkg_files(dir):
|
def pkg_files(dir):
|
||||||
paths = []
|
paths = []
|
||||||
|
# noinspection PyShadowingNames
|
||||||
for (path, dirs, files) in os.walk(dir):
|
for (path, dirs, files) in os.walk(dir):
|
||||||
for file in files:
|
for file in files:
|
||||||
paths.append(os.path.join('..', path, file))
|
paths.append(os.path.join('..', path, file))
|
||||||
|
@ -84,6 +86,7 @@ def pkg_files(dir):
|
||||||
|
|
||||||
|
|
||||||
def create_etc_dir():
|
def create_etc_dir():
|
||||||
|
# noinspection PyShadowingNames
|
||||||
path = '/etc/platypush'
|
path = '/etc/platypush'
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
|
@ -101,40 +104,40 @@ backend = pkg_files('platypush/backend')
|
||||||
# create_etc_dir()
|
# create_etc_dir()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = "platypush",
|
name="platypush",
|
||||||
version = "0.11.2",
|
version="0.11.2",
|
||||||
author = "Fabio Manganiello",
|
author="Fabio Manganiello",
|
||||||
author_email = "info@fabiomanganiello.com",
|
author_email="info@fabiomanganiello.com",
|
||||||
description = ("Platypush service"),
|
description="Platypush service",
|
||||||
license = "MIT",
|
license="MIT",
|
||||||
python_requires = '>= 3.5',
|
python_requires='>= 3.5',
|
||||||
keywords = "home-automation iot mqtt websockets redis dashboard notificaions",
|
keywords="home-automation iot mqtt websockets redis dashboard notificaions",
|
||||||
url = "https://github.com/BlackLight/platypush",
|
url="https://github.com/BlackLight/platypush",
|
||||||
packages = find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data = True,
|
include_package_data=True,
|
||||||
entry_points = {
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'platypush=platypush:main',
|
'platypush=platypush:main',
|
||||||
'pusher=platypush.pusher:main',
|
'pusher=platypush.pusher:main',
|
||||||
'platydock=platypush.platydock:main',
|
'platydock=platypush.platydock:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
scripts = ['bin/platyvenv'],
|
scripts=['bin/platyvenv'],
|
||||||
cmdclass = {
|
cmdclass={
|
||||||
'web_build': WebBuildCommand,
|
'web_build': WebBuildCommand,
|
||||||
'build': BuildCommand,
|
'build': BuildCommand,
|
||||||
},
|
},
|
||||||
# data_files = [
|
# data_files = [
|
||||||
# ('/etc/platypush', ['platypush/config.example.yaml'])
|
# ('/etc/platypush', ['platypush/config.example.yaml'])
|
||||||
# ],
|
# ],
|
||||||
long_description = readfile('README.md'),
|
long_description=readfile('README.md'),
|
||||||
long_description_content_type = 'text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
classifiers = [
|
classifiers=[
|
||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
],
|
],
|
||||||
install_requires = [
|
install_requires=[
|
||||||
'pyyaml',
|
'pyyaml',
|
||||||
'redis',
|
'redis',
|
||||||
'requests',
|
'requests',
|
||||||
|
@ -143,7 +146,7 @@ setup(
|
||||||
'sqlalchemy',
|
'sqlalchemy',
|
||||||
],
|
],
|
||||||
|
|
||||||
extras_require = {
|
extras_require={
|
||||||
# Support for thread custom name
|
# Support for thread custom name
|
||||||
'threadname': ['python-prctl'],
|
'threadname': ['python-prctl'],
|
||||||
# Support for Kafka backend and plugin
|
# Support for Kafka backend and plugin
|
||||||
|
@ -228,7 +231,7 @@ setup(
|
||||||
# Support for LTR559 light/proximity sensor
|
# Support for LTR559 light/proximity sensor
|
||||||
'ltr559': ['ltr559'],
|
'ltr559': ['ltr559'],
|
||||||
# Support for VL53L1X laser ranger/distance sensor
|
# Support for VL53L1X laser ranger/distance sensor
|
||||||
'vl53l1x': ['smbus2','vl53l1x'],
|
'vl53l1x': ['smbus2', 'vl53l1x'],
|
||||||
# Support for Dropbox integration
|
# Support for Dropbox integration
|
||||||
'dropbox': ['dropbox'],
|
'dropbox': ['dropbox'],
|
||||||
# Support for Leap Motion backend
|
# Support for Leap Motion backend
|
||||||
|
@ -260,6 +263,9 @@ setup(
|
||||||
'google-pubsub': ['google-cloud-pubsub'],
|
'google-pubsub': ['google-cloud-pubsub'],
|
||||||
# Support for keyboard/mouse plugin
|
# Support for keyboard/mouse plugin
|
||||||
'inputs': ['pyuserinput'],
|
'inputs': ['pyuserinput'],
|
||||||
|
# Support for Buienradar weather forecast
|
||||||
|
'buienradar': ['buienradar'],
|
||||||
|
# Support for Telegram integration
|
||||||
|
'telegram': ['python-telegram-bot'],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue