Skip to content

Commit 237cd3a

Browse files
authored
Новая форма логина (#522)
1 parent c161230 commit 237cd3a

5 files changed

Lines changed: 375 additions & 20 deletions

File tree

vk_api/credentials.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
:authors: python273, kyzima-spb
3+
:license: Apache License, Version 2.0, see LICENSE file
4+
5+
:copyright: (c) 2019 python273
6+
"""
7+
8+
import json
9+
import re
10+
import typing as t
11+
12+
import requests
13+
14+
from .exceptions import ParseError
15+
from .utils import (
16+
set_cookies_from_list,
17+
generate_device_id,
18+
search_re,
19+
)
20+
21+
22+
__all__ = (
23+
'WebLoginCredentials',
24+
)
25+
26+
27+
class WebLoginCredentials:
28+
"""
29+
Парсит и хранит данные новой формы входа,
30+
нужные для процесса аутентификации и авторизации пользователя.
31+
"""
32+
33+
SIGNIN_URL = 'https://m.vk.com/join?vkid_auth_type=sign_in'
34+
DEFAULT_COOKIES = [
35+
{ # если не установлено, то не будет редирект на страницу с данными аутентификации
36+
'version': 0,
37+
'name': 'remixmdevice',
38+
'value': '1920/1080/2/!!-!!!!',
39+
'port': None,
40+
'port_specified': False,
41+
'domain': '.vk.com',
42+
'domain_specified': True,
43+
'domain_initial_dot': True,
44+
'path': '/',
45+
'path_specified': True,
46+
'secure': True,
47+
'expires': None,
48+
'discard': False,
49+
'comment': None,
50+
'comment_url': None,
51+
'rfc2109': False,
52+
'rest': {}
53+
}
54+
]
55+
56+
def __init__(
57+
self,
58+
session: requests.Session,
59+
api_version: str = '5.207',
60+
) -> None:
61+
set_cookies_from_list(session.cookies, self.DEFAULT_COOKIES)
62+
63+
response = session.get(self.SIGNIN_URL)
64+
pattern = re.compile(r'window\.init\s*=\s*({.*?});', re.DOTALL)
65+
json_config = search_re(pattern, response.text)
66+
67+
if json_config is None:
68+
raise ParseError('Failed to get the value of variable window.init.')
69+
70+
self._config = json.loads(json_config)
71+
72+
self.sid = ''
73+
self.can_skip_password = False
74+
75+
self.device_id = generate_device_id()
76+
self.api_version = api_version
77+
78+
@property
79+
def app_id(self) -> str:
80+
"""ID приложения, через которое выполняется вход."""
81+
return self._config['auth']['host_app_id']
82+
83+
@property
84+
def uuid(self) -> str:
85+
"""
86+
Уникальный идентификатор запроса.
87+
88+
(вероятно, временный для конкретного входа).
89+
"""
90+
return self._config['data']['uuid']
91+
92+
@property
93+
def access_token(self) -> str:
94+
"""Токен доступа."""
95+
return self._config['auth']['access_token']
96+
97+
@property
98+
def anonymous_token(self) -> str:
99+
"""Анонимный токен доступа."""
100+
return self._config['auth']['anonymous_token']

vk_api/enums.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@
66
:copyright: (c) 2019 python273
77
"""
88

9-
from enum import IntEnum
9+
import enum
1010

1111

12-
class VkUserPermissions(IntEnum):
12+
class VerificationMethod(enum.StrEnum):
13+
"""
14+
Перечисление способов подтверждения входа в аккаунт.
15+
16+
EMAIL, SMS и PUSH требуют вызова метода API для отправки.
17+
"""
18+
PUSH = enum.auto()
19+
EMAIL = enum.auto()
20+
QR_CODE = enum.auto()
21+
CODEGEN = enum.auto()
22+
SMS = enum.auto()
23+
CALLRESET = enum.auto()
24+
PASSWORD = enum.auto()
25+
RESERVE_CODE = enum.auto()
26+
PASSKEY = enum.auto()
27+
28+
29+
class VkUserPermissions(enum.IntEnum):
1330
"""
1431
Перечисление прав пользователя.
1532
Список прав получается побитовым сложением (x | y) каждого права.

vk_api/exceptions.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
:copyright: (c) 2019 python273
77
"""
88

9+
import typing as t
910

1011
TWOFACTOR_CODE = -2
1112
HTTP_ERROR_CODE = -1
1213
TOO_MANY_RPS_CODE = 6
1314
CAPTCHA_ERROR_CODE = 14
1415
NEED_VALIDATION_CODE = 17
16+
CONFIRMATION_ERROR_CODE = 1110
1517

1618

1719
class VkApiError(Exception):
@@ -46,6 +48,23 @@ class TwoFactorError(AuthError):
4648
pass
4749

4850

51+
class ParseError(AuthError):
52+
"""Любая ошибка при парсинге исходных кодов сайта."""
53+
54+
55+
class AuthorizeError(AuthError):
56+
"""
57+
Любая ошибка, которая может возникнуть в момент авторизации
58+
существующего пользователя с проверенным кодом подтверждения или паролем.
59+
(https://login.vk.com/?act=connect_authorize)
60+
"""
61+
def __init__(self, error: t.Dict[str, t.Any]) -> None:
62+
self.error = error
63+
64+
def __str__(self) -> str:
65+
return '[{error_code}] {error_info}'.format(**self.error)
66+
67+
4968
class SecurityCheck(AuthError):
5069

5170
def __init__(self, phone_prefix=None, phone_postfix=None, response=None):

vk_api/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from __future__ import print_function
1010

1111
import random
12+
import secrets
13+
import string
1214

1315
try:
1416
import simplejson as json
@@ -166,3 +168,9 @@ def send(self, request, **kwargs):
166168

167169
vk_session.logger.setLevel(logging.INFO)
168170
vk_session.logger.addHandler(logging.StreamHandler(sys.stdout))
171+
172+
173+
def generate_device_id(n: int = 21) -> str:
174+
"""Generates a random string of length n from a given set of characters."""
175+
charset = f'{string.digits}{string.ascii_letters}-_'
176+
return ''.join(secrets.choice(charset) for _ in range(n))

0 commit comments

Comments
 (0)