├── examples ├── __init__.py ├── info.py ├── link.py ├── me.py ├── get_token.py ├── update.py ├── profiles.py └── updates_and_profiles.py ├── buffpy ├── managers │ ├── __init__.py │ ├── profiles.py │ └── updates.py ├── tests │ ├── __init__.py │ ├── test_user.py │ ├── test_response.py │ ├── test_link.py │ ├── test_profile.py │ ├── test_profiles_manager.py │ ├── test_api.py │ ├── test_update.py │ └── test_updates_manager.py ├── __init__.py ├── models │ ├── __init__.py │ ├── user.py │ ├── link.py │ ├── profile.py │ └── update.py ├── test.py ├── response.py └── api.py ├── setup.cfg ├── .coveralls.yml ├── requirements.txt ├── .coveragerc ├── .travis.yml ├── .gitignore ├── setup.py ├── LICENSE.txt └── README.md /examples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /buffpy/managers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /buffpy/tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: NVwejx05F3aPdMbpt4AnddmSgxLVz9aIq 2 | -------------------------------------------------------------------------------- /buffpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import * 2 | from .response import * 3 | -------------------------------------------------------------------------------- /buffpy/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .link import Link 2 | from .profile import Profile 3 | from .user import User 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.2.7 2 | coverage==3.7.1 3 | mock==1.0.1 4 | nose==1.3.0 5 | rauth==0.6.2 6 | requests==1.2.3 7 | -------------------------------------------------------------------------------- /buffpy/test.py: -------------------------------------------------------------------------------- 1 | import nose 2 | 3 | if __name__ == "__main__": 4 | nose.main(argv=["tests=tests", "--with-coverage", "--cover-package=buffpy"]) 5 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | */python?.?/* 4 | */lib-python/?.?/*.py 5 | */lib_pypy/_*.py 6 | */site-packages/ordereddict.py 7 | */site-packages/nose/* 8 | */site-packages/* 9 | */unittest2/* 10 | /usr/* 11 | -------------------------------------------------------------------------------- /buffpy/models/user.py: -------------------------------------------------------------------------------- 1 | from buffpy.response import ResponseObject 2 | 3 | class User(ResponseObject): 4 | ''' 5 | A user represents a single Buffer user account. 6 | ''' 7 | 8 | REQUEST_URL = 'user.json' 9 | 10 | def __init__(self, api): 11 | response = api.get(url=self.REQUEST_URL) 12 | 13 | super(User, self).__init__(response) 14 | -------------------------------------------------------------------------------- /examples/info.py: -------------------------------------------------------------------------------- 1 | from buffpy.api import API 2 | 3 | # check http://bufferapp.com/developers/apps to retrieve a token 4 | # or generate one with the example 5 | token = 'awesome_token' 6 | 7 | # instantiate the api object 8 | api = API(client_id='client_id', 9 | client_secret='client_secret', 10 | access_token=token) 11 | 12 | # get api's info 13 | print api.info 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | # does not have headers provided, please ask https://launchpad.net/~pypy/+archive/ppa 5 | # maintainers to fix their pypy-dev package. 6 | - "pypy" 7 | # command to install dependencies 8 | install: 9 | - pip install -r requirements.txt 10 | - pip install python-coveralls 11 | # command to run tests 12 | script: nosetests --with-coverage --cover-package=buffpy 13 | after_success: 14 | coveralls 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /examples/link.py: -------------------------------------------------------------------------------- 1 | from buffpy.models.link import Link 2 | from buffpy.api import API 3 | 4 | # check http://bufferapp.com/developers/apps to retrieve a token 5 | # or generate one with the example 6 | token = 'awesome_tokne' 7 | 8 | # instantiate the api object 9 | api = API(client_id='client_id', 10 | client_secret='client_secret', 11 | access_token=token) 12 | 13 | # get a link's shares 14 | print Link(api=api, url='http%3A%2F%2Fbufferapp.com').shares 15 | -------------------------------------------------------------------------------- /buffpy/tests/test_user.py: -------------------------------------------------------------------------------- 1 | from mock import MagicMock, patch 2 | 3 | from buffpy.models.user import User 4 | 5 | def test_simple_user_request(): 6 | ''' 7 | Test to see if the api is called when a user request is made 8 | ''' 9 | 10 | mocked_api = MagicMock() 11 | mocked_response = { 12 | "key": "value" 13 | } 14 | mocked_api.get.return_value = mocked_response 15 | 16 | with patch('buffpy.models.user.ResponseObject') as mocked_response: 17 | User(api=mocked_api) 18 | 19 | mocked_api.get.assert_called_once_with(url='user.json') 20 | -------------------------------------------------------------------------------- /buffpy/tests/test_response.py: -------------------------------------------------------------------------------- 1 | from nose.tools import eq_ 2 | 3 | from buffpy.response import ResponseObject 4 | 5 | def test_reponse_check_for_inception(): 6 | ''' 7 | Given a dict with a dict, it should convert all the dicts to ResponseObject 8 | ''' 9 | 10 | awesome_dict = { 11 | 'key': 'value', 12 | 'second_dict': { 13 | 'key2': 'value2' 14 | } 15 | } 16 | 17 | response = ResponseObject(awesome_dict) 18 | response.key3 = 'value3' 19 | 20 | eq_(response.key, 'value') 21 | eq_(response.key3, 'value3') 22 | eq_(response.second_dict, {'key2': 'value2'}) 23 | eq_(response.second_dict.key2, 'value2') 24 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | requires_list = [ 4 | 'colorama==0.2.7', 5 | 'coverage==3.7.1', 6 | 'mock==1.0.1', 7 | 'nose==1.3.0', 8 | 'rauth==0.6.2', 9 | 'requests==1.2.3', 10 | ] 11 | 12 | setup(name='buffer-python', 13 | version='1.07', 14 | platforms='any', 15 | description='Python library for Buffer App', 16 | author='Vlad Temian', 17 | author_email='vladtemian@gmail.com', 18 | url='https://github.com/bufferapp/buffer-python', 19 | packages=['buffpy'], 20 | include_package_data=True, 21 | install_requires=requires_list, 22 | classifiers=[ 23 | 'Programming Language :: Python :: 2.7', 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /examples/me.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint as pp 2 | 3 | from colorama import Fore 4 | 5 | from buffpy.api import API 6 | from buffpy.models.user import User 7 | 8 | # check http://bufferapp.com/developers/apps to retrieve a token 9 | # or generate one with the example 10 | token = 'awesome_token' 11 | 12 | # instantiate the api object 13 | api = API(client_id='client_id', 14 | client_secret='client_secret', 15 | access_token=token) 16 | 17 | # instantiate an user object 18 | user = User(api=api) 19 | 20 | # now play! 21 | print Fore.YELLOW + '<< Just the basic user obj as dict >>\n' + Fore.RESET, user 22 | print Fore.YELLOW + '<< User\'s ID >>\n' + Fore.RESET, user.id 23 | print Fore.YELLOW + '<< User\'s timezone >>\n' + Fore.RESET, user.timezone 24 | -------------------------------------------------------------------------------- /buffpy/models/link.py: -------------------------------------------------------------------------------- 1 | from buffpy.response import ResponseObject 2 | 3 | PATHS = { 4 | 'GET_SHARES': 'links/shares.json?url=%s' 5 | } 6 | 7 | class Link(ResponseObject): 8 | ''' 9 | A link represents a unique URL that has been shared through Buffer 10 | ''' 11 | 12 | def __init__(self, api, url): 13 | shares = api.get(url=PATHS['GET_SHARES'] % url)['shares'] 14 | 15 | super(Link, self).__init__({'shares': shares, 'url': url}) 16 | self.api = api 17 | 18 | def get_shares(self): 19 | ''' 20 | Returns an object with a the numbers of shares a link has had using 21 | Buffer. 22 | 23 | www will be stripped, but other subdomains will not. 24 | ''' 25 | 26 | self.shares = self.api.get(url=PATHS['GET_SHARES'] % self.url)['shares'] 27 | 28 | return self.shares 29 | -------------------------------------------------------------------------------- /examples/get_token.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore 2 | 3 | from buffpy import AuthService 4 | 5 | client_id = 'add_apps_client_id' 6 | client_secret = 'add_apps_secret' 7 | 8 | print Fore.YELLOW + '---- START TOKEN RETRIEVING OPERATION ----' + Fore.RESET 9 | redirect_uri = 'add_your_redirect_uri(with http)' 10 | 11 | 12 | service = AuthService(client_id, client_secret, redirect_uri) 13 | 14 | url = service.authorize_url 15 | print Fore.GREEN + 'Access this url and retrieve the token: ' + Fore.RESET + url 16 | 17 | auth_code = raw_input(Fore.GREEN + 'Paste the code from the redirected url: ' + Fore.RESET) 18 | access_token = service.get_access_token(auth_code) 19 | print Fore.GREEN + 'Acess TOKEN: ' + Fore.RESET + access_token 20 | 21 | api = service.create_session(access_token) 22 | 23 | #Do stuff with your session 24 | -------------------------------------------------------------------------------- /buffpy/response.py: -------------------------------------------------------------------------------- 1 | class ResponseObject(dict): 2 | ''' 3 | Simple data structure that convert any dict to an empty object 4 | where all the atributes are the keys of the dict, but also preserve a dict 5 | behavior 6 | e.g: 7 | 8 | obj = ResponseObject({'a':'b'}) 9 | obj.key = 'value' 10 | 11 | obj.a => 'b' 12 | obj => {'a': 'b', 'key': 'value'} 13 | ''' 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(ResponseObject, self).__init__(*args, **kwargs) 17 | 18 | self.__dict__ = self._check_for_inception(self) 19 | 20 | def _check_for_inception(self, root_dict): 21 | ''' 22 | Used to check if there is a dict in a dict 23 | ''' 24 | 25 | for key in root_dict: 26 | if isinstance(root_dict[key], dict): 27 | root_dict[key] = ResponseObject(root_dict[key]) 28 | 29 | return root_dict 30 | -------------------------------------------------------------------------------- /examples/update.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint as pp 2 | 3 | from colorama import Fore 4 | 5 | from buffpy.models.update import Update 6 | from buffpy.managers.profiles import Profiles 7 | from buffpy.managers.updates import Updates 8 | from buffpy.api import API 9 | 10 | # check http://bufferapp.com/developers/apps to retrieve a token 11 | # or generate one with the example 12 | token = 'awesome_token' 13 | 14 | # instantiate the api object 15 | api = API(client_id='client_id', 16 | client_secret='client_secret', 17 | access_token=token) 18 | 19 | # retrieve a single update based on an id 20 | update = Update(api=api, id='update_id') 21 | print update 22 | 23 | # get update's interactions 24 | print update.interactions 25 | 26 | # edit 27 | update = update.edit(text="Hey!") 28 | 29 | # publish now 30 | update.publish() 31 | 32 | # move to top 33 | update.move_to_top() 34 | 35 | # delete 36 | update.delete() 37 | -------------------------------------------------------------------------------- /examples/profiles.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint as pp 2 | 3 | from colorama import Fore 4 | 5 | from buffpy.managers.profiles import Profiles 6 | from buffpy.api import API 7 | 8 | # check http://bufferapp.com/developers/apps to retrieve a token 9 | # or generate one with the example 10 | token = 'awesome_token' 11 | 12 | # instantiate the api object 13 | api = API(client_id='client_id', 14 | client_secret='client_secret', 15 | access_token=token) 16 | 17 | # get all profiles 18 | profiles = Profiles(api=api) 19 | print profiles.all() 20 | 21 | # filter profiles using some criteria 22 | profile = Profiles(api=api).filter(service='twitter')[0] 23 | print profile 24 | 25 | # get schedules of my twitter profile 26 | profile = Profiles(api=api).filter(service='twitter')[0] 27 | print profile.schedules 28 | 29 | # update schedules times for my twitter profile 30 | profile = Profiles(api=api).filter(service='twitter')[0] 31 | 32 | profile.schedules = { 33 | 'days': ['tue', 'thu'], 34 | 'times': ['13:45'] 35 | } 36 | -------------------------------------------------------------------------------- /buffpy/tests/test_link.py: -------------------------------------------------------------------------------- 1 | from nose.tools import eq_ 2 | from mock import MagicMock 3 | 4 | from buffpy.models.link import Link 5 | 6 | def test_links_shares(): 7 | ''' 8 | Test link's shares retrieving from constructor 9 | ''' 10 | 11 | mocked_api = MagicMock() 12 | mocked_api.get.return_value = {'shares': 123} 13 | 14 | link = Link(api=mocked_api, url='www.google.com') 15 | 16 | eq_(link, {'shares': 123, 'url': 'www.google.com', 'api': mocked_api}) 17 | mocked_api.get.assert_called_once_with(url='links/shares.json?url=www.google.com') 18 | 19 | def test_links_get_shares(): 20 | ''' 21 | Test link's shares retrieving method 22 | ''' 23 | 24 | mocked_api = MagicMock() 25 | mocked_api.get.return_value = {'shares': 123} 26 | 27 | link = Link(api=mocked_api, url='www.google.com') 28 | 29 | eq_(link, {'shares': 123, 'url': 'www.google.com', 'api': mocked_api}) 30 | eq_(link.get_shares(), 123) 31 | mocked_api.get.assert_any_call(url='links/shares.json?url=www.google.com') 32 | eq_(mocked_api.get.call_count, 2) 33 | -------------------------------------------------------------------------------- /examples/updates_and_profiles.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint as pp 2 | 3 | from colorama import Fore 4 | 5 | from buffpy.managers.profiles import Profiles 6 | from buffpy.managers.updates import Updates 7 | 8 | from buffpy.api import API 9 | 10 | # check http://bufferapp.com/developers/apps to retrieve a token 11 | # or generate one with the example 12 | token = 'awesome_token' 13 | 14 | # instantiate the api object 15 | api = API(client_id='client_id', 16 | client_secret='client_secret', 17 | access_token=token) 18 | 19 | # get all pending updates of a social network profile 20 | profile = Profiles(api=api).filter(service='twitter')[0] 21 | print profile.updates.pending 22 | 23 | # get all sent updates of a social network profile 24 | print profile.updates.sent 25 | 26 | # retrieve all update's interactions 27 | print profile.updates.sent[0].interactions 28 | 29 | # shuffle updates 30 | print profile.updates.shuffle(count=10) 31 | 32 | # reorder updates 33 | print profile.updates.reorder(['update_id']) 34 | 35 | # create an update 36 | print profile.updates.new("Hello there", now=True) 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 SimplySocial Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /buffpy/managers/profiles.py: -------------------------------------------------------------------------------- 1 | from buffpy.models.profile import PATHS, Profile 2 | 3 | class Profiles(list): 4 | ''' 5 | Manage profiles 6 | + all -> get all the profiles from buffer 7 | + filter -> wrapper for list filtering 8 | ''' 9 | 10 | def __init__(self, api, *args, **kwargs): 11 | super(Profiles, self).__init__(*args, **kwargs) 12 | 13 | self.api = api 14 | 15 | def all(self): 16 | ''' 17 | Get all social newtworks profiles 18 | ''' 19 | 20 | response = self.api.get(url=PATHS['GET_PROFILES']) 21 | 22 | for raw_profile in response: 23 | self.append(Profile(self.api, raw_profile)) 24 | 25 | return self 26 | 27 | def filter(self, **kwargs): 28 | ''' 29 | Based on some criteria, filter the profiles and return a new Profiles 30 | Manager containing only the chosen items 31 | 32 | If the manager doen't have any items, get all the profiles from Buffer 33 | ''' 34 | 35 | if not len(self): 36 | self.all() 37 | 38 | new_list = filter(lambda item: [True for arg in kwargs if item[arg] == kwargs[arg]] != [], self) 39 | 40 | return Profiles(self.api, new_list) 41 | -------------------------------------------------------------------------------- /buffpy/tests/test_profile.py: -------------------------------------------------------------------------------- 1 | from nose.tools import eq_ 2 | from mock import MagicMock, patch 3 | 4 | from buffpy.models.profile import Profile, PATHS 5 | 6 | mocked_response = { 7 | 'name': 'me', 8 | 'service': 'twiter', 9 | 'id': 1 10 | } 11 | 12 | def test_profile_schedules_getter(): 13 | ''' 14 | Test schedules gettering from buffer api 15 | ''' 16 | 17 | mocked_api = MagicMock() 18 | mocked_api.get.return_value = '123' 19 | 20 | profile = Profile(mocked_api, mocked_response) 21 | 22 | eq_(profile.schedules, '123') 23 | mocked_api.get.assert_called_once_with(url = PATHS['GET_SCHEDULES'] % 1) 24 | 25 | def test_profile_schedules_setter(): 26 | ''' 27 | Test schedules setter from buffer api 28 | ''' 29 | 30 | mocked_api = MagicMock() 31 | mocked_api.get.return_value = '123' 32 | 33 | profile = Profile(mocked_api, mocked_response) 34 | 35 | profile.schedules = { 36 | 'times': ['mo'] 37 | } 38 | 39 | mocked_api.post.assert_called_once_with(url=PATHS['UPDATE_SCHEDULES'] % 1, 40 | data='schedules[0][times][]=mo&') 41 | 42 | def test_profile_updates(): 43 | ''' 44 | Test updates relationship with a profile 45 | ''' 46 | 47 | mocked_api = MagicMock() 48 | 49 | with patch('buffpy.models.profile.Updates') as mocked_updates: 50 | profile = Profile(api=mocked_api, raw_response={'id': 1}) 51 | updates = profile.updates 52 | 53 | mocked_updates.assert_called_once_with(api=mocked_api, profile_id=1) 54 | -------------------------------------------------------------------------------- /buffpy/tests/test_profiles_manager.py: -------------------------------------------------------------------------------- 1 | from nose.tools import eq_ 2 | from mock import MagicMock, patch 3 | 4 | from buffpy.managers.profiles import Profiles 5 | from buffpy.models.profile import Profile, PATHS 6 | 7 | mocked_response = { 8 | 'name': 'me', 9 | 'service': 'twiter', 10 | 'id': 1 11 | } 12 | 13 | def test_profiles_manager_all_method(): 14 | ''' 15 | Test basic profiles retrieving 16 | ''' 17 | 18 | mocked_api = MagicMock() 19 | mocked_api.get.return_value = [{'a':'b'}] 20 | 21 | with patch('buffpy.managers.profiles.Profile') as mocked_profile: 22 | mocked_profile.return_value = 1 23 | 24 | profiles = Profiles(api=mocked_api).all() 25 | 26 | eq_(profiles, [1]) 27 | mocked_api.get.assert_called_once_with(url=PATHS['GET_PROFILES']) 28 | mocked_profile.assert_called_once_with(mocked_api, {'a': 'b'}) 29 | 30 | def test_profiles_manager_filter_method(): 31 | ''' 32 | Test basic profiles filtering based on some minimal criteria 33 | ''' 34 | 35 | mocked_api = MagicMock() 36 | 37 | profiles = Profiles(mocked_api, [{'a':'b'}, {'a': 'c'}]) 38 | 39 | eq_(profiles.filter(a='b'), [{'a': 'b'}]) 40 | 41 | def test_profiles_manager_filter_method_empty(): 42 | ''' 43 | Test basic profiles filtering when the manager is empty 44 | ''' 45 | 46 | mocked_api = MagicMock() 47 | mocked_api.get.return_value = [{'a':'b'}, {'a': 'c'}] 48 | 49 | 50 | profiles = Profiles(api=mocked_api) 51 | 52 | eq_(profiles.filter(a='b'), [Profile(mocked_api, {'a': 'b'})]) 53 | -------------------------------------------------------------------------------- /buffpy/models/profile.py: -------------------------------------------------------------------------------- 1 | from buffpy.response import ResponseObject 2 | from buffpy.managers.updates import Updates 3 | 4 | PATHS = { 5 | 'GET_PROFILES': 'profiles.json', 6 | 'GET_PROFILE': 'profiles/%s.json', 7 | 'GET_SCHEDULES': 'profiles/%s/schedules.json', 8 | 'UPDATE_SCHEDULES': 'profiles/%s/schedules/update.json' 9 | } 10 | 11 | class Profile(ResponseObject): 12 | ''' 13 | A Buffer profile represents a connection to a single social media account. 14 | ''' 15 | 16 | def __init__(self, api, raw_response): 17 | super(Profile, self).__init__(raw_response) 18 | 19 | self.api = api 20 | self.__schedules = None 21 | 22 | @property 23 | def schedules(self): 24 | ''' 25 | Returns details of the posting schedules associated with a social media 26 | profile. 27 | ''' 28 | 29 | url = PATHS['GET_SCHEDULES'] % self.id 30 | 31 | self.__schedules = self.api.get(url=url) 32 | 33 | return self.__schedules 34 | 35 | @schedules.setter 36 | def schedules(self, schedules): 37 | ''' 38 | Set the posting schedules for the specified social media profile. 39 | ''' 40 | 41 | url = PATHS['UPDATE_SCHEDULES'] % self.id 42 | 43 | data_format = "schedules[0][%s][]=%s&" 44 | post_data = "" 45 | 46 | for format_type, values in schedules.iteritems(): 47 | for value in values: 48 | post_data += data_format % (format_type, value) 49 | 50 | self.api.post(url=url, data=post_data) 51 | 52 | @property 53 | def updates(self): 54 | return Updates(api=self.api, profile_id=self.id) 55 | -------------------------------------------------------------------------------- /buffpy/models/update.py: -------------------------------------------------------------------------------- 1 | from buffpy.response import ResponseObject 2 | 3 | PATHS = { 4 | 'GET_UPDATE': 'updates/%s.json', 5 | 'GET_INTERACTIONS': 'updates/%s/interactions.json', 6 | 'EDIT': 'updates/%s/update.json', 7 | 'PUBLISH': 'updates/%s/share.json', 8 | 'DELETE': 'updates/%s/destroy.json', 9 | 'MOVE_TO_TOP': 'updates/%s/move_to_top.json', 10 | } 11 | 12 | class Update(ResponseObject): 13 | ''' 14 | An update represents a single post to a single social media account. An 15 | update can also include media attachments such as pictures and links. 16 | ''' 17 | 18 | def __init__(self, api, id=None, raw_response=None): 19 | if id and not raw_response: 20 | raw_response = api.get(url=PATHS['GET_UPDATE'] % id) 21 | 22 | super(Update, self).__init__(raw_response) 23 | 24 | self.api = api 25 | self.__interactions = [] 26 | 27 | @property 28 | def interactions(self): 29 | ''' 30 | Returns the detailed information on individual interactions with the social 31 | media update such as favorites, retweets and likes. 32 | ''' 33 | 34 | interactions = [] 35 | url = PATHS['GET_INTERACTIONS'] % self.id 36 | 37 | response = self.api.get(url=url) 38 | for interaction in response['interactions']: 39 | interactions.append(ResponseObject(interaction)) 40 | 41 | self.__interactions = interactions 42 | 43 | return self.__interactions 44 | 45 | def edit(self, text, media=None, utc=None, now=None): 46 | ''' 47 | Edit an existing, individual status update. 48 | ''' 49 | 50 | url = PATHS['EDIT'] % self.id 51 | 52 | post_data = "text=%s&" % text 53 | 54 | if now: 55 | post_data += "now=%s&" % now 56 | 57 | if utc: 58 | post_data += "utc=%s&" % utc 59 | 60 | if media: 61 | media_format = "media[%s]=%s&" 62 | 63 | for media_type, media_item in media.iteritems(): 64 | post_data += media_format % (media_type, media_item) 65 | 66 | response = self.api.post(url=url, data=post_data) 67 | 68 | return Update(api=self.api, raw_response=response['update']) 69 | 70 | def publish(self): 71 | ''' 72 | Immediately shares a single pending update and recalculates times for 73 | updates remaining in the queue. 74 | ''' 75 | 76 | url = PATHS['PUBLISH'] % self.id 77 | return self.api.post(url=url) 78 | 79 | def delete(self): 80 | ''' 81 | Permanently delete an existing status update. 82 | ''' 83 | 84 | url = PATHS['DELETE'] % self.id 85 | return self.api.post(url=url) 86 | 87 | def move_to_top(self): 88 | ''' 89 | Move an existing status update to the top of the queue and recalculate 90 | times for all updates in the queue. Returns the update with its new 91 | posting time. 92 | ''' 93 | 94 | url = PATHS['MOVE_TO_TOP'] % self.id 95 | 96 | response = self.api.post(url=url) 97 | return Update(api=self.api, raw_response=response) 98 | -------------------------------------------------------------------------------- /buffpy/tests/test_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from nose.tools import eq_, raises 4 | from mock import MagicMock, patch 5 | 6 | from buffpy.api import API 7 | 8 | def test_api_get_request(): 9 | ''' 10 | Test simply api get request 11 | ''' 12 | 13 | with patch('buffpy.api.OAuth2Session') as mocked_oauth2: 14 | mocked_session = MagicMock() 15 | 16 | mocked_response = MagicMock() 17 | mocked_response.content = json.dumps({'status': 'ok'}) 18 | mocked_session.get.return_value = mocked_response 19 | 20 | mocked_oauth2.return_value = mocked_session 21 | 22 | api = API(client_id='1', client_secret='2', access_token='access_token') 23 | api.get(url="hey") 24 | 25 | mocked_session.get.assert_called_once_with(url='https://api.bufferapp.com/1/hey') 26 | 27 | @raises(ValueError) 28 | def test_api_get_request_no_access_token(): 29 | ''' 30 | Test simply api get request without access_token 31 | ''' 32 | 33 | with patch('buffpy.api.OAuth2Session') as mocked_oauth2: 34 | mocked_session = MagicMock() 35 | mocked_session.access_token = None 36 | 37 | mocked_oauth2.return_value = mocked_session 38 | 39 | api = API(client_id='1', client_secret='2') 40 | api.get(url="hey") 41 | 42 | def test_api_post_request(): 43 | ''' 44 | Test simply api post request 45 | ''' 46 | 47 | with patch('buffpy.api.OAuth2Session') as mocked_oauth2: 48 | mocked_session = MagicMock() 49 | 50 | mocked_response = MagicMock() 51 | mocked_response.content = json.dumps({'status': 'ok'}) 52 | mocked_session.post.return_value = mocked_response 53 | 54 | mocked_oauth2.return_value = mocked_session 55 | 56 | api = API(client_id='1', client_secret='2', access_token='access_token') 57 | api.post(url='hey', data='new=True') 58 | 59 | headers = {'Content-Type':'application/x-www-form-urlencoded'} 60 | mocked_session.post.assert_called_once_with( 61 | url='https://api.bufferapp.com/1/hey', headers=headers, data='new=True') 62 | 63 | @raises(ValueError) 64 | def test_api_post_request_no_access_token(): 65 | ''' 66 | Test simply api post request without access_token 67 | ''' 68 | 69 | with patch('buffpy.api.OAuth2Session') as mocked_oauth2: 70 | mocked_session = MagicMock() 71 | 72 | mocked_session.access_token = None 73 | 74 | mocked_oauth2.return_value = mocked_session 75 | 76 | api = API(client_id='1', client_secret='2', access_token='access_token') 77 | api.post(url='hey', data='new=True') 78 | 79 | def test_api_info(): 80 | ''' 81 | Test simple configuration retrieving 82 | ''' 83 | 84 | with patch('buffpy.api.OAuth2Session') as mocked_oauth2: 85 | mocked_session = MagicMock() 86 | 87 | mocked_response = MagicMock() 88 | mocked_response.content = json.dumps({'status': 'ok'}) 89 | mocked_session.get.return_value = mocked_response 90 | 91 | mocked_oauth2.return_value = mocked_session 92 | 93 | api = API(client_id='1', client_secret='2', access_token='access_token') 94 | info = api.info 95 | 96 | url = 'https://api.bufferapp.com/1/info/configuration.json' 97 | mocked_session.get.assert_called_once_with(url=url) 98 | eq_(info.status, 'ok') 99 | -------------------------------------------------------------------------------- /buffpy/tests/test_update.py: -------------------------------------------------------------------------------- 1 | from nose.tools import eq_ 2 | from mock import MagicMock 3 | 4 | from buffpy.models.update import Update, PATHS 5 | 6 | def test_update_retrieving(): 7 | ''' 8 | Test basic update retrieving based on update's id 9 | ''' 10 | 11 | mocked_api = MagicMock() 12 | mocked_api.get.return_value = { 13 | 'text': 'me', 14 | 'id': 1 15 | } 16 | 17 | update = Update(api=mocked_api, id=1) 18 | 19 | mocked_api.get.assert_called_once_with(url='updates/1.json') 20 | eq_(update.api, mocked_api) 21 | eq_(update.text, 'me') 22 | 23 | def test_update_interactions(): 24 | ''' 25 | Test basic analytics retrieving 26 | ''' 27 | 28 | mocked_api = MagicMock() 29 | mocked_api.get.return_value = {'interactions': [{'replies': 3}]} 30 | 31 | update = Update(mocked_api, raw_response={'id': 1, 'text': 'hey'}) 32 | 33 | eq_(update.interactions, [{'replies': 3}]) 34 | mocked_api.get.assert_called_once_with(url='updates/1/interactions.json') 35 | 36 | def test_update_edit(): 37 | ''' 38 | Test basic update editing 39 | ''' 40 | 41 | mocked_api = MagicMock() 42 | mocked_api.post.return_value = { 43 | 'update': {'id': 1, 'text': 'hey!'} 44 | } 45 | 46 | update = Update(mocked_api, raw_response={'id':1, 'text': 'ola!'}) 47 | new_update = update.edit(text='hey!') 48 | 49 | assert_update = Update(mocked_api, raw_response={'id':1, 'text': 'hey!'}) 50 | 51 | post_data = 'text=hey!&' 52 | mocked_api.post.assert_called_once_with(url='updates/1/update.json', 53 | data=post_data) 54 | eq_(new_update, assert_update) 55 | 56 | def test_update_edit_params(): 57 | ''' 58 | Test basic update editing with all the params 59 | ''' 60 | 61 | mocked_api = MagicMock() 62 | mocked_api.post.return_value = { 63 | 'update': {'id': 1, 'text': 'hey!'} 64 | } 65 | 66 | update = Update(mocked_api, raw_response={'id':1, 'text': 'ola!'}) 67 | new_update = update.edit(text='hey!', media={'link':'w'}, utc="a", now=True) 68 | 69 | assert_update = Update(mocked_api, raw_response={'id':1, 'text': 'hey!'}) 70 | 71 | post_data = 'text=hey!&now=True&utc=a&media[link]=w&' 72 | mocked_api.post.assert_called_once_with(url='updates/1/update.json', 73 | data=post_data) 74 | eq_(new_update, assert_update) 75 | 76 | def test_udpate_publishing(): 77 | ''' 78 | Test basic update publishing 79 | ''' 80 | 81 | mocked_api = MagicMock() 82 | mocked_api.post.return_value = True 83 | 84 | response = Update(api=mocked_api, raw_response={'id': 1}).publish() 85 | 86 | mocked_api.post.assert_called_once_with(url="updates/1/share.json") 87 | eq_(response, True) 88 | 89 | def test_update_deleting(): 90 | ''' 91 | Test update's deleting 92 | ''' 93 | 94 | mocked_api = MagicMock() 95 | mocked_api.post.return_value = True 96 | 97 | response = Update(api=mocked_api, raw_response={'id': 1}).delete() 98 | 99 | mocked_api.post.assert_called_once_with(url='updates/1/destroy.json') 100 | eq_(response, True) 101 | 102 | def test_update_move_to_top(): 103 | ''' 104 | Test move_to_top implementation 105 | ''' 106 | 107 | mocked_api = MagicMock() 108 | mocked_api.post.return_value = {'id': 1, 'text': 'hey'} 109 | 110 | response = Update(api=mocked_api, raw_response={'id': 1}).move_to_top() 111 | 112 | mocked_api.post.assert_called_once_with(url='updates/1/move_to_top.json') 113 | eq_(response.text, 'hey') 114 | -------------------------------------------------------------------------------- /buffpy/api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib 3 | 4 | from rauth import OAuth2Session, OAuth2Service 5 | 6 | from buffpy.response import ResponseObject 7 | 8 | 9 | BASE_URL = 'https://api.bufferapp.com/1/%s' 10 | PATHS = { 11 | 'INFO': 'info/configuration.json' 12 | } 13 | 14 | AUTHORIZE_URL = 'https://bufferapp.com/oauth2/authorize' 15 | ACCESS_TOKEN = 'https://api.bufferapp.com/1/oauth2/token.json' 16 | 17 | 18 | class API(object): 19 | ''' 20 | Small and clean class that embrace all basic 21 | operations with the buffer app 22 | ''' 23 | 24 | def __init__(self, client_id, client_secret, access_token=None): 25 | self.session = OAuth2Session(client_id=client_id, 26 | client_secret=client_secret, 27 | access_token=access_token) 28 | 29 | @property 30 | def access_token(self): 31 | return self.session.access_token 32 | 33 | @access_token.setter 34 | def access_token(self, value): 35 | self.session.access_token = value 36 | 37 | def get(self, url, parser=None): 38 | if parser is None: 39 | parser = json.loads 40 | 41 | if not self.session.access_token: 42 | raise ValueError('Please set an access token first!') 43 | 44 | response = self.session.get(url=BASE_URL % url) 45 | 46 | return parser(response.content) 47 | 48 | def post(self, url, parser=None, **params): 49 | if parser is None: 50 | parser = json.loads 51 | 52 | if not self.session.access_token: 53 | raise ValueError('Please set an access token first!') 54 | 55 | headers = {'Content-Type': 'application/x-www-form-urlencoded'} 56 | 57 | response = self.session.post(url=BASE_URL % url, headers=headers, **params) 58 | 59 | return parser(response.content) 60 | 61 | @property 62 | def info(self): 63 | ''' 64 | Returns an object with the current configuration that Buffer is using, 65 | including supported services, their icons and the varying limits of 66 | character and schedules. 67 | 68 | The services keys map directly to those on profiles and updates so that 69 | you can easily show the correct icon or calculate the correct character 70 | length for an update. 71 | ''' 72 | 73 | response = self.get(url=PATHS['INFO']) 74 | return ResponseObject(response) 75 | 76 | 77 | class AuthService(object): 78 | 79 | def __init__(self, client_id, client_secret, redirect_uri): 80 | self.outh_service = OAuth2Service(client_id=client_id, 81 | client_secret=client_secret, 82 | name='buffer', 83 | authorize_url=AUTHORIZE_URL, 84 | access_token_url=ACCESS_TOKEN, 85 | base_url=BASE_URL % '') 86 | 87 | self.redirect_uri = redirect_uri 88 | 89 | def create_session(self, access_token=None): 90 | return self.outh_service.get_session(access_token) 91 | 92 | def get_access_token(self, auth_code): 93 | auth_code = urllib.unquote(auth_code).decode('utf8') 94 | data = {'code': auth_code, 95 | 'grant_type': 'authorization_code', 96 | 'redirect_uri': self.redirect_uri} 97 | 98 | return self.outh_service.get_access_token(data=data, decoder=json.loads) 99 | 100 | @property 101 | def authorize_url(self): 102 | return self.outh_service.get_authorize_url(response_type='code', redirect_uri=self.redirect_uri) 103 | -------------------------------------------------------------------------------- /buffpy/managers/updates.py: -------------------------------------------------------------------------------- 1 | from buffpy.models.update import Update 2 | import urllib 3 | 4 | PATHS = { 5 | 'GET_PENDING': 'profiles/%s/updates/pending.json', 6 | 'GET_SENT': 'profiles/%s/updates/sent.json', 7 | 'SHUFFLE': 'profiles/%s/updates/shuffle.json', 8 | 'REORDER': 'profiles/%s/updates/reorder.json', 9 | 'CREATE': 'updates/create.json', 10 | } 11 | 12 | class Updates(list): 13 | ''' 14 | Implenents all the profiles+updates logic. 15 | 16 | + retrieve updates related to a profile 17 | + create a new update 18 | + reorder and shuffle updates 19 | ''' 20 | 21 | def __init__(self, api, profile_id): 22 | self.api = api 23 | self.profile_id = profile_id 24 | 25 | self.__pending = [] 26 | self.__sent = [] 27 | 28 | @property 29 | def pending(self): 30 | ''' 31 | Returns an array of updates that are currently in the buffer for an 32 | individual social media profile. 33 | ''' 34 | 35 | pending_updates = [] 36 | url = PATHS['GET_PENDING'] % self.profile_id 37 | 38 | response = self.api.get(url=url) 39 | for update in response['updates']: 40 | pending_updates.append(Update(api=self.api, raw_response=update)) 41 | 42 | self.__pending = pending_updates 43 | 44 | return self.__pending 45 | 46 | @property 47 | def sent(self): 48 | ''' 49 | Returns an array of updates that have been sent from the buffer for an 50 | individual social media profile. 51 | ''' 52 | 53 | sent_updates = [] 54 | url = PATHS['GET_SENT'] % self.profile_id 55 | 56 | response = self.api.get(url=url) 57 | for update in response['updates']: 58 | sent_updates.append(Update(api=self.api, raw_response=update)) 59 | 60 | self.__sent = sent_updates 61 | 62 | return self.__sent 63 | 64 | def shuffle(self, count=None, utc=None): 65 | ''' 66 | Randomize the order at which statuses for the specified social media 67 | profile will be sent out of the buffer. 68 | ''' 69 | 70 | url = PATHS['SHUFFLE'] % self.profile_id 71 | 72 | post_data = '' 73 | if count: 74 | post_data += 'count=%s&' % count 75 | if utc: 76 | post_data += 'utc=%s' % utc 77 | 78 | return self.api.post(url=url, data=post_data) 79 | 80 | def reorder(self, updates_ids, offset=None, utc=None): 81 | ''' 82 | Edit the order at which statuses for the specified social media profile will 83 | be sent out of the buffer. 84 | ''' 85 | 86 | url = PATHS['REORDER'] % self.profile_id 87 | 88 | order_format = "order[]=%s&" 89 | post_data = '' 90 | 91 | if offset: 92 | post_data += 'offset=%s&' % offset 93 | 94 | if utc: 95 | post_data += 'utc=%s&' % utc 96 | 97 | for update in updates_ids: 98 | post_data += order_format % update 99 | 100 | return self.api.post(url=url, data=post_data) 101 | 102 | #TODO: Multiple profile posting 103 | def new(self, text, shorten=None, now=None, top=None, media=None, when=None): 104 | ''' 105 | Create one or more new status updates. 106 | ''' 107 | 108 | url = PATHS['CREATE'] 109 | 110 | post_data = "text=%s&" % urllib.quote(text.encode("utf-8")) 111 | post_data += "profile_ids[]=%s&" % self.profile_id 112 | 113 | if shorten: 114 | post_data += "shorten=%s&" % shorten 115 | 116 | if now: 117 | post_data += "now=%s&" % now 118 | 119 | if top: 120 | post_data += "top=%s&" % top 121 | 122 | if when: 123 | post_data += "scheduled_at=%s&" % str(when) 124 | 125 | if media: 126 | media_format = "media[%s]=%s&" 127 | 128 | for media_type, media_item in media.iteritems(): 129 | post_data += media_format % (media_type, urllib.quote(media_item.encode("utf-8"))) 130 | 131 | response = self.api.post(url=url, data=post_data) 132 | new_update = Update(api=self.api, raw_response=response['updates'][0]) 133 | 134 | self.append(new_update) 135 | 136 | return new_update 137 | -------------------------------------------------------------------------------- /buffpy/tests/test_updates_manager.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from nose.tools import eq_, raises 4 | from mock import MagicMock, patch 5 | 6 | from buffpy.managers.updates import Updates 7 | from buffpy.models.update import Update 8 | 9 | def test_updates_manager_pending(): 10 | ''' 11 | Test basic pending updates retrieving 12 | ''' 13 | 14 | mocked_api = MagicMock() 15 | mocked_api.get.return_value = { 16 | 'updates': [{ 17 | 'text': 'hey' 18 | }] 19 | } 20 | 21 | pendings = Updates(api=mocked_api, profile_id=1).pending 22 | 23 | mocked_api.get.assert_called_once_with(url='profiles/1/updates/pending.json') 24 | eq_(pendings, [Update(api=mocked_api, raw_response={'text': 'hey'})]) 25 | 26 | def test_udpates_manager_sent(): 27 | ''' 28 | Test basic sent updates retrieving 29 | ''' 30 | 31 | mocked_api = MagicMock() 32 | mocked_api.get.return_value = { 33 | 'updates': [{ 34 | 'text': 'sent' 35 | }] 36 | } 37 | 38 | sent = Updates(api=mocked_api, profile_id=1).sent 39 | assert_update = Update(api=mocked_api, raw_response={'text': 'sent'}) 40 | 41 | mocked_api.get.assert_called_once_with(url='profiles/1/updates/sent.json') 42 | eq_(sent, [assert_update]) 43 | 44 | def test_udpates_manager_suffle(): 45 | ''' 46 | Test basic updates shuffle 47 | ''' 48 | 49 | mocked_api = MagicMock() 50 | mocked_api.post.return_value = True 51 | 52 | updates = Updates(api=mocked_api, profile_id=1).shuffle() 53 | 54 | mocked_api.post.assert_called_once_with(url='profiles/1/updates/shuffle.json', data='') 55 | eq_(updates, True) 56 | 57 | def test_udpates_manager_suffle_with_params(): 58 | ''' 59 | Test updates shuffling with count and utc params 60 | ''' 61 | 62 | mocked_api = MagicMock() 63 | mocked_api.post.return_value = True 64 | 65 | updates = Updates(api=mocked_api, profile_id=1).shuffle(count=10, utc='hey') 66 | 67 | data = "count=10&utc=hey" 68 | mocked_api.post.assert_called_once_with(url='profiles/1/updates/shuffle.json', 69 | data=data) 70 | eq_(updates, True) 71 | 72 | def test_updates_manager_reorder(): 73 | ''' 74 | Test basic updates reorder 75 | ''' 76 | 77 | mocked_api = MagicMock() 78 | mocked_api.post.return_value = True 79 | 80 | updates = Updates(api=mocked_api, profile_id=1).reorder([1, 2]) 81 | 82 | data = "order[]=1&order[]=2&" 83 | mocked_api.post.assert_called_once_with(url='profiles/1/updates/reorder.json', 84 | data=data) 85 | 86 | def test_updates_manager_reorder_with_params(): 87 | ''' 88 | Test basic updates reorder with params 89 | ''' 90 | 91 | mocked_api = MagicMock() 92 | mocked_api.post.return_value = True 93 | 94 | updates = Updates(api=mocked_api, profile_id=1).reorder([1, 2], 10, 'hey') 95 | 96 | data = "offset=10&utc=hey&order[]=1&order[]=2&" 97 | mocked_api.post.assert_called_once_with(url='profiles/1/updates/reorder.json', 98 | data=data) 99 | 100 | def test_updates_manager_new_update(): 101 | ''' 102 | Test update creation 103 | ''' 104 | 105 | mocked_api = MagicMock() 106 | mocked_api.post.return_value = {'updates': [{'text': 'hey'}]} 107 | 108 | updates = Updates(api=mocked_api, profile_id=1) 109 | update = updates.new("hey") 110 | 111 | data = "text=hey&profile_ids[]=1&" 112 | mocked_api.post.assert_called_once_with(url='updates/create.json', data=data) 113 | 114 | assert_update = Update(api=mocked_api, raw_response={'text': 'hey'}) 115 | eq_(update, assert_update) 116 | assert assert_update in updates 117 | 118 | def test_updates_manager_new_update_all_params(): 119 | ''' 120 | Test update creation with all params 121 | ''' 122 | 123 | mocked_api = MagicMock() 124 | 125 | raw_update = { 126 | 'text': 'hey', 127 | 'shorten': True, 128 | 'now': True, 129 | 'top': True, 130 | 'media': { 131 | 'link': 'www.google.com', 132 | 'photo': 'www.google.ro' 133 | } 134 | } 135 | update = Updates(api=mocked_api, profile_id=1).new(**raw_update) 136 | 137 | data = "text=hey&profile_ids[]=1&shorten=True&now=True&top=True&"+\ 138 | "media[photo]=www.google.ro&media[link]=www.google.com&" 139 | mocked_api.post.assert_called_once_with(url='updates/create.json', data=data) 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | buffer-app-python (buffpy) 2 | ========================== 3 | Simple to use python library for Buffer App 4 | 5 | [![Build Status](https://travis-ci.org/vtemian/buffpy.png?branch=master)](https://travis-ci.org/vtemian/buffpy) [![Coverage Status](https://coveralls.io/repos/vtemian/buffpy/badge.png?branch=master)](https://coveralls.io/r/vtemian/buffpy?branch=master) 6 | [![Downloads](https://pypip.in/d/buffpy/badge.png)](https://crate.io/packages/buffpy/) 7 | 8 | ### ORM`ish 9 | ------------ 10 | Bufferapp.com details some useful entities: 11 | * user 12 | * profile 13 | * update 14 | * link 15 | * info 16 | 17 | Every entity can be seen as an object that has attributes and methods. Those 18 | methods and attributes are linked to certain endpoints. 19 | 20 | All objects are special dicts. For example, you can do something like: 21 | ```python 22 | user.id => '12455678976asd' 23 | user => {...} 24 | ``` 25 | 26 | If you want to see more complete examples, click [here](../master/examples) 27 | 28 | #### Authorization 29 | ------------------ 30 | Get access_token using buffer [docs](https://bufferapp.com/developers/api/oauth) 31 | 32 | ```python 33 | 34 | service = AuthService(client_id, client_secret, redirect_uri) 35 | 36 | url = service.authorize_url 37 | 38 | # Access the url and retrieve the token 39 | auth_code = #Paste the code from the redirected url 40 | 41 | access_token = service.get_access_token(auth_code) 42 | 43 | api = service.create_session(access_token) 44 | ``` 45 | 46 | #### User 47 | ---------- 48 | A user represents a single Buffer user account. 49 | 50 | ```python 51 | 52 | api = API(client_id='client_id', 53 | client_secret='client_secret', 54 | access_token='access_token') 55 | 56 | # instantiate an user object 57 | user = User(api=api) 58 | 59 | print user 60 | print user.id 61 | print user.timezone 62 | ``` 63 | 64 | #### Profile 65 | ------------ 66 | A Buffer profile represents a connection to a single social media account. 67 | 68 | ```python 69 | profiles = Profiles(api=api) 70 | print profiles.all() # get all profiles 71 | 72 | # filter profiles using some criteria 73 | profile = Profiles(api=api).filter(service='twitter')[0] 74 | print profile # my twitter profile 75 | 76 | # get schedules of my twitter profile 77 | print profile.schedules 78 | 79 | # update schedules times for my twitter profile 80 | profile.schedules = { 81 | 'days': ['tue', 'thu'], 82 | 'times': ['13:45'] 83 | } 84 | ``` 85 | 86 | #### Update 87 | ----------- 88 | An update represents a single post to a single social media account. 89 | 90 | ```python 91 | # retrieve a single update based on an id 92 | update = Update(api=api, id='51de8d33e48c051712000019') 93 | print update 94 | 95 | # get update's interactions 96 | print update.interactions 97 | 98 | # edit 99 | update = update.edit(text="Hey!") 100 | 101 | # publish now 102 | update.publish() 103 | 104 | # move to top 105 | update.move_to_top() 106 | 107 | # delete 108 | update.delete() 109 | ``` 110 | 111 | #### Updates and profiles 112 | ------------------------- 113 | 114 | ```python 115 | # get all pending updates of a social network profile 116 | profile = Profiles(api=api).filter(service='twitter')[0] 117 | print profile.updates.pending 118 | 119 | # get all sent updates of a social network profile 120 | print profile.updates.sent 121 | 122 | # retrieve all update's interactions 123 | print profile.updates.sent[0].interactions 124 | 125 | # shuffle updates 126 | print profile.updates.shuffle(count=10) 127 | 128 | # reorder updates 129 | print profile.updates.reorder(['51dd27629f7fdf520d00009a']) 130 | 131 | # create an update 132 | print profile.updates.new("Hello there", now=True) 133 | ``` 134 | 135 | #### Links 136 | ---------- 137 | A link represents a unique URL that has been shared through Buffer 138 | 139 | ```python 140 | # get a link's shares 141 | print Link(api=api, url='http%3A%2F%2Fbufferapp.com').shares 142 | ``` 143 | 144 | #### Info 145 | --------- 146 | Returns an object with the current configuration that Buffer is using, 147 | including supported services, their icons and the varying limits of character 148 | and schedules. 149 | 150 | ```python 151 | # instantiate the api object 152 | api = API(client_id='client_id', 153 | client_secret='client_secret', 154 | access_token='access_token') 155 | 156 | # get api's info 157 | print api.info 158 | ``` 159 | --------------------------------------------------------------------------------