Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Contributors:
Ilya Otyutskiy <[email protected]>
Stefano Teodorani <[email protected]>
Francesco Zimbolo <[email protected]>
Mattia Effendi <[email protected]>

Original author:

Expand Down
1 change: 1 addition & 0 deletions botogram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .objects import *
from .utils import usernames_in
from .callbacks import Buttons, ButtonsRow
from .keyboards import Keyboard, KeyboardRow


# This code will simulate the Windows' multiprocessing behavior if the
Expand Down
84 changes: 84 additions & 0 deletions botogram/keyboards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) 2015-2019 The Botogram Authors (see AUTHORS)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER


class KeyboardRow:
"""A row of a keyboard"""

def __init__(self):
self._content = []

def text(self, text):
"""Sends a message when the button is pressed"""
self._content.append({"text": text})

def request_contact(self, label):
"""Ask the user if he wants to share his contact"""

self._content.append({
"text": label,
"request_contact": True,
})

def request_location(self, label):
"""Ask the user if he wants to share his location"""

self._content.append({
"text": label,
"request_location": True,
})

def _get_content(self, chat):
"""Get the content of this row"""
for item in self._content:
new = item.copy()

# Replace any callable with its value
# This allows to dynamically generate field values
for key, value in new.items():
if callable(value):
new[key] = value(chat)

yield new


class Keyboard:
"""Factory for keyboards"""

def __init__(self, resize=False, one_time=False, selective=False):
self.resize_keyboard = resize
self.one_time_keyboard = one_time
self.selective = selective
self._rows = {}

def __getitem__(self, index):
if index not in self._rows:
self._rows[index] = KeyboardRow()
return self._rows[index]

def _serialize_attachment(self, chat):
rows = [
list(row._get_content(chat)) for i, row in sorted(
tuple(self._rows.items()), key=lambda i: i[0]
)
]

return {"keyboard": rows, "resize_keyboard": self.resize_keyboard,
"one_time_keyboard": self.one_time_keyboard,
"selective": self.selective}
98 changes: 71 additions & 27 deletions botogram/objects/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def __(self, *args, **kwargs):
class ChatMixin:
"""Add some methods for chats"""

def _get_call_args(self, reply_to, extra, attach, notify):
def _get_call_args(self, reply_to, extra, attach, notify, remove_keyboard,
force_reply, selective):
"""Get default API call arguments"""
# Convert instance of Message to ids in reply_to
if hasattr(reply_to, "id"):
Expand All @@ -75,6 +76,23 @@ def _get_call_args(self, reply_to, extra, attach, notify):
if not notify:
args["disable_notification"] = True

if (remove_keyboard is None or force_reply is None) \
and selective is not None:
raise ValueError("The selective attribute is only usable" +
"when remove_keyboard or force_reply is True")

if remove_keyboard is not None:
args["reply_markup"] = json.dumps(
{"remove_keyboard": remove_keyboard}
)
if selective is not None:
args["reply_markup"] = json.dumps({"selective": selective})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this you replace you need to append


if force_reply is not None:
args["reply_markup"] = json.dumps({"force_reply": force_reply})
if selective is not None:
args["reply_markup"] = json.dumps({"selective": selective})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this you replace you need to append


return args

@staticmethod
Expand All @@ -97,9 +115,11 @@ def _get_file_args(path, file_id, url):

