Skip to content

Commit

Permalink
Merge pull request #360 from Kegbot/mikey/plugin-fixes
Browse files Browse the repository at this point in the history
Fix plugins
  • Loading branch information
mik3y authored Jun 25, 2017
2 parents d493236 + 324aba9 commit 4f298f2
Show file tree
Hide file tree
Showing 21 changed files with 552 additions and 237 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ python:
- "2.7"
sudo: false

notifications:
email: false

cache: pip

services:
Expand Down
46 changes: 46 additions & 0 deletions pykeg/contrib/foursquare/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2017 Bevbot LLC, All Rights Reserved
#
# This file is part of the Pykeg package of the Kegbot project.
# For more information on Pykeg or Kegbot, see http://kegbot.org/
#
# Pykeg is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Pykeg is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pykeg. If not, see <http://www.gnu.org/licenses/>.

import foursquare


class FoursquareClient:
AUTHORIZATION_URL = 'https://foursquare.com/oauth2/authorize'
ACCESS_TOKEN_URL = 'https://foursquare.com/oauth2/token'

def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret

def get_authorization_url(self, redirect_uri):
fs = foursquare.Foursquare(client_id=self.client_id, client_secret=self.client_secret,
redirect_uri=redirect_uri)
return fs.oauth.auth_url()

def handle_authorization_callback(self, code, redirect_uri):
fs = foursquare.Foursquare(client_id=self.client_id, client_secret=self.client_secret,
redirect_uri=redirect_uri)
return fs.oauth.get_token(code)

def venues(self, venue_id):
fs = foursquare.Foursquare(client_id=self.client_id, client_secret=self.client_secret)
return fs.venues(venue_id)

def users(self, access_token):
fs = foursquare.Foursquare(access_token=access_token)
return fs.users()
11 changes: 5 additions & 6 deletions pykeg/contrib/foursquare/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
from pykeg.plugin import plugin
from pykeg.plugin import util

import foursquare

from . import forms
from . import tasks
from . import views
from .client import FoursquareClient

KEY_SITE_SETTINGS = 'settings'
KEY_CLIENT_ID = 'client_id'
Expand All @@ -50,8 +49,8 @@ def get_user_settings_view(self):

def get_extra_user_views(self):
return [
('redirect/$', 'pykeg.contrib.foursquare.views.auth_redirect', 'redirect'),
('callback/$', 'pykeg.contrib.foursquare.views.auth_callback', 'callback'),
('redirect/$', views.auth_redirect, 'redirect'),
('callback/$', views.auth_callback, 'callback'),
]

def handle_new_events(self, events):
Expand Down Expand Up @@ -103,9 +102,9 @@ def get_credentials(self):
data = self.get_site_settings()
return data.get('client_id'), data.get('client_secret')

def get_foursquare_client(self):
def get_client(self):
client_id, client_secret = self.get_credentials()
client = foursquare.Foursquare(client_id=client_id, client_secret=client_secret)
client = FoursquareClient(client_id, client_secret)
return client

def get_venue_id(self):
Expand Down
84 changes: 26 additions & 58 deletions pykeg/contrib/foursquare/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,14 @@
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from socialregistration.clients.oauth import OAuthError
from socialregistration.contrib.foursquare.client import Foursquare
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

from pykeg.web.decorators import staff_member_required
from kegbot.util import kbjson

import foursquare

from . import forms


class FoursquareClient(Foursquare):
def set_callback_url(self, url):
self.callback_url = url

def get_callback_url(self):
return self.callback_url
from . import client


@staff_member_required
Expand All @@ -54,23 +43,23 @@ def admin_settings(request, plugin):
venue_id = settings_form.cleaned_data.get('venue_id')
venue = None
if venue_id:
client = plugin.get_foursquare_client()
c = plugin.get_client()
try:
venue = client.venues(venue_id)
except foursquare.FoursquareException as e:
venue = c.venues(venue_id)
except client.FoursquareClientError as e:
messages.error(request, 'Error fetching venue information: %s' % str(e))
plugin.save_venue_detail(venue)
messages.success(request, 'Settings updated.')

if 'test-api' in request.POST:
plugin = request.plugins['foursquare']
client = plugin.get_foursquare_client()
c = plugin.get_client()
venue_id = plugin.get_venue_id() or '49d01698f964a520fd5a1fe3' # Golden Gate Bridge
try:
venue_info = client.venues(venue_id)
venue_info = c.venues(venue_id)
context['test_response'] = kbjson.dumps(venue_info, indent=2)
messages.success(request, 'API test successful.')
except foursquare.FoursquareException as e:
except client.FoursquareClientError as e:
messages.success(request, 'API test failed: {}'.format(e.message))

context['plugin'] = plugin
Expand Down Expand Up @@ -112,50 +101,29 @@ def auth_redirect(request):
return redirect('account-plugin-settings', plugin_name='foursquare')

plugin = request.plugins['foursquare']
client = get_client(*plugin.get_credentials())

url = request.build_absolute_uri(reverse('plugin-foursquare-callback'))
client.set_callback_url(url)

request.session['foursquare_client'] = client

try:
return redirect(client.get_redirect_url())
except OAuthError as error:
messages.error(request, 'Error: %s' % str(error))
return redirect('account-plugin-settings', plugin_name='foursquare')
client = plugin.get_client()
redirect_url = request.build_absolute_uri(reverse('plugin-foursquare-callback'))
url = client.get_authorization_url(redirect_url)
return redirect(url)


