├── .gitignore ├── FBTools.py ├── LICENSE ├── README.md └── commands.txt /.gitignore: -------------------------------------------------------------------------------- 1 | cookies.pkl 2 | friendList.pkl 3 | ghostdriver.log 4 | one.jpg 5 | test.py -------------------------------------------------------------------------------- /FBTools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # FBTools by Ashish Chaudhary [http://github.com/yankee101] 3 | 4 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 5 | from selenium.common.exceptions import NoSuchElementException 6 | from selenium.webdriver.common.keys import Keys 7 | from pyfiglet import Figlet 8 | import selenium.webdriver 9 | import requests 10 | import pickle 11 | import sys 12 | import re 13 | import os 14 | 15 | class FBTools: 16 | 17 | ##Initialise Driver_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 18 | 19 | def __init__(self): 20 | dcap = dict(DesiredCapabilities.PHANTOMJS) 21 | dcap["phantomjs.page.settings.userAgent"] = ( 22 | "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0" #The xpaths used in the code are with reference to the layout of m.facebook.com on Firefox Windows. 23 | ) 24 | serviceArgs = ['--load-images=no',] 25 | self.driver=selenium.webdriver.PhantomJS(desired_capabilities=dcap,service_args=serviceArgs) 26 | 27 | ##Login Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 28 | 29 | def loginChecker(self): 30 | if os.path.isfile("cookies.pkl") == True: 31 | return True 32 | else: 33 | return False 34 | 35 | def login(self): 36 | print("Provide your credentials for login.") 37 | print("Credentials are not stored and required only once...") 38 | email = input("Email/Username/Phone : ") 39 | password = input("Password : ") 40 | 41 | print("Attempting Login...") 42 | 43 | self.driver.get("http://m.facebook.com/settings") 44 | self.driver.find_element_by_name("email").send_keys(email) 45 | self.driver.find_element_by_name("pass").send_keys(password + Keys.RETURN) 46 | 47 | dummy = 0 48 | 49 | try: 50 | if self.driver.find_element_by_xpath('//*[@id="viewport"]/div[3]/div/table/tbody/tr/td[2]/a[3]').is_displayed() == True: 51 | print("Successfully logged in. Dumping Cookies...") 52 | self.cookieDumper() 53 | print("Dumped Cookies") 54 | except NoSuchElementException: 55 | dummy += 1 56 | 57 | if dummy == 1: 58 | print("xxxxxxx") 59 | print("Unable to login, try again later.") 60 | 61 | 62 | 63 | def cookieDumper(self): #Dumps cookies on first login. 64 | pickle.dump(self.driver.get_cookies() , open("cookies.pkl","wb")) 65 | 66 | 67 | def cookieInjector(self): #Injects cookies on subsequent logins. 68 | if os.path.isfile("cookies.pkl") == True: 69 | cookies = pickle.load(open("cookies.pkl", "rb")) 70 | self.driver.get("http://m.facebook.com") 71 | for cookie in cookies: 72 | self.driver.add_cookie(cookie) 73 | self.driver.get("http://m.facebook.com/settings") 74 | 75 | ##Home Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 76 | 77 | def home(self,pageNumber,click): 78 | if pageNumber == 0 and click == 0: 79 | self.driver.get("http://m.facebook.com") 80 | if click == 1: 81 | try: 82 | if pageNumber == 1: 83 | self.driver.find_element_by_xpath('//*[@id="m_newsfeed_stream"]/div[3]/a').click() 84 | elif pageNumber > 1: 85 | self.driver.find_element_by_xpath('//*[@id="root"]/div/div[3]/a').click() 86 | except NoSuchElementException: 87 | print("Cannot access the next page.") 88 | self.onPage = 0 89 | 90 | holder = [] 91 | like_link_holder = [] 92 | comment_link_holder = [] 93 | n = 0 94 | while n < 10: 95 | try: 96 | path = '//*[@id="u_0_{}"]'.format(n) 97 | post = self.driver.find_element_by_xpath(path) 98 | if post.text != "": 99 | comment_package = self.commentLinkExtractor(path) 100 | if comment_package[0] != False: 101 | like_link_holder.append(self.likeLinkExtractor(path)) 102 | comment_link_holder.append(comment_package[1]) 103 | holder.append(post.text.strip()) 104 | n += 1 105 | except NoSuchElementException: 106 | n += 1 107 | 108 | n = 0 109 | while n < 10: 110 | try: 111 | path = '//*[@id="u_0_{}"]'.format(chr(n + 97)) 112 | post = self.driver.find_element_by_xpath(path) 113 | if post.text != "": 114 | comment_package = self.commentLinkExtractor(path) 115 | if comment_package[0] != False: 116 | like_link_holder.append(self.likeLinkExtractor(path)) 117 | comment_link_holder.append(comment_package[1]) 118 | holder.append(post.text.strip()) 119 | n += 1 120 | except NoSuchElementException: 121 | n += 1 122 | 123 | self.returnedList = self.homeParser(holder,like_link_holder,comment_link_holder) 124 | for index,post in enumerate(self.returnedList[0]): 125 | try: 126 | print("---{}---\n{}".format(index,self.render(post))) 127 | except (UnicodeDecodeError,UnicodeEncodeError): 128 | pass 129 | print("xxxxxxx") 130 | self.onPage += 1 131 | 132 | def homeParser(self,posts,like_links,comment_links): 133 | for post in posts: 134 | dummy = -1 #Some posts get repeated while fetching, this block of code deletes them. 135 | for y in posts: 136 | dummy += 1 137 | if post != y: 138 | if y in post: 139 | del posts[dummy] 140 | del like_links[dummy] 141 | del comment_links[dummy] 142 | break 143 | b = -1 144 | for post in posts: #This block of code is supposed to only allow english chars. Needs rechecking. 145 | b += 1 146 | if self.isEnglish(post) == False: 147 | del posts[b] 148 | del like_links[b] 149 | del comment_links[b] 150 | 151 | return [posts,like_links,comment_links] 152 | 153 | def isEnglish(self,s): 154 | try: 155 | s.encode('ascii') 156 | return True 157 | except UnicodeEncodeError: 158 | return False 159 | 160 | def render(self,post): 161 | post = re.sub('. Add Friend . Full Story . More','',post) #Replaces irrelevent text with '' 162 | post = re.sub('Add Friend\n','',post) 163 | post = re.sub('. Full Story . More','',post) 164 | post = re.sub('. Like Page','',post) 165 | post = re.sub('Like Page . More','',post) 166 | post = re.sub('. Share','',post) 167 | post = re.sub('More','',post) 168 | post = re.sub('. More','',post) 169 | post = re.sub('Share','',post) 170 | post = re.sub('Join Page','',post) 171 | post = re.sub('Like Page','',post) 172 | post = re.sub('Join Event','',post) 173 | 174 | return post 175 | 176 | def likeLinkExtractor(self,path): 177 | try: 178 | like_link = self.driver.find_element_by_xpath('{}/div[2]/div[2]/div[2]/span[1]/a[2]'.format(path)) 179 | return like_link 180 | except NoSuchElementException: 181 | try: 182 | like_link = self.driver.find_element_by_xpath('{}/div[2]/div[2]/span[1]/a[2]'.format(path)) 183 | return like_link 184 | except NoSuchElementException: 185 | return False 186 | 187 | def commentLinkExtractor(self,path): 188 | try: 189 | comment_link = self.driver.find_element_by_xpath('{}/div[2]/div[2]/a[1]'.format(path)) 190 | return [True,comment_link] 191 | except NoSuchElementException: 192 | return [False,False] 193 | 194 | def like(self,index): 195 | try: 196 | self.driver.get(self.returnedList[1][index].get_attribute("href")) 197 | print("Liked.") 198 | except: 199 | print("Unable to like.") 200 | self.onPage = 0 201 | self.home(self.onPage,0) 202 | 203 | def comment(self,index): 204 | try: 205 | self.driver.get(self.returnedList[2][index].get_attribute("href")) 206 | comment = input("Enter your comment:\n") 207 | self.driver.find_element_by_xpath('//*[@id="composerInput"]').send_keys(comment + Keys.RETURN) 208 | print("Commented.") 209 | except: 210 | print("Unable to comment.") 211 | self.onPage = 0 212 | self.home(self.onPage,0) 213 | 214 | def homeActionsParser(self,action): #Parses the news feed post index from the command. 215 | operand = -1 216 | for s in action.split(): 217 | if s.isdigit(): 218 | operand = int(s) 219 | try: 220 | if operand == -1: 221 | print("Invalid command") 222 | return -1 223 | elif operand >= len(self.returnedList[1]): 224 | print("Invalid command") 225 | return -1 226 | return operand 227 | except AttributeError: 228 | print("Nothing here! So can't perform that action.") 229 | return -1 230 | 231 | ##Friend List Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 232 | 233 | 234 | def friendWriter(self,friendList): 235 | if os.path.isfile("friendList.pkl") == True: 236 | file = open("friendList.pkl",'wb') 237 | else: 238 | print("Generating Friend List for the first time.") 239 | file = open("friendList.pkl",'wb') 240 | pickle.dump(friendList,file) 241 | 242 | def friendList(self): 243 | holder = [] 244 | n = 0 245 | dummy = 0 246 | 247 | print("Fetching Friend List",end='') 248 | 249 | while n <= 500: 250 | print(".",end='') 251 | sys.stdout.flush() 252 | try: 253 | self.driver.get("https://m.facebook.com/friends/center/friends/?ppk={}".format(n)) 254 | a = 1 255 | while a<= 10: 256 | element = self.driver.find_element_by_xpath('//*[@id="friends_center_main"]/div[2]/div[{}]/table/tbody/tr/td[2]/a'.format(a)) 257 | holder.append(element.text + "," + element.get_attribute("href").split('/')[6].split('&')[0].split('?uid=')[1]) 258 | a += 1 259 | n += 1 260 | except NoSuchElementException: 261 | try: 262 | elem = self.driver.find_element_by_xpath('//*[@id="friends_center_main"]/div[2]/div[1]/table/tbody/tr/td[2]/a') 263 | n += 1 264 | except: 265 | break 266 | 267 | print("") 268 | return holder 269 | 270 | def friendComparator(self,newList): 271 | print("Finding who unfriended you! (or you unfriended them)") 272 | kickingFriends = [] 273 | 274 | if os.path.isfile("friendList.pkl") == True and newList != []: 275 | oldFile = pickle.load(open("friendList.pkl", "rb")) 276 | for line in oldFile: 277 | if line not in newList: 278 | kickingFriends.append(line.split(',')[0]) 279 | elif os.path.isfile("friendList.pkl") == False: 280 | print("Failed to find the Old Friend List") 281 | print("Writing new Friend List") 282 | else: 283 | print("Function Failed") 284 | 285 | self.friendWriter(newList) 286 | return kickingFriends 287 | 288 | def notInList(self): 289 | comparison = self.friendComparator(self.friendList()) 290 | if comparison == []: 291 | print("xxxxxxx\nNo new Un-friends\nxxxxxxx") 292 | else: 293 | print("These prople are no more in your friend list: ") 294 | print("CAUTION : If they haven't unfriended you, they may have deactivated their account temporarily.") 295 | print("\nxxxxxxx") 296 | for kickingFriend in comparison: 297 | print(kickingFriend) 298 | print("xxxxxxx") 299 | 300 | ##Notifications Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 301 | 302 | def notify(self): 303 | try: 304 | self.driver.get("https://m.facebook.com/notifications") 305 | temp = self.dateCurator() 306 | dates = temp[0] 307 | xpaths = temp[1] 308 | print("\nxxxxxxx\nNotifications\nxxxxxxx\n") 309 | for index,date in enumerate(dates): 310 | print(date + ":") 311 | notifications = self.getNotifications(xpaths[index]) 312 | for notification in notifications: 313 | print("- - - - -") 314 | print(notification) 315 | print("x_x_x_x_x_x_x_x_x_x") 316 | except NoSuchElementException: 317 | print("xxxxxxx\nCannot Print Notifications\nxxxxxxx") 318 | 319 | def dateCurator(self): 320 | dates = [] 321 | xpaths = [] 322 | n = 1 323 | while n < 10: 324 | try: 325 | xpath = '//*[@id="notifications_list"]/div[{}]/h5'.format(n) 326 | date = self.driver.find_element_by_xpath(xpath).text 327 | dates.append(date) 328 | xpaths.append(xpath) 329 | n += 1 330 | except NoSuchElementException: 331 | n += 1 332 | break 333 | return [dates,xpaths] 334 | 335 | def getNotifications(self,basepath): 336 | notifications = [] 337 | n = 1 338 | while n < 20: 339 | try: 340 | xpath = re.sub("/h5",'',basepath)+"/div[{}]/table/tbody/tr/td[2]/a/div".format(n) 341 | notification = self.driver.find_element_by_xpath(xpath).text 342 | notifications.append(notification) 343 | n += 1 344 | except NoSuchElementException: 345 | n += 1 346 | break 347 | return notifications 348 | 349 | ##Friend's Timeline Liker Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 350 | 351 | def friendLiker(self): 352 | print("THIS WILL LIKE EACH AND EVERY POST OF A SPECIFIED FRIEND.") 353 | print("ENTER 'p' TO PROCEED OR ANYTHING ELSE TO QUIT.") 354 | choice = input("") 355 | if choice == 'p' or choice == 'P': 356 | if os.path.isfile("friendList.pkl") == False: 357 | print("Friend List not found.") 358 | print("Use 'unfr' first to get the initial friend list.") 359 | return False 360 | else: 361 | print("\nxxxxxxx\nIndex : Friend (alphabetical order):\nxxxxxxx") 362 | file = pickle.load(open("friendList.pkl",'rb')) 363 | for index,line in enumerate(sorted(file)): 364 | print(str(index)+" : "+line.split(',')[0]) 365 | print("\nxxxxxxx\nEnter Index\nxxxxxxx") 366 | index = input("Index: ") 367 | if index.isdigit(): 368 | self.loadProfile(int(index)) 369 | else: 370 | print("Invalid Index") 371 | else: 372 | return False 373 | 374 | def loadProfile(self,number): 375 | uid = 0 376 | for index,line in enumerate(sorted(pickle.load(open("friendList.pkl",'rb')))): 377 | if index == number: 378 | uid = int(line.split(',')[1]) 379 | print("Liking all posts on {}'s Timeline...".format(line.split(',')[0].split()[0])) 380 | break 381 | self.driver.get("http://m.facebook.com/{}".format(uid)) 382 | try: 383 | temp = self.elementYear() 384 | years = [] 385 | names = [] 386 | numberOfLikes = 0 387 | alreadyLiked = 0 388 | totalLiked = 0 389 | totalAlreadyLiked = 0 390 | for x in temp: 391 | years.append(x.get_attribute("href")) 392 | names.append(x.text) 393 | print("*one '.' == 1 Like Administered*") 394 | for index,year in enumerate(years): 395 | self.driver.get(year) 396 | if index != 0: 397 | stories = self.allStories() 398 | else: 399 | stories = "dummyValue" 400 | print("\nxxxxxxx {} xxxxxxx".format(names[index])) 401 | if stories != None: 402 | if stories != "dummy": 403 | self.driver.get(stories) 404 | showmorelink = self.showMore() 405 | while showmorelink != "dummy": 406 | likelinks = self.friendLikeLink() 407 | alreadyLiked += likelinks[0] 408 | totalAlreadyLiked += likelinks[0] 409 | likedJustNow = self.likeAllLinks(likelinks[1]) 410 | numberOfLikes += likedJustNow 411 | totalLiked += likedJustNow 412 | self.driver.get(showmorelink) 413 | showmorelink = self.showMore() 414 | if showmorelink == "dummy": 415 | likelinks = self.friendLikeLink() 416 | alreadyLiked += likelinks[0] 417 | totalAlreadyLiked += likelinks[0] 418 | likedJustNow = self.likeAllLinks(likelinks[1]) 419 | numberOfLikes += likedJustNow 420 | totalLiked += likedJustNow 421 | print("\nPosts liked now: {}".format(numberOfLikes)) 422 | print("Posts already liked: {}".format(alreadyLiked)) 423 | numberOfLikes = 0 424 | alreadyLiked = 0 425 | else: 426 | print("Failed") 427 | if stories == None: 428 | likelinks = self.friendLikeLink() 429 | alreadyLiked += likelinks[0] 430 | totalAlreadyLiked += likelinks[0] 431 | likedJustNow = self.likeAllLinks(likelinks[1]) 432 | numberOfLikes += likedJustNow 433 | totalLiked += likedJustNow 434 | print("\nPosts liked now: {}".format(numberOfLikes)) 435 | print("Posts already liked: {}".format(alreadyLiked)) 436 | numberOfLikes = 0 437 | alreadyLiked = 0 438 | print("\nxxxxxxx REPORT xxxxxxx\n") 439 | print("Total Likes Administered Now: {}".format(totalLiked)) 440 | print("Number Of Already Liked Posts: {}".format(totalAlreadyLiked)) 441 | print("Total Likes on Friend's Timeline: {}".format(totalLiked + totalAlreadyLiked)) 442 | except NoSuchElementException: 443 | print("Can't Proceed") 444 | 445 | def allStories(self): 446 | link = "" 447 | try: 448 | showall = self.driver.find_elements_by_xpath("//*[contains(text(), 'Show all stories')]") 449 | for show in showall: 450 | if self.checkValidLink(show,"stories") == True: 451 | link = show.get_attribute("href") 452 | return link 453 | else: 454 | return "dummy" 455 | except NoSuchElementException: 456 | return "dummy" 457 | 458 | def showMore(self): 459 | try: 460 | showmore = self.driver.find_element_by_xpath("//*[contains(text(), 'Show more')]") 461 | if self.checkValidLink(showmore,"showmore") == True: 462 | return showmore.get_attribute("href") 463 | else: 464 | return "dummy" 465 | except NoSuchElementException: 466 | return "dummy" 467 | 468 | 469 | def checkValidLink(self,temp,user): 470 | if user == "stories": 471 | try: 472 | link = temp.get_attribute("href") 473 | if link.split('/')[2] == "m.facebook.com": 474 | return True 475 | else: 476 | return False 477 | except: 478 | return False 479 | elif user == "likes": 480 | try: 481 | link = temp.get_attribute("href") 482 | if link.split('/')[2] == "m.facebook.com": 483 | if "like.php" in link: 484 | return True 485 | else: 486 | return False 487 | except: 488 | return False 489 | elif user == "showmore": 490 | try: 491 | link = temp.get_attribute("href") 492 | if link.split('/')[2] == "m.facebook.com": 493 | return True 494 | else: 495 | return False 496 | except: 497 | return False 498 | else: 499 | return False 500 | 501 | 502 | def elementYear(self): 503 | n = 1 504 | holder = [] 505 | while n < 20: 506 | try: 507 | holder.append(self.driver.find_element_by_xpath('//*[@id="structured_composer_async_container"]/div[4]/div[{}]/a'.format(n))) 508 | n += 1 509 | except NoSuchElementException: 510 | break 511 | if holder == []: 512 | n = 1 513 | while n < 20: 514 | try: 515 | holder.append(self.driver.find_element_by_xpath('//*[@id="structured_composer_async_container"]/div[3]/div[{}]/a'.format(n))) 516 | n += 1 517 | except NoSuchElementException: 518 | break 519 | return holder 520 | 521 | def friendLikeLink(self): 522 | holder = [] 523 | try: 524 | likeLinks = self.driver.find_elements_by_xpath("//*[contains(text(), 'Like')]") 525 | unlikeLinks = self.driver.find_elements_by_xpath("//*[contains(text(), 'Unlike')]") 526 | for like in likeLinks: 527 | if self.checkValidLink(like,"likes") == True: 528 | holder.append(like.get_attribute("href")) 529 | alreadyLiked = len(unlikeLinks) 530 | return [alreadyLiked,holder] 531 | except NoSuchElementException: 532 | return [] 533 | 534 | def likeAllLinks(self,links): 535 | file = pickle.load(open("cookies.pkl",'rb')) 536 | numberOfLikes = 0 537 | cookies = {} 538 | for x in file: 539 | if 'datr' in str(x): 540 | cookies["datr"] = x["value"] 541 | if 'xs' in str(x): 542 | cookies["xs"] = x["value"] 543 | if 'c_user' in str(x): 544 | cookies["c_user"] = x["value"] 545 | for link in links: 546 | r = requests.get(link,cookies = cookies) 547 | if r.status_code == 200: 548 | print(".",end = '') 549 | sys.stdout.flush() 550 | numberOfLikes += 1 551 | else: 552 | print("Failed") 553 | return numberOfLikes 554 | 555 | 556 | ##Flow Manager Functions_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 557 | 558 | def greeting(self): 559 | try: 560 | data = self.driver.find_element_by_xpath("//*[contains(text(), 'Logout')]") 561 | f = Figlet(font='slant') 562 | self.name = re.search('\((.*?)\)',data.text).group(1) #Extracts username from 'Logout (username)' fetched from the page. 563 | print(f.renderText(self.name)) 564 | self.name = self.name.lower() 565 | except NoSuchElementException: 566 | pass 567 | 568 | def manager(self,command): 569 | if command == "help": 570 | if os.path.isfile("commands.txt"): 571 | file = open("commands.txt") 572 | for line in file.readlines(): 573 | print(line) 574 | else: 575 | print("Commands list missing.") 576 | elif command == "exit": 577 | sys.exit() 578 | elif command == "unfr": 579 | self.onPage = 0 580 | self.notInList() 581 | elif command == "home": 582 | self.onPage = 0 583 | self.home(self.onPage,0) 584 | elif command == "home next": 585 | if self.onPage == 0: 586 | self.home(0,0) 587 | else: 588 | self.home(self.onPage,1) 589 | elif "like" in command != -1: 590 | operand = self.homeActionsParser(command) 591 | if operand != -1: 592 | self.like(operand) 593 | elif "comment" in command != -1: 594 | operand = self.homeActionsParser(command) 595 | if operand != -1: 596 | self.comment(operand) 597 | elif command == "notif": 598 | self.notify() 599 | elif command == "auli": 600 | self.friendLiker() 601 | else: 602 | print("Invalid command. Use 'help' to get a list of commands.") 603 | 604 | self.commandInput() 605 | 606 | def commandInput(self): 607 | print("") 608 | print("Use 'help' to get the list of commands. Use 'exit' to logoff.") 609 | command = input("Enter command : ") 610 | self.manager(command) 611 | 612 | def main(): 613 | 614 | tool = FBTools() 615 | f = Figlet(font='slant') 616 | print(f.renderText('FBTools\n------')) 617 | 618 | if tool.loginChecker() == True: 619 | print("Attempting Login...") 620 | tool.cookieInjector() 621 | else: 622 | tool.login() 623 | 624 | if tool.loginChecker() == True: 625 | tool.greeting() 626 | tool.commandInput() 627 | else: 628 | sys.exit("Can't proceed.") 629 | 630 | if __name__ == "__main__":main() 631 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ashish Chaudhary 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #FBTools 2 | 3 | >FBTools allows you to access Facebook via command-line. 4 | 5 | This project was made for learning purpose and is not maintained currently. It should work fine nonetheless. In case of any bug, please file an issue and I'll rectify it. 6 | 7 | ##Highlight 8 | It does not use Facebook Graph API. 9 | So a lot of features like fetching full friend list are now available. 10 | And its blazing fast because it uses the mobile version. 11 | 12 | 13 | ##Current Features 14 | 15 | * Check if someone unfriended you. 16 | * Like all posts on a friend's timeline. 17 | * Browse through your News Feed. 18 | * Like any item in the feed. 19 | * Comment on any item in the feed. 20 | * Check your notifications. 21 | 22 | ###Upcoming Features 23 | 24 | [None] 25 | 26 | ##Requirements 27 | 28 | * Python3 29 | * [PhantomJS 2.0.0](https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.0.0-windows.zip) 30 | * Steps for setting up PhantomJS on Windows: 31 | * Make a folder `C:\PhantomJS` 32 | * Copy the contents of `phantomjs-2.0.0-windows` folder to `C:\PhantomJS` 33 | * Add `C:\PhantomJS\bin\` to your PATH environment variable. 34 | * Python packages required: 35 | * selenium (`pip install selenium`) 36 | * pyfiglet (`pip install pyfiglet`) 37 | * requests (`pip install requests`) 38 | 39 | ##Issue 40 | 41 | Posts not in English are skipped in the news feed due to decoding issues when writing the feed in command prompt (windows). Output on Python3 IDLE works fine though. 42 | 43 | ##Screenshots 44 | 45 | ###Mainscreen 46 | ![FBTools](http://i.imgur.com/GsvnBk2.png) 47 | 48 | ###Autoliker 49 | ![FBTools](http://i.imgur.com/jvZDN8u.png) 50 | 51 | Tested with Python 3.4.3 on Windows. 52 | 53 | ##License 54 | 55 | MIT © [Ashish Chaudhary](http://tocttou.mit-license.org/) 56 | -------------------------------------------------------------------------------- /commands.txt: -------------------------------------------------------------------------------- 1 | 2 | xxxxx 3 | The following commands are available in the current version: 4 | -> exit :- Exits the program. 5 | -> unfr :- Checks if someone unfriended you. 6 | (Caution :- It may give false positive in case you have unfriended a person or the person has deactivated their FB account.) 7 | -> home :- Displays your news feed. 8 | -> home next :- Goes to the next page in your news feed. Goes to home if home not already open. 9 | -> like [index] :- Likes the item in news feed on given index. Index is printed over the item. Some posts do not have "Like" written under them. They cannot be liked. 10 | -> comment [index] :- Comments on the item in news feed on given index. Index is printed over the item. Some posts do not have "Comment" written under them. You cannot comment on them. 11 | -> notif :- Displays notifications. 12 | -> auli :- Prints your friend list and prompts for liking all the posts by a specific friend or posts the he/she was tagged in. *Hidden posts cannot be liked.* 13 | xxxxx --------------------------------------------------------------------------------