77Copyright (C) 2017
88"""
99
10+ import logging
1011import re
1112import threading
1213import time
3637
3738
3839class VkApi (object ):
39- def __init__ (self , login = None , password = None , number = None , sec_number = None ,
40- token = None ,
41- proxies = None ,
40+ def __init__ (self , login = None , password = None , token = None ,
4241 auth_handler = None , captcha_handler = None ,
43- config = None , config_filename = 'vk_config.json' ,
42+ config = jconfig . Config , config_filename = 'vk_config.v2 .json' ,
4443 api_version = '5.63' , app_id = 2895443 , scope = 33554431 ,
4544 client_secret = None ):
4645 """
47- :param login: Логин ВКонтакте
48- :param password: Пароль ВКонтакте
49- :param number: Номер для проверки безопасности (указывать, если
50- в качестве логина используется не номер)
51- :param sec_number: Часть номера, которая проверяется при проверке
52- безопасности (указывать, если точно известно, что
53- вводить и если автоматическое получение кода из
54- номера работает не корректно)
46+ :param login: Логин ВКонтакте (лучше использовать номер телефона для
47+ автоматического обхода проверки безопасности)
48+ :param password: Пароль ВКонтакте (если пароль не передан, то будет
49+ попытка использовать сохраненные данные для
50+ аутентификации)
5551
5652 :param token: access_token
57- :param proxies: proxy server
58- {'http': 'http://127.0.0.1:8888/',
59- 'https': 'https://127.0.0.1:8888/'}
6053 :param auth_handler: Функция для обработки двухфакторной аутентификации,
6154 должна возвращать строку с кодом и
6255 булевое значение, означающее, стоит ли запомнить
@@ -74,8 +67,6 @@ def __init__(self, login=None, password=None, number=None, sec_number=None,
7467
7568 self .login = login
7669 self .password = password
77- self .number = number
78- self .sec_number = sec_number
7970
8071 self .sid = None
8172 self .token = {'access_token' : token }
@@ -85,13 +76,9 @@ def __init__(self, login=None, password=None, number=None, sec_number=None,
8576 self .scope = scope
8677 self .client_secret = client_secret
8778
88- if config is None :
89- self .settings = jconfig .Config (login , filename = config_filename )
90- else :
91- self .settings = config (login , filename = config_filename )
79+ self .settings = config (self .login , filename = config_filename )
9280
9381 self .http = requests .Session ()
94- self .http .proxies = proxies
9582 self .http .headers .update ({
9683 'User-agent' : 'Mozilla/5.0 (Windows NT 6.1; rv:40.0) '
9784 'Gecko/20100101 Firefox/40.0'
@@ -108,27 +95,94 @@ def __init__(self, login=None, password=None, number=None, sec_number=None,
10895
10996 self .lock = threading .Lock ()
11097
111- def auth (self , reauth = False ):
112- """ Полная авторизация с получением токена
98+ self .logger = logging .getLogger ('vk_api' )
99+
100+ def auth (self , reauth = False , token_only = False ):
101+ """ Аутентификация
113102
114103 :param reauth: Позволяет переавторизиваться, игнорируя сохраненные
115- куки и токен
104+ куки и токен
105+
106+ :param token_only: Включает оптимальную стратегию аутентификации, если
107+ необходим только access_token
108+
109+ Например если сохраненные куки не валидны,
110+ но токен валиден, то аутентификация пройдет успешно
111+
112+ При token_only=False, сначала проверяется
113+ валидность куки. Если кука не будет валидна, то
114+ будет произведена попытка аутетификации с паролем.
115+ Тогда если пароль не верен или пароль не передан,
116+ то аутентификация закончится с ошибкой.
117+
118+ Если вы не делаете запросы к веб версии сайта
119+ используя куки, то лучше использовать
120+ token_only=True
116121 """
117122
118- if self .login and self .password :
119- if reauth :
120- self .settings .clear_section ()
123+ if not self .login :
124+ self .logger .info ('No login to auth' )
125+ return
126+
127+ self .logger .info ('Auth with login: {}' .format (self .login ))
128+
129+ self .sid = self .settings .remixsid
130+ self .token = self .settings .setdefault ('token' , {}).get (str (self .scope ))
131+
132+ if not token_only :
133+ self ._auth_cookies (reauth = reauth )
134+ else :
135+ self ._auth_token (reauth = reauth )
136+
137+ def _auth_cookies (self , reauth = False ):
138+
139+ if reauth :
140+ self .logger .info ('Auth forced' )
141+
142+ self .settings .clear_section ()
143+
144+ self .vk_login ()
145+ self .api_login ()
146+ return
147+
148+ if not self .check_sid ():
149+ self .logger .info (
150+ 'remixsid from config is not valid: {}' .format (
151+ self .sid
152+ )
153+ )
154+
155+ self .vk_login ()
156+ else :
157+ self .security_check ()
158+
159+ if not self .check_token ():
160+ self .logger .info (
161+ 'access_token from config is not valid: {}' .format (
162+ self .token
163+ )
164+ )
165+
166+ self .api_login ()
167+ else :
168+ self .logger .info ('access_token from config is valid' )
169+
170+ def _auth_token (self , reauth = False ):
121171
122- self .sid = self .settings .remixsid
123- self .token = self .settings .token
172+ if not reauth and self .check_token ():
173+ self .logger .info ('access_token from config is valid' )
174+ return
124175
125- if not self .check_sid ():
126- self .vk_login ()
127- else :
128- self .security_check ()
176+ if reauth :
177+ self .logger .info ('Auth (API) forced' )
129178
130- if not self .check_token ():
131- self .api_login ()
179+ if self .check_sid ():
180+ self .security_check ()
181+ self .api_login ()
182+
183+ elif self .password :
184+ self .vk_login ()
185+ self .api_login ()
132186
133187 def authorization (self , * args , ** kwargs ):
134188 import warnings
@@ -144,6 +198,12 @@ def authorization(self, *args, **kwargs):
144198 def vk_login (self , captcha_sid = None , captcha_key = None ):
145199 """ Авторизация ВКонтакте с получением cookies remixsid """
146200
201+ self .logger .info ('Logging in...' )
202+
203+ if not self .password :
204+ self .logger .info ('No password' )
205+ raise PasswordRequired ('Password is required to login' )
206+
147207 self .http .cookies .clear ()
148208
149209 # Get cookies
@@ -160,6 +220,13 @@ def vk_login(self, captcha_sid=None, captcha_key=None):
160220 }
161221
162222 if captcha_sid and captcha_key :
223+ self .logger .info (
224+ 'Using captcha code: {}: {}' .format (
225+ captcha_sid ,
226+ captcha_key
227+ )
228+ )
229+
163230 values .update ({
164231 'captcha_sid' : captcha_sid ,
165232 'captcha_key' : captcha_key
@@ -168,15 +235,20 @@ def vk_login(self, captcha_sid=None, captcha_key=None):
168235 response = self .http .post ('https://login.vk.com/' , values )
169236
170237 if 'onLoginCaptcha(' in response .text :
238+ self .logger .info ('Captcha code is required' )
239+
171240 captcha_sid = search_re (RE_CAPTCHAID , response .text )
172241 captcha = Captcha (self , captcha_sid , self .vk_login )
173242
174243 return self .error_handlers [CAPTCHA_ERROR_CODE ](captcha )
175244
176245 if 'onLoginFailed(4' in response .text :
246+ self .logger .info ('Bad password' )
177247 raise BadPassword ('Bad password' )
178248
179249 if 'act=authcheck' in response .text :
250+ self .logger .info ('Two factor is required' )
251+
180252 response = self .http .get ('https://vk.com/login?act=authcheck' )
181253
182254 self .twofactor (response )
@@ -187,6 +259,8 @@ def vk_login(self, captcha_sid=None, captcha_key=None):
187259 )
188260
189261 if remixsid :
262+ self .logger .info ('Got remixsid' )
263+
190264 self .settings .remixsid = remixsid
191265
192266 # Нужно для авторизации в API
@@ -199,6 +273,8 @@ def vk_login(self, captcha_sid=None, captcha_key=None):
199273
200274 self .sid = remixsid
201275 else :
276+ self .logger .info ('Unknown auth error' )
277+
202278 raise AuthError (
203279 'Unknown error. Please send bugreport: https://vk.com/python273'
204280 )
@@ -236,20 +312,19 @@ def twofactor(self, auth_response):
236312 raise TwoFactorError ('Two factor authentication failed' )
237313
238314 def security_check (self , response = None ):
315+ self .logger .info ('Checking security check request' )
316+
239317 if response is None :
240318 response = self .http .get ('https://vk.com/settings' )
241319 if 'security_check' not in response .url :
320+ self .logger .info ('Security check is not required' )
242321 return response
243322
244323 phone_prefix = clean_string (search_re (RE_PHONE_PREFIX , response .text ))
245324 phone_postfix = clean_string (search_re (RE_PHONE_POSTFIX , response .text ))
246325
247326 code = None
248- if self .sec_number :
249- code = self .sec_number
250- elif self .number :
251- code = code_from_number (phone_prefix , phone_postfix , self .number )
252- elif self .login :
327+ if self .login :
253328 code = code_from_number (phone_prefix , phone_postfix , self .login )
254329
255330 if code :
@@ -277,18 +352,26 @@ def security_check(self, response=None):
277352 def check_sid (self ):
278353 """ Проверка Cookies remixsid на валидность """
279354
280- if self .sid :
281- url = 'https://vk.com/feed2.php'
282- self .http .cookies .update ({
283- 'remixsid' : self .sid ,
284- 'remixlang' : '0' ,
285- 'remixsslsid' : '1'
286- })
355+ self .logger .info ('Checking remixsid...' )
287356
288- response = self .http .get (url ).json ()
357+ if not self .sid :
358+ self .logger .info ('No remixsid' )
359+ return
289360
290- if response ['user' ]['id' ] != - 1 :
291- return response
361+ url = 'https://vk.com/feed2.php'
362+ self .http .cookies .update ({
363+ 'remixsid' : self .sid ,
364+ 'remixlang' : '0' ,
365+ 'remixsslsid' : '1'
366+ })
367+
368+ response = self .http .get (url ).json ()
369+
370+ if response ['user' ]['id' ] != - 1 :
371+ self .logger .info ('remixsid is valid' )
372+ return response
373+
374+ self .logger .info ('remixsid is not valid' )
292375
293376 def api_login (self ):
294377 """ Получение токена через Desktop приложение """
@@ -322,9 +405,11 @@ def api_login(self):
322405 x = i .split ('=' )
323406 token .update ({x [0 ]: x [1 ]})
324407
325- self .settings .token = token
408+ self .settings .setdefault ( ' token' , {})[ str ( self . scope )] = token
326409 self .settings .save ()
327410 self .token = token
411+
412+ self .logger .info ('Got access_token' )
328413 else :
329414 raise AuthError ('Authorization error (api)' )
330415
0 commit comments