@_require_api
def send(self, message, preview=True, reply_to=None, syntax=None,
extra=None, attach=None, notify=True):
extra=None, attach=None, notify=True, remove_keyboard=None,
force_reply=None, selective=None):
"""Send a message"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["text"] = message
args["disable_web_page_preview"] = not preview

Expand All @@ -112,9 +132,11 @@ def send(self, message, preview=True, reply_to=None, syntax=None,
@_require_api
def send_photo(self, path=None, file_id=None, url=None, caption=None,
syntax=None, reply_to=None, extra=None, attach=None,
notify=True):
notify=True, remove_keyboard=None, force_reply=None,
selective=None):
"""Send a photo"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if caption is not None:
args["caption"] = caption
if syntax is not None:
Expand All @@ -133,10 +155,12 @@ def send_photo(self, path=None, file_id=None, url=None, caption=None,
@_require_api
def send_audio(self, path=None, file_id=None, url=None, duration=None,
thumb=None, performer=None, title=None, reply_to=None,
extra=None, attach=None, notify=True, caption=None, *,
extra=None, attach=None, notify=True, remove_keyboard=None,
force_reply=None, selective=None, caption=None, *,
syntax=None):
"""Send an audio track"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if caption is not None:
args["caption"] = caption
if syntax is not None:
Expand Down Expand Up @@ -164,9 +188,11 @@ def send_audio(self, path=None, file_id=None, url=None, duration=None,
@_require_api
def send_voice(self, path=None, file_id=None, url=None, duration=None,
title=None, reply_to=None, extra=None, attach=None,
notify=True, caption=None, *, syntax=None):
notify=True, remove_keyboard=None, force_reply=None,
selective=None, caption=None, *, syntax=None):
"""Send a voice message"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if caption is not None:
args["caption"] = caption
if syntax is not None:
Expand Down Expand Up @@ -194,9 +220,11 @@ def send_voice(self, path=None, file_id=None, url=None, duration=None,
def send_video(self, path=None, file_id=None, url=None,
duration=None, caption=None, streaming=True, thumb=None,
reply_to=None, extra=None, attach=None,
notify=True, *, syntax=None):
notify=True, remove_keyboard=None, force_reply=None,
selective=None, *, syntax=None):
"""Send a video"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["supports_streaming"] = streaming
if duration is not None:
args["duration"] = duration
Expand All @@ -221,9 +249,11 @@ def send_video(self, path=None, file_id=None, url=None,
@_require_api
def send_video_note(self, path=None, file_id=None, duration=None,
diameter=None, thumb=None, reply_to=None, extra=None,
attach=None, notify=True):
attach=None, notify=True, remove_keyboard=None,
force_reply=None, selective=None):
"""Send a video note"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if duration is not None:
args["duration"] = duration
if diameter is not None:
Expand All @@ -245,9 +275,11 @@ def send_video_note(self, path=None, file_id=None, duration=None,
def send_gif(self, path=None, file_id=None, url=None, duration=None,
width=None, height=None, caption=None, thumb=None,
reply_to=None, extra=None, attach=None,
notify=True, syntax=None):
notify=True, remove_keyboard=None, force_reply=None,
selective=None, syntax=None):
"""Send an animation"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if duration is not None:
args["duration"] = duration
if caption is not None:
Expand Down Expand Up @@ -275,9 +307,11 @@ def send_gif(self, path=None, file_id=None, url=None, duration=None,
@_require_api
def send_file(self, path=None, file_id=None, url=None, thumb=None,
reply_to=None, extra=None, attach=None,
notify=True, caption=None, *, syntax=None):
notify=True, remove_keyboard=None, force_reply=None,
selective=None, caption=None, *, syntax=None):
"""Send a generic file"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
if caption is not None:
args["caption"] = caption
if syntax is not None:
Expand All @@ -298,10 +332,12 @@ def send_file(self, path=None, file_id=None, url=None, thumb=None,

@_require_api
def send_location(self, latitude, longitude, live_period=None,
reply_to=None, extra=None, attach=None, notify=True):
reply_to=None, extra=None, attach=None, notify=True,
remove_keyboard=None, force_reply=None, selective=None):
"""Send a geographic location, set live_period to a number between 60
and 86400 if it's a live location"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["latitude"] = latitude
args["longitude"] = longitude

Expand All @@ -316,9 +352,11 @@ def send_location(self, latitude, longitude, live_period=None,

@_require_api
def send_venue(self, latitude, longitude, title, address, foursquare=None,
reply_to=None, extra=None, attach=None, notify=True):
reply_to=None, extra=None, attach=None, notify=True,
remove_keyboard=None, force_reply=None, selective=None):
"""Send a venue"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["latitude"] = latitude
args["longitude"] = longitude
args["title"] = title
Expand All @@ -330,7 +368,8 @@ def send_venue(self, latitude, longitude, title, address, foursquare=None,

@_require_api
def send_sticker(self, sticker=None, reply_to=None, extra=None,
attach=None, notify=True, *,
attach=None, notify=True, remove_keyboard=None,
force_reply=None, selective=None, *,
path=None, file_id=None, url=None):
"""Send a sticker"""
if sticker is not None:
Expand All @@ -342,7 +381,8 @@ def send_sticker(self, sticker=None, reply_to=None, extra=None,
"The sticker parameter", "1.0", "use the path parameter", -3
)

args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)

files = dict()
args["sticker"], files["sticker"] = self._get_file_args(path,
Expand All @@ -356,9 +396,11 @@ def send_sticker(self, sticker=None, reply_to=None, extra=None,

@_require_api
def send_contact(self, phone, first_name, last_name=None, *, reply_to=None,
extra=None, attach=None, notify=True):
extra=None, attach=None, notify=True,
remove_keyboard=None, force_reply=None, selective=None):
"""Send a contact"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["phone_number"] = phone
args["first_name"] = first_name

Expand All @@ -369,9 +411,11 @@ def send_contact(self, phone, first_name, last_name=None, *, reply_to=None,

@_require_api
def send_poll(self, question, *kargs, reply_to=None, extra=None,
attach=None, notify=True):
attach=None, notify=True, remove_keyboard=None,
force_reply=None, selective=None):
"""Send a poll"""
args = self._get_call_args(reply_to, extra, attach, notify)
args = self._get_call_args(reply_to, extra, attach, notify,
remove_keyboard, force_reply, selective)
args["question"] = question
args["options"] = json.dumps(list(kargs))

Expand Down