mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-27 03:43:45 +03:00
a5f5d6ef23
In particular, removed code which no longer worked, made light theme easier on the eyes, added slight syntax highlightning, and fixed search for exact matches.
246 lines
8.0 KiB
Python
246 lines
8.0 KiB
Python
import re
|
|
|
|
|
|
def _fmt_strings(*dicts):
|
|
for d in dicts:
|
|
for k, v in d.items():
|
|
if v in ('None', 'True', 'False'):
|
|
d[k] = '<strong>{}</strong>'.format(v)
|
|
else:
|
|
d[k] = re.sub(
|
|
r'([brf]?([\'"]).*\2)',
|
|
lambda m: '<em>{}</em>'.format(m.group(1)),
|
|
v
|
|
)
|
|
|
|
|
|
KNOWN_NAMED_EXAMPLES = {
|
|
('message', 'string'): "'Hello there!'",
|
|
('expires_at', 'date'): 'datetime.timedelta(minutes=5)',
|
|
('until_date', 'date'): 'datetime.timedelta(days=14)',
|
|
('view_messages', 'true'): 'None',
|
|
('send_messages', 'true'): 'None',
|
|
('limit', 'int'): '100',
|
|
('hash', 'int'): '0',
|
|
('hash', 'string'): "'A4LmkR23G0IGxBE71zZfo1'",
|
|
('min_id', 'int'): '0',
|
|
('max_id', 'int'): '0',
|
|
('min_id', 'long'): '0',
|
|
('max_id', 'long'): '0',
|
|
('add_offset', 'int'): '0',
|
|
('title', 'string'): "'My awesome title'",
|
|
('device_model', 'string'): "'ASUS Laptop'",
|
|
('system_version', 'string'): "'Arch Linux'",
|
|
('app_version', 'string'): "'1.0'",
|
|
('system_lang_code', 'string'): "'en'",
|
|
('lang_pack', 'string'): "''",
|
|
('lang_code', 'string'): "'en'",
|
|
('chat_id', 'int'): '478614198',
|
|
('client_id', 'long'): 'random.randrange(-2**63, 2**63)'
|
|
}
|
|
|
|
KNOWN_TYPED_EXAMPLES = {
|
|
'int128': "int.from_bytes(os.urandom(16), 'big')",
|
|
'bytes': "b'arbitrary\\x7f data \\xfa here'",
|
|
'long': "-12398745604826",
|
|
'string': "'some string here'",
|
|
'int': '42',
|
|
'date': 'datetime.datetime(2018, 6, 25)',
|
|
'double': '7.13',
|
|
'Bool': 'False',
|
|
'true': 'True',
|
|
'InputChatPhoto': "client.upload_file('/path/to/photo.jpg')",
|
|
'InputFile': "client.upload_file('/path/to/file.jpg')",
|
|
'InputPeer': "'username'"
|
|
}
|
|
|
|
_fmt_strings(KNOWN_NAMED_EXAMPLES, KNOWN_TYPED_EXAMPLES)
|
|
|
|
SYNONYMS = {
|
|
'InputUser': 'InputPeer',
|
|
'InputChannel': 'InputPeer',
|
|
'InputDialogPeer': 'InputPeer',
|
|
'InputNotifyPeer': 'InputPeer',
|
|
'InputMessage': 'int'
|
|
}
|
|
|
|
# These are flags that are cleaner to leave off
|
|
OMITTED_EXAMPLES = {
|
|
'silent',
|
|
'background',
|
|
'clear_draft',
|
|
'reply_to_msg_id',
|
|
'random_id',
|
|
'reply_markup',
|
|
'entities',
|
|
'embed_links',
|
|
'hash',
|
|
'min_id',
|
|
'max_id',
|
|
'add_offset',
|
|
'grouped',
|
|
'broadcast',
|
|
'admins',
|
|
'edit',
|
|
'delete'
|
|
}
|
|
|
|
|
|
class TLArg:
|
|
def __init__(self, name, arg_type, generic_definition):
|
|
"""
|
|
Initializes a new .tl argument
|
|
:param name: The name of the .tl argument
|
|
:param arg_type: The type of the .tl argument
|
|
:param generic_definition: Is the argument a generic definition?
|
|
(i.e. {X:Type})
|
|
"""
|
|
self.name = 'is_self' if name == 'self' else name
|
|
|
|
# Default values
|
|
self.is_vector = False
|
|
self.is_flag = False
|
|
self.skip_constructor_id = False
|
|
self.flag_index = -1
|
|
self.cls = None
|
|
|
|
# Special case: some types can be inferred, which makes it
|
|
# less annoying to type. Currently the only type that can
|
|
# be inferred is if the name is 'random_id', to which a
|
|
# random ID will be assigned if left as None (the default)
|
|
self.can_be_inferred = name == 'random_id'
|
|
|
|
# The type can be an indicator that other arguments will be flags
|
|
if arg_type == '#':
|
|
self.flag_indicator = True
|
|
self.type = None
|
|
self.is_generic = False
|
|
else:
|
|
self.flag_indicator = False
|
|
self.is_generic = arg_type.startswith('!')
|
|
# Strip the exclamation mark always to have only the name
|
|
self.type = arg_type.lstrip('!')
|
|
|
|
# The type may be a flag (flags.IDX?REAL_TYPE)
|
|
# Note that 'flags' is NOT the flags name; this
|
|
# is determined by a previous argument
|
|
# However, we assume that the argument will always be called 'flags'
|
|
flag_match = re.match(r'flags.(\d+)\?([\w<>.]+)', self.type)
|
|
if flag_match:
|
|
self.is_flag = True
|
|
self.flag_index = int(flag_match.group(1))
|
|
# Update the type to match the exact type, not the "flagged" one
|
|
self.type = flag_match.group(2)
|
|
|
|
# Then check if the type is a Vector<REAL_TYPE>
|
|
vector_match = re.match(r'[Vv]ector<([\w\d.]+)>', self.type)
|
|
if vector_match:
|
|
self.is_vector = True
|
|
|
|
# If the type's first letter is not uppercase, then
|
|
# it is a constructor and we use (read/write) its ID
|
|
# as pinpointed on issue #81.
|
|
self.use_vector_id = self.type[0] == 'V'
|
|
|
|
# Update the type to match the one inside the vector
|
|
self.type = vector_match.group(1)
|
|
|
|
# See use_vector_id. An example of such case is ipPort in
|
|
# help.configSpecial
|
|
if self.type.split('.')[-1][0].islower():
|
|
self.skip_constructor_id = True
|
|
|
|
# The name may contain "date" in it, if this is the case and
|
|
# the type is "int", we can safely assume that this should be
|
|
# treated as a "date" object. Note that this is not a valid
|
|
# Telegram object, but it's easier to work with
|
|
if self.type == 'int' and (
|
|
re.search(r'(\b|_)(date|until|since)\b', name) or
|
|
name in ('expires', 'expires_at', 'was_online')):
|
|
self.type = 'date'
|
|
|
|
self.generic_definition = generic_definition
|
|
|
|
def type_hint(self):
|
|
cls = self.type
|
|
if '.' in cls:
|
|
cls = cls.split('.')[1]
|
|
result = {
|
|
'int': 'int',
|
|
'long': 'int',
|
|
'int128': 'int',
|
|
'int256': 'int',
|
|
'string': 'str',
|
|
'date': 'Optional[datetime]', # None date = 0 timestamp
|
|
'bytes': 'bytes',
|
|
'true': 'bool',
|
|
}.get(cls, "Type{}".format(cls))
|
|
if self.is_vector:
|
|
result = 'List[{}]'.format(result)
|
|
if self.is_flag and cls != 'date':
|
|
result = 'Optional[{}]'.format(result)
|
|
|
|
return result
|
|
|
|
def real_type(self):
|
|
# Find the real type representation by updating it as required
|
|
real_type = self.type
|
|
if self.flag_indicator:
|
|
real_type = '#'
|
|
|
|
if self.is_vector:
|
|
if self.use_vector_id:
|
|
real_type = 'Vector<{}>'.format(real_type)
|
|
else:
|
|
real_type = 'vector<{}>'.format(real_type)
|
|
|
|
if self.is_generic:
|
|
real_type = '!{}'.format(real_type)
|
|
|
|
if self.is_flag:
|
|
real_type = 'flags.{}?{}'.format(self.flag_index, real_type)
|
|
|
|
return real_type
|
|
|
|
def __str__(self):
|
|
if self.generic_definition:
|
|
return '{{{}:{}}}'.format(self.name, self.real_type())
|
|
else:
|
|
return '{}:{}'.format(self.name, self.real_type())
|
|
|
|
def __repr__(self):
|
|
return str(self).replace(':date', ':int').replace('?date', '?int')
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'name': self.name.replace('is_self', 'self'),
|
|
'type': re.sub(r'\bdate$', 'int', self.real_type())
|
|
}
|
|
|
|
def as_example(self, f, indent=0):
|
|
if self.is_generic:
|
|
f.write('other_request')
|
|
return
|
|
|
|
known = (KNOWN_NAMED_EXAMPLES.get((self.name, self.type))
|
|
or KNOWN_TYPED_EXAMPLES.get(self.type)
|
|
or KNOWN_TYPED_EXAMPLES.get(SYNONYMS.get(self.type)))
|
|
if known:
|
|
f.write(known)
|
|
return
|
|
|
|
assert self.omit_example() or self.cls, 'TODO handle ' + str(self)
|
|
|
|
# Pick an interesting example if any
|
|
for cls in self.cls:
|
|
if cls.is_good_example():
|
|
cls.as_example(f, indent)
|
|
break
|
|
else:
|
|
# If no example is good, just pick the first
|
|
self.cls[0].as_example(f, indent)
|
|
|
|
def omit_example(self):
|
|
return (self.is_flag or self.can_be_inferred) \
|
|
and self.name in OMITTED_EXAMPLES
|