@@ 18,7 18,7 @@ setup(
url='https://bitbucket.org/netlandish/pytinder/',
long_description=long_description,
platforms=['any'],
- install_requires=['requests>=2.6.0'],
+ install_requires=['requests>=2.6.0', 'beautifulsoup4>=4.4.1'],
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
@@ 0,0 1,151 @@
+# -*- coding: utf-8 -*-
+''' This module will emulate a FB login
+
+ Taken, and adapted, from django-oauth-tokens
+ https://github.com/ramusus/django-oauth-tokens
+'''
+import requests
+from bs4 import BeautifulSoup
+from abc import ABCMeta, abstractproperty, abstractmethod
+
+
+class AuthRequestBase(object):
+
+ __metaclass__ = ABCMeta
+
+ authorize_form_attributes = {}
+
+ session = None
+ headers = {}
+
+ @abstractproperty
+ def form_action_domain(self):
+ pass
+
+ def __init__(self, username, password):
+ self.session = requests.Session()
+
+ self.username = username
+ self.password = password
+
+ def authorized_request(self, method='get', **kwargs):
+ if method not in ['get', 'post']:
+ raise ValueError('Only `get` and `post` are allowed methods')
+
+ if not self.session.cookies:
+ self.authorize()
+
+ if self.session.cookies:
+ return getattr(self.session, method)(
+ headers=kwargs.pop('headers', self.headers),
+ **kwargs
+ )
+ else:
+ raise ValueError('Session cookies are not defined')
+
+ def authorize(self):
+ ''' Authorize and set self.session for next requests and return
+ response of last request
+ '''
+ response = self.session.get(self.login_url, headers=self.headers)
+
+ method, action, data = self.get_form_data_from_content(
+ response.content,
+ **self.authorize_form_attributes
+ )
+
+ # submit auth form data
+ return self.session.post(action, data, headers=self.headers)
+
+ def get_form_data(self, form):
+ data = {}
+ for input in form.findAll('input'):
+ if input.get('name'):
+ data[input.get('name')] = input.get('value')
+
+ self.add_data_credentials(data)
+
+ action = form.get('action')
+ if action[0] == '/':
+ action = self.form_action_domain + action
+
+ return (form.get('method').lower(), action, data)
+
+ @abstractmethod
+ def add_data_credentials(self, data):
+ pass
+
+ def get_form_data_from_content(self, content, **kwargs):
+ bs = BeautifulSoup(content)
+ form = self.get_form_from_bs_content(bs, **kwargs)
+ return self.get_form_data(form)
+
+ def get_form_from_bs_content(self, bs, **kwargs):
+ form = bs.find('form', **kwargs)
+ if not form:
+ raise Exception('There is no any form in response')
+ return form
+
+
+class FacebookAuthRequest(AuthRequestBase):
+ ''' Facebook authorized request class
+ '''
+ provider = 'facebook'
+ form_action_domain = 'https://m.facebook.com'
+ login_url = 'https://m.facebook.com/login.php'
+ headers = {
+ 'User-Agent': ('Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 '
+ '(KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 '
+ 'Chrome/34.0.1847.116 Safari/537.36'),
+ 'Upgrade-Insecure-Requests': 1,
+ 'Accept': ('text/html,application/xhtml+xml,application/xml;q=0.9,'
+ 'image/webp,*/*;q=0.8'),
+ 'Accept-Charset': 'utf-8;q=0.7,*;q=0.3',
+ 'Accept-Encoding': 'gzip,deflate,sdch',
+ 'Accept-Language': 'en-US,en;q=0.8',
+ 'Connection': 'keep-alive',
+ 'Host': 'www.facebook.com',
+ }
+
+ account_locked_phrases = [
+ 'Ваш аккаунт временно заблокирован',
+ ('Мы заблокировали ваш аккаунт в связи '
+ 'с попыткой входа из незнакомого '
+ 'места. Пожалуйста, помогите нам '
+ 'подтвердить, что попытка входа была i'
+ 'произведена вами.'),
+ 'Your account is temporarily locked.',
+ ]
+
+ def add_data_credentials(self, data):
+ data['email'] = self.username
+ data['pass'] = self.password
+
+ def authorize(self):
+ response = super(FacebookAuthRequest, self).authorize()
+
+ if 'Cookies Required' in response.content:
+ self.session.get(self.form_action_domain)
+ response = super(FacebookAuthRequest, self).authorize()
+ if 'Cookies Required' in response.content:
+ raise Exception("Facebook 'Cookies required' error")
+
+ if 'You are trying too often' in response.content:
+ raise Exception(('Facebook authorization request returns error '
+ '\'You are trying too often\''))
+
+ # TODO: move this to FacebookAcessToken class
+ if 'API Error Code: 191' in response.content:
+ raise ImproperlyConfigured(
+ ('You must specify URL \'{0}\' in your facebook application '
+ 'settings').format(self.redirect_uri)
+ )
+
+ for account_locked_phrase in self.account_locked_phrases:
+ if account_locked_phrase in response.content:
+ raise AccountLocked(
+ ('Facebook errored \'Your account is temporarily locked.\''
+ ' Try to login via web browser')
+ )
+
+ return response
@@ 1,9 1,11 @@
import os
+import re
import stat
import json
import datetime
import requests
from . import constants
+from .facebook import FacebookAuthRequest
def pull_date(date_str):
@@ 47,3 49,26 @@ def load_api_from_file(fname=constants.D
from . import api # Circular
auth = load_auth_from_file(fname)
return api.API(auth_handler=auth)
+
+
+def get_tinder_access_token(username, password):
+ ''' Helper to login to your FB account and authorize
+ the Tinder App to get an access token that will work
+ with the Tinder API.
+ '''
+ fb_url = ('https://www.facebook.com/dialog/oauth?client_id=464891386855067'
+ '&redirect_uri=https://www.facebook.com/connect/login_success.'
+ 'html&scope=basic_info,email,public_profile,user_about_me,'
+ 'user_activities,user_birthday,user_education_history,'
+ 'user_friends,user_interests,user_likes,user_location,'
+ 'user_photos,user_relationship_details&response_type=token')
+ req = FacebookAuthRequest(username=username, password=password)
+ response = req.authorized_request(url=fb_url)
+ if response.status_code != 200:
+ raise ValueError('Error logging in {0}'.format(response.content))
+
+ location = response.history[0].headers['location']
+ res = re.search(r'.*access_token=(\w+)\&.*', location)
+ if not res:
+ raise ValueError('Unable to get access key {0}'.format(location))
+ return res.groups()[0]