1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Bot API update

Bot API conformance up to 4.4

Added webhook parameters from 5.0
This commit is contained in:
Badiboy 2020-12-29 19:24:41 +03:00
parent c4e624d999
commit 6559f431b7
4 changed files with 97 additions and 52 deletions

View File

@ -540,11 +540,13 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
_Checking is in progress..._ _Checking is in progress..._
✅ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019) _- To be checked..._ ✅ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._
* ✔ [Bot API 4.4](https://core.telegram.org/bots/api-changelog#july-29-2019)
* ✔ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019)
* ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019) * ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019)
* [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support. * [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support.
* [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support. * [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support.
* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018) * ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018)
* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) * ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017)
* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) * ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017)

View File

@ -235,17 +235,37 @@ class TeleBot:
""" """
self.reply_backend.load_handlers(filename, del_file_after_loading) self.reply_backend.load_handlers(filename, del_file_after_loading)
def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None):
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) """
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an
update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update.
In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success.
def delete_webhook(self): :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration
:param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details.
:param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput.
:param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [message, edited_channel_post, callback_query] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used.
:param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS
:return:
"""
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address)
def delete_webhook(self, drop_pending_updates=None):
""" """
Use this method to remove webhook integration if you decide to switch back to getUpdates. Use this method to remove webhook integration if you decide to switch back to getUpdates.
:param drop_pending_updates: Pass True to drop all pending updates
:return: bool :return: bool
""" """
return apihelper.delete_webhook(self.token) return apihelper.delete_webhook(self.token, drop_pending_updates)
def get_webhook_info(self): def get_webhook_info(self):
"""
Use this method to get current webhook status. Requires no parameters.
If the bot is using getUpdates, will return an object with the url field empty.
:return: On success, returns a WebhookInfo object.
"""
result = apihelper.get_webhook_info(self.token) result = apihelper.get_webhook_info(self.token)
return types.WebhookInfo.de_json(result) return types.WebhookInfo.de_json(result)
@ -432,7 +452,8 @@ class TeleBot:
while not self.__stop_polling.is_set(): while not self.__stop_polling.is_set():
try: try:
self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs)
except Exception: except Exception as e:
logger.error("Infinity polling exception: {}".format(e))
time.sleep(3) time.sleep(3)
pass pass
logger.info("Break infinity polling") logger.info("Break infinity polling")
@ -1059,11 +1080,16 @@ class TeleBot:
def unban_chat_member(self, chat_id, user_id, only_if_banned = False): def unban_chat_member(self, chat_id, user_id, only_if_banned = False):
""" """
Removes member from the ban Use this method to unban a previously kicked user in a supergroup or channel.
:param chat_id: The user will not return to the group or channel automatically, but will be able to join via link, etc.
:param user_id: The bot must be an administrator for this to work. By default, this method guarantees that after the call
:param only_if_banned: the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat
:return: they will also be removed from the chat. If you don't want this, use the parameter only_if_banned.
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username)
:param user_id: Unique identifier of the target user
:param only_if_banned: Do nothing if the user is not banned
:return: True on success
""" """
return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned)
@ -1077,7 +1103,7 @@ class TeleBot:
Use this method to restrict a user in a supergroup. Use this method to restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have The bot must be an administrator in the supergroup for this to work and must have
the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user.
Returns True on success.
:param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup
or channel (in the format @channelusername) or channel (in the format @channelusername)
:param user_id: Int : Unique identifier of the target user :param user_id: Int : Unique identifier of the target user
@ -1096,7 +1122,7 @@ class TeleBot:
:param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat,
implies can_invite_users implies can_invite_users
:param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups
:return: types.Message :return: True on success
""" """
return apihelper.restrict_chat_member( return apihelper.restrict_chat_member(
self.token, chat_id, user_id, until_date, self.token, chat_id, user_id, until_date,
@ -1111,7 +1137,8 @@ class TeleBot:
""" """
Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator
in the chat for this to work and must have the appropriate admin rights. in the chat for this to work and must have the appropriate admin rights.
Pass False for all boolean parameters to demote a user. Returns True on success. Pass False for all boolean parameters to demote a user.
:param chat_id: Unique identifier for the target chat or username of the target channel ( :param chat_id: Unique identifier for the target chat or username of the target channel (
in the format @channelusername) in the format @channelusername)
:param user_id: Int : Unique identifier of the target user :param user_id: Int : Unique identifier of the target user
@ -1125,7 +1152,7 @@ class TeleBot:
:param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset
of his own privileges or demote administrators that he has promoted, directly or indirectly of his own privileges or demote administrators that he has promoted, directly or indirectly
(promoted by administrators that were appointed by him) (promoted by administrators that were appointed by him)
:return: :return: True on success.
""" """
return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages, return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages,
can_edit_messages, can_delete_messages, can_invite_users, can_edit_messages, can_delete_messages, can_invite_users,
@ -1134,26 +1161,27 @@ class TeleBot:
def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title):
""" """
Use this method to set a custom title for an administrator Use this method to set a custom title for an administrator
in a supergroup promoted by the bot. in a supergroup promoted by the bot.
Returns True on success.
:param chat_id: Unique identifier for the target chat or username of the target supergroup :param chat_id: Unique identifier for the target chat or username of the target supergroup
(in the format @supergroupusername) (in the format @supergroupusername)
:param user_id: Unique identifier of the target user :param user_id: Unique identifier of the target user
:param custom_title: New custom title for the administrator; :param custom_title: New custom title for the administrator;
0-16 characters, emoji are not allowed 0-16 characters, emoji are not allowed
:return: :return: True on success.
""" """
return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title)
def set_chat_permissions(self, chat_id, permissions): def set_chat_permissions(self, chat_id, permissions):
""" """
Use this method to set default chat permissions for all members. Use this method to set default chat permissions for all members.
The bot must be an administrator in the group or a supergroup for this to work The bot must be an administrator in the group or a supergroup for this to work
and must have the can_restrict_members admin rights. and must have the can_restrict_members admin rights.
:param chat_id: Unique identifier for the target chat or username of the target supergroup :param chat_id: Unique identifier for the target chat or username of the target supergroup
(in the format @supergroupusername) (in the format @supergroupusername)
:param permissions: New default chat permissions :param permissions: New default chat permissions
:return: :return: True on success
""" """
return apihelper.set_chat_permissions(self.token, chat_id, permissions) return apihelper.set_chat_permissions(self.token, chat_id, permissions)
@ -1161,10 +1189,10 @@ class TeleBot:
""" """
Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator
in the chat for this to work and must have the appropriate admin rights. in the chat for this to work and must have the appropriate admin rights.
Returns exported invite link as String on success.
:param chat_id: Id: Unique identifier for the target chat or username of the target channel :param chat_id: Id: Unique identifier for the target chat or username of the target channel
(in the format @channelusername) (in the format @channelusername)
:return: :return: exported invite link as String on success.
""" """
return apihelper.export_chat_invite_link(self.token, chat_id) return apihelper.export_chat_invite_link(self.token, chat_id)
@ -1218,15 +1246,15 @@ class TeleBot:
""" """
return apihelper.set_chat_title(self.token, chat_id, title) return apihelper.set_chat_title(self.token, chat_id, title)
def set_chat_description(self, chat_id, description): def set_chat_description(self, chat_id, description=None):
""" """
Use this method to change the description of a supergroup or a channel. Use this method to change the description of a supergroup or a channel.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername) (in the format @channelusername)
:param description: Str: New chat description, 0-255 characters :param description: Str: New chat description, 0-255 characters
:return: :return: True on success.
""" """
return apihelper.set_chat_description(self.token, chat_id, description) return apihelper.set_chat_description(self.token, chat_id, description)