@login_required
def auth_callback(request):
try:
client = request.session['foursquare_client']
del request.session['foursquare_client']
token = client.complete(dict(request.GET.items()))
except KeyError:
messages.error(request, 'Session expired.')
except OAuthError as error:
messages.error(request, str(error))
plugin = request.plugins['foursquare']
client = plugin.get_client()
code = request.GET.get('code')
redirect_url = request.build_absolute_uri(reverse('plugin-foursquare-callback'))
token = client.handle_authorization_callback(code, redirect_url)

profile = client.users(token)
if not profile or not profile.get('user'):
messages.error(request, 'Unexpected profile response.')
else:
plugin = request.plugins.get('foursquare')
api_client = foursquare.Foursquare(access_token=token)
profile = api_client.users()
if not profile or not profile.get('user'):
messages.error(request, 'Unexpected profile response.')
else:
profile = profile['user']
token = client.get_access_token()
plugin.save_user_profile(request.user, profile)
plugin.save_user_token(request.user, token)

username = '%s %s' % (profile.get('firstName'), profile.get('lastName'))
messages.success(request, 'Successfully linked to foursquare user %s' % username)

return redirect('account-plugin-settings', plugin_name='foursquare')
profile = profile['user']
plugin.save_user_profile(request.user, profile)
plugin.save_user_token(request.user, token)

username = '%s %s' % (profile.get('firstName'), profile.get('lastName'))
messages.success(request, 'Successfully linked to foursquare user %s' % username)

def get_client(client_id, client_secret):
client = FoursquareClient()
client.client_id = client_id
client.secret = client_secret
return client
return redirect('account-plugin-settings', plugin_name='foursquare')
115 changes: 115 additions & 0 deletions pykeg/contrib/twitter/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright 2017 Bevbot LLC, All Rights Reserved
#
# This file is part of the Pykeg package of the Kegbot project.
# For more information on Pykeg or Kegbot, see http://kegbot.org/
#
# Pykeg is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Pykeg is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pykeg. If not, see <http://www.gnu.org/licenses/>.

import tweepy
from requests_oauthlib import OAuth1Session
from requests_oauthlib.oauth1_session import TokenMissing
from requests_oauthlib.oauth1_session import TokenRequestDenied

import requests


class TwitterClientError(Exception):
"""Base client error."""


class AuthError(TwitterClientError):
"""Wraps an error raised by oauthlib."""
def __init__(self, message, cause):
super(AuthError, self).__init__(message)
self.cause = cause


class RequestError(TwitterClientError):
"""Wraps an error raised by the request library."""
def __init__(self, message, cause):
super(RequestError, self).__init__(message)
self.cause = cause


class TwitterClient:
REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'

SESSION_RESOURCE_OWNER = 'twitter:resource_owner_key'
SESSION_RESOURCE_OWNER_SECRET = 'twitter:resource_owner_secret'

def __init__(self, client_key, client_secret):
self.client_key = client_key
self.client_secret = client_secret

def fetch_request_token(self, callback_uri):
"""Step 1 of flow: Get a request token."""
session = OAuth1Session(self.client_key,
client_secret=self.client_secret,
callback_uri=callback_uri)

try:
res = session.fetch_request_token(self.REQUEST_TOKEN_URL)
except requests.exceptions.RequestException, e:
raise RequestError('Request error fetching token.', e)
except (TokenRequestDenied, TokenMissing), e:
raise AuthError('Token request failed.', e)

request_token = res.get('oauth_token')
request_token_secret = res.get('oauth_token_secret')
return request_token, request_token_secret

def get_authorization_url(self, request_token, request_token_secret):
"""Step 2 of flow: Get an authorization url."""
session = OAuth1Session(self.client_key,
client_secret=self.client_secret,
resource_owner_key=request_token,
resource_owner_secret=request_token_secret)
return session.authorization_url(self.AUTHORIZATION_URL)

def get_redirect_url(self, callback_uri):
"""Convenience for steps 1 and 2."""
request_token, request_token_secret = self.fetch_request_token(callback_uri)
url = self.get_authorization_url(request_token, request_token_secret)
return url, request_token, request_token_secret

def handle_authorization_callback(self, request_token, request_token_secret, request=None, uri=None):
"""Step 3 of the flow: Parse the response and fetch token."""
session = OAuth1Session(self.client_key,
client_secret=self.client_secret,
resource_owner_key=request_token,
resource_owner_secret=request_token_secret)

if not uri:
uri = request.build_absolute_uri() + '?' + request.META.get('QUERY_STRING', '')
session.parse_authorization_response(uri)

try:
res = session.fetch_access_token(self.ACCESS_TOKEN_URL)
except requests.exceptions.RequestException, e:
raise RequestError('Request error fetching access token.', e)
except (TokenRequestDenied, TokenMissing), e:
raise AuthError('Auth error fetching access token.', e)

oauth_token = res.get('oauth_token')
oauth_token_secret = res.get('oauth_token_secret')
return oauth_token, oauth_token_secret

def get_user_info(self, access_token, access_token_secret):
auth = tweepy.OAuthHandler(self.client_key, self.client_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
me = api.me()
return me
Loading

0 comments on commit 4f298f2

Please sign in to comment.