├── .gitignore ├── LICENSE ├── README.md ├── bin └── phantomjs-osx ├── cli ├── __init__.py ├── __main__.py ├── browser.py └── email_loader.py ├── emails.example.csv ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.egg-info 3 | *.pyc 4 | *.log 5 | emails.csv 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Daniel Ireson 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bulk email inviter for Facebook groups 2 | This is a quick tool that allows you to invite members to your Facebook group via email in bulk. I put it together for use at the Manchester Entrepreneurs society at the start of the year to import our new fresher's member list to our active Facebook group. 3 | 4 | ## How it works 5 | It was built in Python (2.7) and uses Selenium with the PhantomJS driver. The PhantomJS driver for osx has been included in the repo. If you want to run it on windows or linux you'll have to download the relevant driver and update the *executable_path* in *cli/browser.py*. 6 | 7 | ## Installing 8 | ``` bash 9 | # from the facebook-bulk-group-inviter directory 10 | pip install . 11 | 12 | # install with editable flag if you plan on making changes 13 | pip install -e . 14 | ``` 15 | 16 | ## Getting started 17 | Invoke *facebook-bulk-group-inviter* from the command line with arguments *-e*, and *-g* with your facebook email and group name (or number). You will then be prompted to enter your password. 18 | 19 | ``` bash 20 | # run the import 21 | facebook-bulk-group-inviter -e email@example.com -g nameornumber 22 | ``` 23 | 24 | By default email addresses will be loaded from *emails.csv* in the package directory but you can override this by passing a new file name with the *-f* parameter. Emails should be on a new line and in the first column. There can be other columns in the csv file but the email address has to be in the first column. Please also ensure your csv has no headers. 25 | 26 | -------------------------------------------------------------------------------- /bin/phantomjs-osx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielireson/facebook-bulk-group-inviter/8be91625c879100ee0d63ca762829d47430b0a63/bin/phantomjs-osx -------------------------------------------------------------------------------- /cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielireson/facebook-bulk-group-inviter/8be91625c879100ee0d63ca762829d47430b0a63/cli/__init__.py -------------------------------------------------------------------------------- /cli/__main__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import getpass 3 | 4 | from browser import Browser 5 | from email_loader import EmailLoader 6 | 7 | def main(): 8 | parser = argparse.ArgumentParser(description='This tool lets you invite people in bulk to your Facebook group') 9 | parser.add_argument('-e','--email', help='Your personal Facebook account email', required=True) 10 | parser.add_argument('-g','--group', help='The Facebook group name', required=True) 11 | parser.add_argument('-f','--file', help='The csv file to load email addresses from', default='emails.csv') 12 | args = vars(parser.parse_args()) 13 | args['password'] = getpass.getpass() 14 | 15 | email_loader = EmailLoader(filename=args['file']) 16 | browser = Browser() 17 | browser.navigate( 18 | url='https://www.facebook.com', 19 | wait_for='facebook', 20 | error='Unable to load the Facebook website' 21 | ) 22 | browser.enter_login_details(email=args['email'], password=args['password']) 23 | browser.navigate( 24 | url='https://www.facebook.com/groups/' + args['group'], 25 | wait_for='pagelet_group_', 26 | error='Couldn\'t navigate to the group\'s members page' 27 | ) 28 | browser.import_members(emails=email_loader.all_emails) 29 | print(str(len(email_loader.all_emails)) + ' email addresses successfully imported') 30 | 31 | if __name__ == '__main__': 32 | main() 33 | -------------------------------------------------------------------------------- /cli/browser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import sys 4 | import time 5 | import unicodedata 6 | 7 | from selenium import webdriver 8 | from selenium.common.exceptions import TimeoutException 9 | from selenium.webdriver.common.by import By 10 | from selenium.webdriver.common.keys import Keys 11 | from selenium.webdriver.support import expected_conditions 12 | from selenium.webdriver.support.ui import WebDriverWait 13 | 14 | class Browser: 15 | delay = 3 16 | 17 | def __init__(self): 18 | driver_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '/bin/phantomjs-osx' 19 | self.browser = webdriver.PhantomJS(executable_path=driver_path) 20 | 21 | def navigate(self, url, wait_for, error): 22 | try: 23 | print('Navigating to: ' + url) 24 | self.browser.get(url) 25 | element_present = expected_conditions.presence_of_element_located((By.ID, wait_for)) 26 | WebDriverWait(self.browser, self.delay).until(element_present) 27 | except TimeoutException: 28 | sys.exit(error) 29 | 30 | def enter_login_details(self, email, password): 31 | try: 32 | print('Entering login details') 33 | email_field = self.browser.find_element_by_id('email') 34 | pass_field = self.browser.find_element_by_id('pass') 35 | email_field.send_keys(email) 36 | pass_field.send_keys(password) 37 | pass_field.submit() 38 | element_present = expected_conditions.presence_of_element_located((By.ID, 'userNavigationLabel')) 39 | WebDriverWait(self.browser, self.delay).until(element_present) 40 | except TimeoutException: 41 | sys.exit('Login with your credentials unsuccessful') 42 | 43 | def import_members(self, emails): 44 | print('Attempting to import email addresses') 45 | input_xpath = "//input[@placeholder='Enter name or email address...']" 46 | add_members_field = self.browser.find_element_by_xpath(input_xpath) 47 | for email in emails: 48 | for c in email: 49 | add_members_field.send_keys(self._get_base_character(c)) 50 | add_members_field.send_keys(Keys.RETURN) 51 | time.sleep(random.randint(1,self.delay)) 52 | 53 | @staticmethod 54 | def _get_base_character(c): 55 | desc = unicodedata.name(unicode(c)) 56 | cutoff = desc.find(' WITH ') 57 | if cutoff != -1: 58 | desc = desc[:cutoff] 59 | return unicodedata.lookup(desc) 60 | -------------------------------------------------------------------------------- /cli/email_loader.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import sys 4 | 5 | class EmailLoader: 6 | all_emails = [] 7 | 8 | def __init__(self, filename): 9 | file_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '/' + filename 10 | if not os.path.isfile(file_path): 11 | sys.exit('File does not exist: ' + filename) 12 | 13 | with open(file_path, 'rb') as file: 14 | csv_reader = csv.reader(file) 15 | for email in csv_reader: 16 | self.all_emails.append(email[0]) 17 | 18 | if len(self.all_emails) < 1: 19 | sys.exit('There are no emails in your supplied file') 20 | else: 21 | print('Loaded ' + str(len(self.all_emails)) + ' emails from ' + filename) 22 | -------------------------------------------------------------------------------- /emails.example.csv: -------------------------------------------------------------------------------- 1 | john@example.com 2 | jane@example.com 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | selenium==3.0.1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open('requirements.txt') as f: 4 | requirements = f.read().splitlines() 5 | 6 | setup( 7 | name = 'facebook-bulk-group-inviter', 8 | version = '0.0.1', 9 | packages = ['cli'], 10 | install_requires=requirements, 11 | entry_points = { 12 | 'console_scripts': [ 13 | 'facebook-bulk-group-inviter = cli.__main__:main' 14 | ] 15 | }) 16 | --------------------------------------------------------------------------------