View File

@ -206,7 +206,7 @@ def send_message(
return _make_request(token, method_url, params=payload, method='post') return _make_request(token, method_url, params=payload, method='post')
def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None): def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None):
method_url = r'setWebhook' method_url = r'setWebhook'
payload = { payload = {
'url': url if url else "", 'url': url if url else "",
@ -218,12 +218,17 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed
payload['max_connections'] = max_connections payload['max_connections'] = max_connections
if allowed_updates is not None: # Empty lists should pass if allowed_updates is not None: # Empty lists should pass
payload['allowed_updates'] = json.dumps(allowed_updates) payload['allowed_updates'] = json.dumps(allowed_updates)
if ip_address is not None: # Empty string should pass
payload['ip_address'] = ip_address
return _make_request(token, method_url, params=payload, files=files) return _make_request(token, method_url, params=payload, files=files)
def delete_webhook(token): def delete_webhook(token, drop_pending_updates=None):
method_url = r'deleteWebhook' method_url = r'deleteWebhook'
return _make_request(token, method_url) payload = {}
if drop_pending_updates is not None: # None / True / False
payload['drop_pending_updates'] = drop_pending_updates
return _make_request(token, method_url, params=payload)
def get_webhook_info(token): def get_webhook_info(token):
@ -703,7 +708,7 @@ def kick_chat_member(token, chat_id, user_id, until_date=None):
def unban_chat_member(token, chat_id, user_id, only_if_banned): def unban_chat_member(token, chat_id, user_id, only_if_banned):
method_url = 'unbanChatMember' method_url = 'unbanChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id} payload = {'chat_id': chat_id, 'user_id': user_id}
if only_if_banned: if only_if_banned is not None: # None / True / False
payload['only_if_banned'] = only_if_banned payload['only_if_banned'] = only_if_banned
return _make_request(token, method_url, params=payload, method='post') return _make_request(token, method_url, params=payload, method='post')
@ -820,7 +825,9 @@ def set_my_commands(token, commands):
def set_chat_description(token, chat_id, description): def set_chat_description(token, chat_id, description):
method_url = 'setChatDescription' method_url = 'setChatDescription'
payload = {'chat_id': chat_id, 'description': description} payload = {'chat_id': chat_id}
if description is not None: # Allow empty strings
payload['description'] = description
return _make_request(token, method_url, params=payload, method='post') return _make_request(token, method_url, params=payload, method='post')

View File

@ -132,18 +132,20 @@ class WebhookInfo(JsonDeserializable):
url = obj['url'] url = obj['url']
has_custom_certificate = obj['has_custom_certificate'] has_custom_certificate = obj['has_custom_certificate']
pending_update_count = obj['pending_update_count'] pending_update_count = obj['pending_update_count']
ip_address = obj.get('ip_address')
last_error_date = obj.get('last_error_date') last_error_date = obj.get('last_error_date')
last_error_message = obj.get('last_error_message') last_error_message = obj.get('last_error_message')
max_connections = obj.get('max_connections') max_connections = obj.get('max_connections')
allowed_updates = obj.get('allowed_updates') allowed_updates = obj.get('allowed_updates')
return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, return cls(url, has_custom_certificate, pending_update_count, ip_address, last_error_date,
max_connections, allowed_updates) last_error_message, max_connections, allowed_updates)
def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, def __init__(self, url, has_custom_certificate, pending_update_count, ip_address, last_error_date,
max_connections, allowed_updates): last_error_message, max_connections, allowed_updates):
self.url = url self.url = url
self.has_custom_certificate = has_custom_certificate self.has_custom_certificate = has_custom_certificate
self.pending_update_count = pending_update_count self.pending_update_count = pending_update_count
self.ip_address = ip_address
self.last_error_date = last_error_date self.last_error_date = last_error_date
self.last_error_message = last_error_message self.last_error_message = last_error_message
self.max_connections = max_connections self.max_connections = max_connections
@ -218,8 +220,8 @@ class Chat(JsonDeserializable):
username = obj.get('username') username = obj.get('username')
first_name = obj.get('first_name') first_name = obj.get('first_name')
last_name = obj.get('last_name') last_name = obj.get('last_name')
all_members_are_administrators = obj.get('all_members_are_administrators')
photo = ChatPhoto.de_json(obj.get('photo')) photo = ChatPhoto.de_json(obj.get('photo'))
bio = obj.get('bio')
description = obj.get('description') description = obj.get('description')
invite_link = obj.get('invite_link') invite_link = obj.get('invite_link')
pinned_message = Message.de_json(obj.get('pinned_message')) pinned_message = Message.de_json(obj.get('pinned_message'))
@ -227,25 +229,27 @@ class Chat(JsonDeserializable):
slow_mode_delay = obj.get('slow_mode_delay') slow_mode_delay = obj.get('slow_mode_delay')
sticker_set_name = obj.get('sticker_set_name') sticker_set_name = obj.get('sticker_set_name')
can_set_sticker_set = obj.get('can_set_sticker_set') can_set_sticker_set = obj.get('can_set_sticker_set')
linked_chat_id = obj.get('linked_chat_id')
location = None # Not implemented
return cls( return cls(
id, type, title, username, first_name, last_name, id, type, title, username, first_name, last_name,
all_members_are_administrators, photo, description, invite_link, photo, bio, description, invite_link,
pinned_message, permissions, slow_mode_delay, sticker_set_name, pinned_message, permissions, slow_mode_delay, sticker_set_name,
can_set_sticker_set) can_set_sticker_set, linked_chat_id, location)
def __init__(self, id, type, title=None, username=None, first_name=None, def __init__(self, id, type, title=None, username=None, first_name=None,
last_name=None, all_members_are_administrators=None, last_name=None, photo=None, bio=None, description=None, invite_link=None,
photo=None, description=None, invite_link=None,
pinned_message=None, permissions=None, slow_mode_delay=None, pinned_message=None, permissions=None, slow_mode_delay=None,
sticker_set_name=None, can_set_sticker_set=None): sticker_set_name=None, can_set_sticker_set=None,
linked_chat_id=None, location=None):
self.id = id self.id = id
self.type = type self.type = type
self.title = title self.title = title
self.username = username self.username = username
self.first_name = first_name self.first_name = first_name
self.last_name = last_name self.last_name = last_name
self.all_members_are_administrators = all_members_are_administrators
self.photo = photo self.photo = photo
self.bio = bio
self.description = description self.description = description
self.invite_link = invite_link self.invite_link = invite_link
self.pinned_message = pinned_message self.pinned_message = pinned_message
@ -253,6 +257,8 @@ class Chat(JsonDeserializable):
self.slow_mode_delay = slow_mode_delay self.slow_mode_delay = slow_mode_delay
self.sticker_set_name = sticker_set_name self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set self.can_set_sticker_set = can_set_sticker_set
self.linked_chat_id = linked_chat_id
self.location = location
class Message(JsonDeserializable): class Message(JsonDeserializable):
@ -1054,7 +1060,7 @@ class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable):
json_dict['forward_text'] = self.forward_text json_dict['forward_text'] = self.forward_text
if self.bot_username: if self.bot_username:
json_dict['bot_username'] = self.bot_username json_dict['bot_username'] = self.bot_username
if self.request_write_access: if self.request_write_access is not None:
json_dict['request_write_access'] = self.request_write_access json_dict['request_write_access'] = self.request_write_access
return json_dict return json_dict
@ -1160,7 +1166,6 @@ class ChatMember(JsonDeserializable):
user = User.de_json(obj['user']) user = User.de_json(obj['user'])
status = obj['status'] status = obj['status']
custom_title = obj.get('custom_title') custom_title = obj.get('custom_title')
until_date = obj.get('until_date')
can_be_edited = obj.get('can_be_edited') can_be_edited = obj.get('can_be_edited')
can_post_messages = obj.get('can_post_messages') can_post_messages = obj.get('can_post_messages')
can_edit_messages = obj.get('can_edit_messages') can_edit_messages = obj.get('can_edit_messages')
@ -1176,23 +1181,23 @@ class ChatMember(JsonDeserializable):
can_send_polls = obj.get('can_send_polls') can_send_polls = obj.get('can_send_polls')
can_send_other_messages = obj.get('can_send_other_messages') can_send_other_messages = obj.get('can_send_other_messages')
can_add_web_page_previews = obj.get('can_add_web_page_previews') can_add_web_page_previews = obj.get('can_add_web_page_previews')
until_date = obj.get('until_date')
return cls( return cls(
user, status, custom_title, until_date, can_be_edited, can_post_messages, user, status, custom_title, can_be_edited, can_post_messages,
can_edit_messages, can_delete_messages, can_restrict_members, can_edit_messages, can_delete_messages, can_restrict_members,
can_promote_members, can_change_info, can_invite_users, can_pin_messages, can_promote_members, can_change_info, can_invite_users, can_pin_messages,
is_member, can_send_messages, can_send_media_messages, can_send_polls, is_member, can_send_messages, can_send_media_messages, can_send_polls,
can_send_other_messages, can_add_web_page_previews) can_send_other_messages, can_add_web_page_previews, until_date)
def __init__(self, user, status, custom_title=None, until_date=None, can_be_edited=None, def __init__(self, user, status, custom_title=None, can_be_edited=None,
can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None,
can_restrict_members=None, can_promote_members=None, can_change_info=None, can_restrict_members=None, can_promote_members=None, can_change_info=None,
can_invite_users=None, can_pin_messages=None, is_member=None, can_invite_users=None, can_pin_messages=None, is_member=None,
can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None,
can_send_other_messages=None, can_add_web_page_previews=None): can_send_other_messages=None, can_add_web_page_previews=None, until_date=None):
self.user = user self.user = user
self.status = status self.status = status
self.custom_title = custom_title self.custom_title = custom_title
self.until_date = until_date
self.can_be_edited = can_be_edited self.can_be_edited = can_be_edited
self.can_post_messages = can_post_messages self.can_post_messages = can_post_messages
self.can_edit_messages = can_edit_messages self.can_edit_messages = can_edit_messages
@ -1208,6 +1213,7 @@ class ChatMember(JsonDeserializable):
self.can_send_polls = can_send_polls self.can_send_polls = can_send_polls
self.can_send_other_messages = can_send_other_messages self.can_send_other_messages = can_send_other_messages
self.can_add_web_page_previews = can_add_web_page_previews self.can_add_web_page_previews = can_add_web_page_previews
self.until_date = until_date
class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable):
@ -2297,14 +2303,16 @@ class StickerSet(JsonDeserializable):
obj = cls.check_json(json_string) obj = cls.check_json(json_string)
name = obj['name'] name = obj['name']
title = obj['title'] title = obj['title']
is_animated = obj['is_animated']
contains_masks = obj['contains_masks'] contains_masks = obj['contains_masks']
stickers = [] stickers = []
for s in obj['stickers']: for s in obj['stickers']:
stickers.append(Sticker.de_json(s)) stickers.append(Sticker.de_json(s))
return cls(name, title, contains_masks, stickers) return cls(name, title, is_animated, contains_masks, stickers)
def __init__(self, name, title, contains_masks, stickers): def __init__(self, name, title, is_animated, contains_masks, stickers):
self.stickers = stickers self.stickers = stickers
self.is_animated = is_animated
self.contains_masks = contains_masks self.contains_masks = contains_masks
self.title = title self.title = title
self.name = name self.name = name