Advertisement
stuppid_bot

Untitled

Jun 3rd, 2015
403
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.76 KB | None | 0 0
  1. from urllib.error import HTTPError
  2. from urllib.parse import urlparse, urlencode
  3. from urllib.request import (
  4.     build_opener, Request, HTTPCookieProcessor, urlopen
  5. )
  6. import copy
  7. import hashlib
  8. import io
  9. import json
  10. import logging
  11. import mimetypes
  12. import os
  13. import re
  14. import time
  15. import uuid
  16. __author__ = "Sergei Snegirev <tz4678@gmail.com>"
  17. __license__ = "MIT"
  18. USER_AGENT = "Mozilla/5.0 (vkclient)"
  19. API_VERSION = 5.33
  20. API_DELAY = 0.34
  21. TIMEOUT = 15
  22.  
  23.  
  24. class VKClient:
  25.     http_re = re.compile('https?://', re.I)
  26.  
  27.     def __init__(self,
  28.                  access_token=None,
  29.                  user_id=None,
  30.                  expires=None,
  31.                  secret=None,
  32.                  client_id=None,
  33.                  client_secret=None,
  34.                  scope=None,
  35.                  captcha_handler=None,
  36.                  opener=None,
  37.                  api_version=API_VERSION,
  38.                  api_delay=API_DELAY,
  39.                  timeout=TIMEOUT):
  40.         self.access_token = access_token
  41.         self.user_id = user_id
  42.         self.expires = expires
  43.         self.secret = secret
  44.         self.client_id = client_id
  45.         self.client_secret = client_secret
  46.         self.scope = scope
  47.         self.captcha_handler = captcha_handler
  48.         if opener is None:
  49.             opener = build_opener(HTTPCookieProcessor())
  50.             opener.addheaders = [('user-agent', USER_AGENT)]
  51.         self.opener = opener
  52.         self.api_version = api_version
  53.         self.api_delay = api_delay
  54.         self.timeout = timeout
  55.         self.last_api_request = 0
  56.         name = '{}.{}'.format(self.__module__, self.__class__.__name__)
  57.         self.log = logging.getLogger(name)
  58.  
  59.     def request(self, url, data=None, headers={}):
  60.         if isinstance(data, dict):
  61.             data = urlencode(data).encode('ascii')
  62.         req = Request(url, data, headers)
  63.         try:
  64.             h = self.opener.open(req, timeout=self.timeout)
  65.         except HTTPError as e:
  66.             self.log.warning("HTTPError: %d %s", e.code, e.msg)
  67.             h = e
  68.         body = h.read()
  69.         encoding = h.headers.get_content_charset('utf-8')
  70.         body = body.decode(encoding)
  71.         return json.loads(body)
  72.  
  73.     def auth_direct(self, username, password):
  74.         params = dict(username=username,
  75.                       password=password,
  76.                       client_id=self.client_id,
  77.                       client_secret=self.client_secret,
  78.                       scope=self.scope,
  79.                       grant_type="password")
  80.         url = 'https://oauth.vk.com/token'
  81.         while True:
  82.             self.log.debug("Request: url=%s, params=%s", url, params)
  83.             response = self.request(url, params)
  84.             self.log.debug("Response: %s", response)
  85.             if 'error' in response:
  86.                 error = AuthError(response)
  87.                 if error.name == 'need_captcha':
  88.                     params.update({
  89.                         'captcha_key': self._handle_captcha(error),
  90.                         'captcha_sid': error.captcha_sid
  91.                     })
  92.                     continue
  93.                 raise error
  94.             break
  95.         self.access_token = response['access_token']
  96.         self.user_id = response['user_id']
  97.         expires_in = response['expires_in']
  98.         if expires_in > 0:
  99.             self.expires = time.time() + expires_in
  100.         # Если в scope есть nohttps будет возвращено поле secret
  101.         self.secret = response.get('secret')
  102.  
  103.     def auth_mobile(self):
  104.         raise NotImplementedError("method not implemented yet")
  105.  
  106.     def auth_site(self):
  107.         raise NotImplementedError("method not implemented yet")
  108.  
  109.     def auth_server(self):
  110.         raise NotImplementedError("method not implemented yet")
  111.  
  112.     def api(self, method, params={}):
  113.         """Отправляет запрос к API.
  114.  
  115.        :param method: Имя метода.
  116.        :param params: - словарь, необязательный. Параметры запроса.
  117.        :return: Значение поля `response`.
  118.        """
  119.         params = dict(params)
  120.         if not 'access_token' in params and self.access_token:
  121.             params['access_token'] = self.access_token
  122.         if not 'v' in params and self.api_version:
  123.             params['v'] = self.api_version
  124.         scheme = 'http' if self.secret else 'https'
  125.         url = '{}://api.vk.com/method/{}'.format(scheme, method)
  126.         while True:
  127.             if self.secret:
  128.                 if 'sig' in params:
  129.                     del params['sig']
  130.                 query = urlencode(params)
  131.                 s = '/method/{}?{}{}'.format(method, query, self.secret)
  132.                 params['sig'] = hashlib.md5(s.encode('ascii')).hexdigest()
  133.             delay = self.api_delay + self.last_api_request - time.time()
  134.             if delay > 0:
  135.                 self.log.debug("Waiting for %.6f sec.", delay)
  136.                 self.sleep(delay)
  137.             self.log.debug("Request: url=%s, params=%s", url, params)
  138.             response = self.request(url, params)
  139.             self.log.debug("Response: %s", response)
  140.             self.last_api_request = time.time()
  141.             if 'error' in response:
  142.                 error = ApiError(response['error'])
  143.                 if error.code == 14:
  144.                     params.update({
  145.                         'captcha_key': self._handle_captcha(error),
  146.                         'captcha_sid': error.captcha_sid
  147.                     })
  148.                     continue
  149.                 raise error
  150.             return response['response']
  151.  
  152.     def execute(self, code, **kw):
  153.         kw['code'] = code
  154.         return self.api('execute', kw)
  155.  
  156.     def test_access_token(self):
  157.         """Проверяет `access_token`.
  158.  
  159.        Работает только с клиентскими приложениями."""
  160.         try:
  161.             # можно вызвать любой метод требующий передачи access_token
  162.             self.users.get()
  163.         except ApiError:
  164.             return False
  165.         return True
  166.  
  167.     def upload(self, url, files):
  168.         self.log.debug('Upload files to %s', url)
  169.         boundary = uuid.uuid4().hex
  170.         buf = io.BytesIO()
  171.         for k, v in files.items():
  172.             if isinstance(v, str):
  173.                 if self.http_re.match(v):
  174.                     content = urlopen(v).read()
  175.                     filename = os.path.basename(urlparse(v).path)
  176.                 else:
  177.                     with open(v, 'rb') as fp:
  178.                         content = fp.read()
  179.                     filename = os.path.basename(v)
  180.             elif isinstance(v, io.IOBase):
  181.                 filename = os.path.basename(v.name)
  182.                 content = v.read()
  183.                 if not isinstance(content, bytes):
  184.                     content = str(content).encode('utf-8')
  185.             elif isinstance(v, (list, tuple)):
  186.                 filename, content = v
  187.                 if not isinstance(content, bytes):
  188.                     content = str(content).encode('utf-8')
  189.             else:
  190.                 raise ValueError("bad value: {!r}".format(v))
  191.             content_type = mimetypes.guess_type(filename)[0] or\
  192.                 'application/octeat-stream'
  193.             headers = (
  194.                 '--{}\r\n'
  195.                 'Content-Disposition: form-data; name="{}"; filename="{}"\r\n'
  196.                 'Content-Type: {}\r\n\r\n'
  197.             ).format(boundary, k, filename, content_type)
  198.             buf.write(bytes(headers, 'utf-8'))
  199.             buf.write(content)
  200.             buf.write(b'\r\n')
  201.         buf.write(bytes('--{}--\r\n'.format(boundary), 'utf-8'))
  202.         response = self.request(url, buf.getvalue(), {
  203.             'Content-Type': 'multipart/form-data; boundary=' + boundary
  204.         })
  205.         self.log.debug("Response: %s", response)
  206.         if 'error' in response:
  207.             raise UploadError(response['error'])
  208.         return response
  209.  
  210.     def sleep(self, seconds):
  211.         time.sleep(seconds)
  212.  
  213.     def _handle_captcha(self, error):
  214.         if self.captcha_handler:
  215.             solution = self.captcha_handler.handle(self.opener,
  216.                                                    error.captcha_img)
  217.             if solution is not None:
  218.                 return solution
  219.         raise error
  220.  
  221.     def copy(self, **kw):
  222.         clone = copy.copy(self)
  223.         clone.__dict__.update(kw)
  224.         return clone
  225.  
  226.     def __getattr__(self, name):
  227.         # users, auth, wall и т.д.
  228.         if re.match('[a-z]+$', name):
  229.             return _ComplexMethod(self, name)
  230.         raise AttributeError(name)
  231.  
  232.  
  233. class VKError(Exception):
  234.     pass
  235.  
  236.  
  237. class AuthError(VKError):
  238.     def __init__(self, details):
  239.         message = "{error_description} ({error})".format(**details)\
  240.                   if 'error_description' in details else details['error']
  241.         super().__init__(message)
  242.         self.name = details['error']
  243.         self.description = details.get('error_description')
  244.         self.captcha_img = details.get('captcha_img')
  245.         self.captcha_sid = details.get('captcha_sid')
  246.         self.redirect_uri = details.get('redirect_uri')
  247.  
  248.  
  249. class ApiError(VKError):
  250.     def __init__(self, details):
  251.         super().__init__("{error_code} - {error_msg}".format(**details))
  252.         self.code = details['error_code']
  253.         self.msg = details['error_msg']
  254.         self.captcha_img = details.get('captcha_img')
  255.         self.captcha_sid = details.get('captcha_sid')
  256.         self.redirect_uri = details.get('redirect_uri')
  257.         params = {x['key']: x['value'] for x in details['request_params']}
  258.         self.method = params.pop('method')
  259.         self.oauth = params.pop('oauth')
  260.         self.params = params
  261.  
  262.  
  263. class UploadError(VKError):
  264.     pass
  265.  
  266.  
  267. class _ComplexMethod:
  268.     def __init__(self, client, prefix):
  269.         self._client = client
  270.         self._prefix = prefix
  271.  
  272.     def __getattr__(self, name):
  273.         # Может принимать словарь либо именованные параметры
  274.         def wrapper(*arg, **kw):
  275.             # Если имя параметра совпадает с ключевым словом, добавляем к нему
  276.             # подчеркивание:
  277.             # <object VKClient>.storage.get(_global=1, ...)
  278.             params = {k[1:] if len(k) > 1 and k.startswith('_') else k: v
  279.                       for k, v in kw.items()}
  280.             if len(arg):
  281.                 d = dict(arg[0])
  282.                 d.update(params)
  283.                 params = d
  284.             method = '{}.{}'.format(self._prefix, name)
  285.             return self._client.api(method, params)
  286.         # e.g.: users.isAppUser
  287.         if re.match('[a-z]+([A-Z][a-z]+)*$', name):
  288.             return wrapper
  289.         raise AttributeError(name)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement