Skip to content

Commit 372a637

Browse files
authored
Принудительный вход по паролю и рефакторинг (#525)
1 parent 237cd3a commit 372a637

4 files changed

Lines changed: 59 additions & 39 deletions

File tree

vk_api/credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import requests
1313

14-
from .exceptions import ParseError
14+
from .exceptions import AuthError
1515
from .utils import (
1616
set_cookies_from_list,
1717
generate_device_id,
@@ -65,7 +65,7 @@ def __init__(
6565
json_config = search_re(pattern, response.text)
6666

6767
if json_config is None:
68-
raise ParseError('Failed to get the value of variable window.init.')
68+
raise AuthError('Failed to get the value of variable window.init.')
6969

7070
self._config = json.loads(json_config)
7171

vk_api/enums.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@
88

99
import enum
1010

11+
try:
12+
from enum import StrEnum
13+
except ImportError:
14+
class StrEnum(str, enum.Enum):
15+
def _generate_next_value_(name, start, count, last_values):
16+
return name.lower()
1117

12-
class VerificationMethod(enum.StrEnum):
18+
19+
class VerificationMethod(StrEnum):
1320
"""
1421
Перечисление способов подтверждения входа в аккаунт.
1522
16-
EMAIL, SMS и PUSH требуют вызова метода API для отправки.
23+
EMAIL, SMS, CALLRESET и PUSH требуют вызова метода API для отправки.
1724
"""
1825
PUSH = enum.auto()
1926
EMAIL = enum.auto()

vk_api/exceptions.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,6 @@ class TwoFactorError(AuthError):
4848
pass
4949

5050

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-
6851
class SecurityCheck(AuthError):
6952

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

vk_api/vk_api.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -291,19 +291,27 @@ def _vk_login(self, captcha_sid=None, captcha_key=None):
291291
}
292292
)
293293

294+
if 'sid' not in account:
295+
raise AuthError('Account not exists')
296+
294297
credentials.sid = account['sid']
295298
next_step = account.get('next_step')
296299

297300
if next_step is not None and next_step['verification_method'] != VerificationMethod.PASSWORD:
298-
self.logger.info('Confirmation code is required')
299-
self._pass_confirmation_code(next_step['verification_method'], credentials)
301+
if (
302+
not next_step.get('has_another_verification_methods')
303+
or
304+
VerificationMethod.PASSWORD not in self._get_allowed_verification_methods(credentials)
305+
):
306+
self.logger.info('Confirmation code is required')
307+
self._pass_confirmation_code(next_step['verification_method'], credentials)
300308

301309
if not credentials.can_skip_password and not self.password:
302310
raise PasswordRequired('Password is required to login')
303311

304312
response_dict = self.vk_login_method(
305313
action='connect_authorize',
306-
data={
314+
values={
307315
'username': self.login,
308316
'password': self.password,
309317
'auth_token': credentials.access_token,
@@ -323,12 +331,7 @@ def _vk_login(self, captcha_sid=None, captcha_key=None):
323331
},
324332
)
325333

326-
if response_dict['type'] != 'okay':
327-
if response_dict['error_code'] == 'incorrect_password':
328-
raise BadPassword('Bad password')
329-
raise AuthorizeError(response_dict)
330-
331-
if response_dict['data']['is_user_banned']:
334+
if response_dict['is_user_banned']:
332335
raise AccountBlocked('Account is blocked')
333336

334337
if not self._sid:
@@ -437,6 +440,23 @@ def _vk_login_legacy(self, response, captcha_sid=None, captcha_key=None):
437440
if 'act=blocked' in response.url:
438441
raise AccountBlocked('Account is blocked')
439442

443+
def _get_allowed_verification_methods(self, credentials: WebLoginCredentials) -> t.Set[VerificationMethod]:
444+
"""Возвращает множество доступных для подтверждения входа методов."""
445+
verification_methods = set(VerificationMethod)
446+
response = self.method(
447+
with_cookies=True,
448+
method='ecosystem.getVerificationMethods',
449+
values={
450+
'v': credentials.api_version,
451+
'client_id': credentials.app_id,
452+
'sid': credentials.sid,
453+
'device_id': credentials.device_id,
454+
'anonymous_token': credentials.anonymous_token,
455+
'access_token': '',
456+
},
457+
)
458+
return {VerificationMethod(m['name']) for m in response['methods'] if m['name'] in verification_methods}
459+
440460
def _pass_confirmation_code(
441461
self,
442462
verification_method: str,
@@ -918,7 +938,7 @@ def method(
918938
def vk_login_method(
919939
self,
920940
action: str,
921-
data: t.Dict[str, t.Any],
941+
values: t.Dict[str, t.Any],
922942
headers: t.Optional[t.Dict[str, t.Any]] = None,
923943
captcha_sid: t.Optional[str] = None,
924944
captcha_key: t.Optional[str] = None,
@@ -929,8 +949,8 @@ def vk_login_method(
929949
:param action: имя действия, например, connect_authorize или connect_internal
930950
:type action: str
931951
932-
:param data: данные/поля формы
933-
:type data: dict
952+
:param values: данные/поля формы
953+
:type values: dict
934954
935955
:param headers: HTTP заголовки
936956
:type headers: dict
@@ -943,16 +963,15 @@ def vk_login_method(
943963
"""
944964
if captcha_sid and captcha_key:
945965
self.logger.info(f'Using captcha code: {captcha_sid}: {captcha_key}')
946-
data['captcha_sid'] = captcha_sid
947-
data['captcha_key'] = captcha_key
966+
values['captcha_sid'] = captcha_sid
967+
values['captcha_key'] = captcha_key
948968

949969
response = self.http.post(
950970
url=f'https://login.vk.com/?act={action}',
951-
data=data,
971+
data=values,
952972
headers=headers,
953973
)
954974
response_dict = response.json()
955-
self.logger.debug(response_dict)
956975

957976
if response_dict['type'] == 'captcha':
958977
captcha_type = response_dict['captcha_type']
@@ -965,14 +984,25 @@ def vk_login_method(
965984
func=self.vk_login_method,
966985
kwargs={
967986
'action': action,
968-
'data': data,
987+
'values': values,
969988
'headers': headers,
970989
},
971990
)
972991

973992
return self.error_handlers[CAPTCHA_ERROR_CODE](captcha)
974993

975-
return response_dict
994+
if response_dict['type'] != 'okay':
995+
if response_dict['error_code'] == 'incorrect_password':
996+
raise BadPassword(response_dict['error_info'])
997+
998+
if response_dict['error_code']:
999+
error_message = '[{error_code}] {error_info}'.format(**response_dict)
1000+
else:
1001+
error_message = response_dict['error_info']
1002+
1003+
raise AuthError(error_message)
1004+
1005+
return response_dict.get('data', response_dict)
9761006

9771007

9781008
class VkApiGroup(VkApi):

0 commit comments

Comments
 (0)