├── MANIFEST.in ├── fbadmin ├── __init__.py ├── blockobvious.py └── fbadmin.py ├── .gitignore ├── setup.py └── readme.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include readme.md -------------------------------------------------------------------------------- /fbadmin/__init__.py: -------------------------------------------------------------------------------- 1 | from fbadmin import login, FBGroup, Member, Applicant -------------------------------------------------------------------------------- /fbadmin/blockobvious.py: -------------------------------------------------------------------------------- 1 | from fbadmin import FBGroup, login 2 | 3 | group = FBGroup(login(), 'https://www.facebook.com/groups/782652721814257/') 4 | for applicant in group.applicants: 5 | print applicant.name, applicant.age, 'other groups =', applicant.groupcount 6 | if applicant.groupcount > 50: 7 | try: 8 | group.block(applicant) 9 | except: 10 | print'Failed' 11 | elif applicant.groupcount < 10 and 'month' not in applicant.age: 12 | try: 13 | group.approve(applicant) 14 | except: 15 | print 'Failed' 16 | 17 | group.quit() # teardown 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | #extra 57 | credentials.cfg 58 | ghostdriver.log -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import os 3 | try: 4 | import pypandoc 5 | description=pypandoc.convert('readme.md','rst') 6 | except: 7 | description='' 8 | setup(name='fbadmin', 9 | version='0.1.0.5', 10 | description='A python library to automate facebook group administration', 11 | long_description=description, 12 | url='https://github.com/thekindlyone/fbadmin', 13 | author='thekindlyone', 14 | author_email='dodo.dodder@gmail.com', 15 | license='GNU GPL v2', 16 | packages=['fbadmin'], 17 | install_requires=[ 18 | 'selenium', 19 | ], 20 | classifiers=[ 21 | "Development Status :: 4 - Beta", 22 | "Topic :: Utilities", 23 | "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", 24 | "Operating System :: OS Independent", 25 | "Programming Language :: Python :: 2.7" 26 | ], 27 | zip_safe=False) -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #fbadmin 2 | ## A python library to admin facebook groups that uses selenium and phantomjs behind the scenes 3 | [![Supported Python versions](https://img.shields.io/pypi/pyversions/fbadmin.svg)](https://pypi.python.org/pypi//) 4 | [![License](https://img.shields.io/pypi/l/fbadmin.svg)](https://pypi.python.org/pypi/fbadmin/) 5 | [![Development Status](https://img.shields.io/pypi/status/fbadmin.svg)](https://pypi.python.org/pypi/fbadmin/) 6 | 7 | #### Note: This library is under active development. Lots more to come. To update 8 | 9 | ```pip install -U --no-deps fbadmin``` 10 | 11 | ### Dependencies 12 | ####phantomjs 13 | Easiest way to install phantomjs is via ```npm``` 14 | ```npm install phantomjs``` 15 | 16 | ####other dependencies and the package itself can be installed via pip 17 | ```pip install fbadmin``` 18 | 19 | ###Usage 20 | ####Login and instantiate FBgroup 21 | 22 | ``` 23 | from fbadmin import login,FBGroup 24 | group_url='https://www.facebook.com/groups/782652721814257/' 25 | group=FBGroup(login('path/to/credentials.cfg'),group_url) 26 | ``` 27 | ####Print all applicants and the number of groups they are members of. 28 | 29 | ``` 30 | for applicant in group.applicants: 31 | print applicant.name,applicant.groupcount 32 | 33 | Liviu Vs Ze'us 17 34 | Iliya Tamarkin 24 35 | Raj K Rana 21 36 | Royendgel Silberie 41 37 | Bishnu Prasad Chowdhury 27 38 | Taranjeet Galactus Singh 13 39 | Aws Al-Aisafa 4 40 | أحمد محمود محمد عبدالوهاب 49 41 | Lha Ckg 22 42 | Krishna Jha 10 43 | Bhavesh Nigam 48 44 | Jeevan Anand Anne 19 45 | Sai Sandeep 19 46 | Raga Tarun 25 47 | Tarun Tremendous 48 48 | Aakeem Coleman 37 49 | Bill Pearce 17 50 | Derrick Kearney 17 51 | 52 | ``` 53 | 54 | ####Block if member of more than 100 groups and approve if member of less than 10 groups 55 | 56 | ``` 57 | for applicant in group.applicants: 58 | if applicant.groupcount>100: 59 | group.block(applicant) 60 | elif applicant.groupcount<=10: 61 | group.approve(applicant) 62 | 63 | ``` 64 | 65 | ####Get members 66 | 67 | ``` 68 | for page in group.get_members(): 69 | for member in page: 70 | print member.name #prints member names one page at a time 71 | ``` 72 | 73 | ####Get source of member's homepage(for spam analysis) 74 | 75 | ``` 76 | html=group.peak(member.url) 77 | ``` 78 | 79 | ####Teardown when done with it 80 | 81 | ``` 82 | group.quit() 83 | ``` 84 | ####Please look at the code for other functionality. There are docstrings. 85 | 86 | ###needs a config file of following format to be saved as credentials.cfg 87 | 88 | ``` 89 | #This is the configuration file. Enter FB credentials here. 90 | [credentials] 91 | email = malcolmreynolds@serenity.com 92 | password = youcanttaketheskyfromme 93 | ``` 94 | 95 | Suggestions/Comments/Issues can be sent at dodo.dodder@gmail.com -------------------------------------------------------------------------------- /fbadmin/fbadmin.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import unicode_literals 3 | from selenium import webdriver 4 | from ConfigParser import SafeConfigParser 5 | from os.path import exists 6 | import sys 7 | import re 8 | import time 9 | 10 | 11 | def get_cred(path): 12 | if exists(path): 13 | parser = SafeConfigParser() 14 | parser.read(path) 15 | em = parser.get('credentials', 'email') 16 | pw = parser.get('credentials', 'password') 17 | return em, pw 18 | else: 19 | print('config file not found') 20 | sys.exit(1) 21 | 22 | 23 | def login(configfile='credentials.cfg'): 24 | """logs in to facebook using credentials from credentials.cfg config file by default. 25 | Keyword Arguments 26 | configfile -- path to configfile 27 | """ 28 | em, pw = get_cred(configfile) 29 | service_args = ['--load-images=no', '--ignore-ssl-errors=true', '--ssl-protocol=any'] 30 | driver = webdriver.PhantomJS(service_args=service_args) 31 | driver.set_window_size(1600, 900) 32 | driver.get('http://www.facebook.com') 33 | email = driver.find_element_by_id('email') 34 | email.send_keys(em) 35 | password = driver.find_element_by_id('pass') 36 | password.send_keys(pw) 37 | button = driver.find_element_by_id('loginbutton') 38 | button.click() 39 | return driver 40 | 41 | 42 | class Applicant(object): 43 | 44 | def __init__(self, name, age, othergroupstext, url): 45 | """ Stores details of an applicant 46 | Useful Attributes: 47 | self.name Contains name of applicant 48 | self.age Contains the 'joined facebook..ago' string. Check for 'month' in age or 49 | 'year' in age to get an estimate of how old the account is. 50 | self.othergroupstext String containing the 'Member of X groups'. For printing purposes 51 | self.groupcount Integer containing number of groups applicant is a member of 52 | self.url FB URL of applicant 53 | """ 54 | self.name, self.age, self.othergroupstext, self.url = name, age, othergroupstext, url 55 | match = re.search(r'\d+', self.othergroupstext) 56 | if match: 57 | self.groupcount = int(match.group(0)) 58 | else: 59 | self.groupcount = 0 60 | 61 | 62 | class Member(object): 63 | 64 | def __init__(self, member_id, name, url, member_since): 65 | """ 66 | Member class 67 | Attributes 68 | self.name 69 | self.url 70 | self.member_since "2015-01-15 09:53:03" 71 | """ 72 | self.member_id = member_id 73 | self.name = name 74 | self.url = url 75 | self.member_since = member_since 76 | 77 | 78 | class FBGroup(object): 79 | 80 | def __init__(self, driver, groupurl): 81 | """main class that deals with group 82 | Arguments 83 | driver -- A selenium webdriver object. Generally what fbadmin.login() returns. 84 | groupurl -- Complete url of the FB group (with trailing slash). 85 | 86 | Attributes 87 | self.applicants -- List of fbadmin.Applicant objects. Represents all the applicants pending membership approval. 88 | Refresh by calling self.get_applicants() 89 | """ 90 | self.driver = driver 91 | self.groupurl = groupurl 92 | self.applicants = self.get_applicants() 93 | 94 | def get_applicants(self): 95 | """Returns List of fbadmin.Applicant objects. Represents all the applicants pending membership approval.""" 96 | self.driver.get(self.groupurl + 'requests') 97 | try: 98 | blocks = self.driver.find_elements_by_link_text('Block') 99 | containers = [ 100 | block.find_element_by_xpath('../..') for block in blocks] 101 | applicants = [ 102 | Applicant(*self.parse_applicant(container)) for container in containers] 103 | except: 104 | applicants = None 105 | return applicants 106 | 107 | def parse_applicant(self, container): 108 | name, age, othergroupstext = tuple(container.text.split('\n')[1:4]) 109 | url = container.find_element_by_link_text(name).get_attribute('href') 110 | return name, age, othergroupstext, url 111 | 112 | def block(self, applicant): 113 | """blocks applicant. returns name. 114 | Arguments 115 | applicant -- fbadmin.Applicant object to be blocked 116 | """ 117 | self.driver.find_element_by_link_text(applicant.name).find_element_by_xpath( 118 | '../../../..').find_element_by_link_text('Block').click() 119 | time.sleep(1) 120 | print 'blocking', applicant.name 121 | return applicant.name 122 | 123 | def approve(self, applicant): 124 | """approves applicant. returns name. 125 | Arguments 126 | applicant -- fbadmin.Applicant object to be approved 127 | """ 128 | self.driver.find_element_by_link_text(applicant.name).find_element_by_xpath( 129 | '../../../..').find_element_by_link_text('Approve').click() 130 | time.sleep(1) 131 | print 'approving', applicant.name 132 | return applicant.name 133 | 134 | def get_members(self): 135 | """ 136 | this is a generator method that yields a list of Member objects every iteration. 137 | changes page every iteration (delay > 3 secs). 138 | example : 139 | for page in group.get_members(): 140 | for member in page: 141 | print member.name #prints member names one page at a time 142 | """ 143 | self.driver.get(self.groupurl + 'members') 144 | count = 0 145 | more = True 146 | while more: 147 | members = [Member(*self.parse_member(td)) 148 | for td in self.driver.find_elements_by_tag_name('td')[count:] if td.text] 149 | count = len(members) 150 | more = self.seemore() 151 | yield members 152 | 153 | def seemore(self): 154 | try: 155 | button = self.driver.find_element_by_link_text('See More') 156 | button.click() 157 | time.sleep(3) 158 | return True 159 | except: 160 | return False 161 | 162 | def parse_member(self, container): 163 | d = container.text.split('\n') 164 | name = d[0] 165 | member_since = convert_epoch_to_timestamp_string(container.find_element_by_tag_name('abbr').get_attribute('data-utime')) 166 | url = container.find_element_by_link_text(name).get_attribute('href') 167 | member_id = self.parse_id(container, name) 168 | return member_id, name, url, member_since 169 | 170 | def convert_epoch_to_timestamp_string(self, epoch): 171 | t = datetime.datetime.fromtimestamp(float(epoch)) 172 | fmt = "%Y-%m-%d %H:%M:%S" 173 | return t.strftime(fmt) 174 | 175 | def parse_id(self, container, name): 176 | hovercard = container.find_element_by_link_text(name).get_attribute('data-hovercard') 177 | regex_id = re.compile('php\?id=(\d*)') 178 | member_id = regex_id.search(hovercard).group(1) 179 | return member_id 180 | 181 | def peak(self, url): 182 | """returns the homepage html of url of user 183 | Arguments 184 | url -- url of user to peak at 185 | """ 186 | self.driver.get(url) 187 | return self.driver.page_source 188 | 189 | def quit(self): 190 | """Tear Down""" 191 | self.driver.quit() 192 | --------------------------------------------------------------------------------