├── .gitignore ├── README.md ├── composer.json ├── instapp-cli └── src ├── Command ├── FollowInHashtagCommand.php ├── FollowInLocationCommand.php ├── FollowUserFollowersCommand.php ├── InstagramCommand.php ├── LikeTimelineCommand.php └── Traits │ └── Data │ ├── FollowCount.php │ ├── Hashtag.php │ ├── LikeCount.php │ ├── Location.php │ ├── Login.php │ ├── Persons.php │ └── WaitTime.php ├── Event ├── FollowEvent.php ├── LikeEvent.php ├── LogEvent.php └── LoggedEvent.php ├── EventHelper.php ├── Exception ├── EventNotFoundException.php ├── InvalidLoginDataException.php ├── LoginDataRequiredException.php ├── MaxFollowCountException.php └── MaxLikeCountException.php ├── Instapp.php ├── Macro.php ├── Macro ├── FollowContentsOfPost.php ├── FollowUserFollowers.php ├── FollowUsersInHashtag.php ├── FollowUsersInLocation.php └── LikeAllPostsInTimeline.php ├── Provider ├── FollowServiceProvider.php ├── InstagramAPIServiceProvider.php ├── LikeServiceProvider.php ├── LoggerServiceProvider.php └── UserServiceProvider.php ├── Service ├── Follow.php ├── Like.php ├── Logger.php └── User.php └── Traits └── Wait.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out 4 | gen 5 | vendor 6 | composer.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # instapp 3 | 4 | basic instagram macros and cli tool 5 | 6 | ## requirements 7 | 8 | * php >= 5.6 9 | * php-curl 10 | * php-mbstring 11 | * php-gd 12 | * php-exif 13 | * php-zlib 14 | * php-gmp 15 | 16 | ## installation 17 | 18 | $ git clone https://github.com/funcphp/instapp.git 19 | $ cd instapp 20 | instapp$ composer install 21 | 22 | ## cli usage 23 | 24 | **follow all users in locations** 25 | 26 | instapp$ php instapp-cli follow:location 27 | 28 | **follow all users in user's followers** 29 | 30 | instapp$ php instapp-cli follow:user:follower 31 | 32 | **follow all users in hashtags feeds** 33 | 34 | instapp$ php instapp-cli follow:hashtag 35 | 36 | **like timeline feeds** 37 | 38 | instapp$ php instapp-cli like:timeline 39 | 40 | required data will be asked when commands are run 41 | 42 | or you can type data in command 43 | 44 | ex: 45 | 46 | instapp$ php instapp-cli --username=my_account_username --password=my_password --max=200 --wait=10 47 | 48 | ### list commands 49 | 50 | instapp$ php instapp-cli list 51 | 52 | ex: 53 | 54 | php instapp-cli list follow 55 | 56 | ### cli arguments 57 | 58 | |argument|description|type|default|command| 59 | |-|-|-|-|-| 60 | |username|account username|string|*required*|*all commands*| 61 | |password|account password|string|*required*|*all commands*| 62 | |wait|duration between events (s)|integer|**20**|*all commands*| 63 | |max|max number of follow / like|integer|**500**|**follow:\*** , **like:\***| 64 | |locations|location ids|integer[] (commas)|*required for*|**follow:location**| 65 | |persons|person usernames|string[] (commas)|*required for*|**follow:follower**| 66 | |hashtags|hashtags|string[] (commas)|*required for*|**follow:hashtag**| 67 | 68 | ## todo list 69 | 70 | |macro|description|status| 71 | |-|-|:-:| 72 | |`follow:user:followers`|Follow all users in a user's follower list|:heavy_check_mark:| 73 | |`follow:user:following`|Follow all users in a user's following list|waiting| 74 | |`follow:location`|Follow all users in location feed|:heavy_check_mark:| 75 | |`follow:hashtag`|Follow all users in hashtag feed|:heavy_check_mark:| 76 | |`follow:discover:all`|Follow all users in discover feed|waiting| 77 | |`follow:discover:popular`|Follow all users in popular feed|waiting| 78 | |`unfollow:following`|Unfollow all users who you following|waiting| 79 | |`unfollow:followers`|Unfollow all users who you following in your follower list|waiting| 80 | |`like:timeline`|Like all posts in timeline feed|:heavy_check_mark:| 81 | |`like:location`|Like all posts in location feed|waiting| 82 | |`like:hashtag`|Like all posts in hashtag feed|waiting| 83 | |`like:user`|Like all posts in user feed|waiting| 84 | |`like:discover:all`|Like all posts in discover feed|waiting| 85 | |`like:discover:popular`|Like all posts in popular feed|waiting| -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "funcphp/instapp", 3 | "keywords": ["instagram bot"], 4 | "description": "Instagram macro apps and command line client with PHP", 5 | "require": { 6 | "pimple/pimple": "^3.2", 7 | "mgp25/instagram-php": "dev-master", 8 | "symfony/console": "^3.4", 9 | "symfony/event-dispatcher": "^3.4" 10 | }, 11 | "require-dev": { 12 | "symfony/var-dumper": "^3.4" 13 | }, 14 | "autoload-dev": { 15 | "files": [ 16 | "vendor/symfony/var-dumper/Resources/functions/dump.php" 17 | ] 18 | }, 19 | "autoload": { 20 | "psr-4": {"Instapp\\": "src"} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /instapp-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(new Command\FollowInLocationCommand($app)); 14 | $console->add(new Command\FollowInHashtagCommand($app)); 15 | $console->add(new Command\FollowUserFollowersCommand($app)); 16 | $console->add(new Command\LikeTimelineCommand($app)); 17 | $console->run(); 18 | -------------------------------------------------------------------------------- /src/Command/FollowInHashtagCommand.php: -------------------------------------------------------------------------------- 1 | setName('follow:hashtag') 23 | ->setDescription('Follow users in hashtag') 24 | ->setHelp('Follow all users in hashtag') 25 | 26 | ->addOption('max', null, InputArgument::OPTIONAL, 'Max follow count') 27 | ->addOption('hashtags', null, InputArgument::OPTIONAL, 'Hashtags (commas)') 28 | ; 29 | } 30 | 31 | protected function init() 32 | { 33 | $hashtags = $this->getHashtags(); 34 | 35 | $this->instapp['follow']->waitTime = $this->getWaitTime(); 36 | $this->instapp['follow']->maxFollowCount = $this->getFollowCount(); 37 | 38 | foreach ($hashtags as $hashtag) 39 | { 40 | $this->instapp['follow']->followUsersInHashtag($hashtag); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Command/FollowInLocationCommand.php: -------------------------------------------------------------------------------- 1 | setName('follow:location') 23 | ->setDescription('Follow users by location') 24 | ->setHelp('Follow all users in a location') 25 | 26 | ->addOption('max', null, InputArgument::OPTIONAL, 'Max follow count') 27 | ->addOption('locations', null, InputArgument::OPTIONAL, 'Location ids (commas)') 28 | ; 29 | } 30 | 31 | protected function init() 32 | { 33 | $locations = $this->getLocations(); 34 | 35 | $this->instapp['follow']->waitTime = $this->getWaitTime(); 36 | $this->instapp['follow']->maxFollowCount = $this->getFollowCount(); 37 | 38 | foreach ($locations as $locationId) 39 | { 40 | $this->instapp['follow']->followUsersInLocation($locationId); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Command/FollowUserFollowersCommand.php: -------------------------------------------------------------------------------- 1 | setName('follow:user:follower') 23 | ->setDescription('Follow a user\'s followers') 24 | ->setHelp('Follow all users in a user\'s follower list') 25 | 26 | ->addOption('max', null, InputArgument::OPTIONAL, 'Max follow count') 27 | ->addOption('persons', null, InputArgument::OPTIONAL, 'Users (username or id) (commas)') 28 | ; 29 | } 30 | 31 | protected function init() 32 | { 33 | $persons = $this->getPersons(); 34 | 35 | $this->instapp['follow']->waitTime = $this->getWaitTime(); 36 | $this->instapp['follow']->maxFollowCount = $this->getFollowCount(); 37 | 38 | foreach ($persons as $personId) 39 | { 40 | $this->instapp['follow']->followUserFollowers($personId); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Command/InstagramCommand.php: -------------------------------------------------------------------------------- 1 | instapp = $instapp; 40 | parent::__construct($name); 41 | } 42 | 43 | protected function configure() 44 | { 45 | $this->config(); 46 | 47 | $this 48 | ->addOption('username', 'u', InputArgument::OPTIONAL, 'Account username') 49 | ->addOption('password', 'p', InputArgument::OPTIONAL, 'Account password') 50 | ->addOption('wait', null, InputArgument::OPTIONAL, 'Wait time') 51 | ; 52 | } 53 | 54 | protected function execute(InputInterface $input, OutputInterface $output) 55 | { 56 | $this->instapp['logger']->title("Instagram Api Bot: {$this->getDescription()}"); 57 | 58 | $this->input = $input; 59 | $this->output = $output; 60 | 61 | $this->login(); 62 | 63 | $this->init(); 64 | } 65 | 66 | protected function login($useOption = true) 67 | { 68 | $loginData = $this->getLoginData($useOption); 69 | 70 | try { 71 | $this->instapp->login($loginData['username'], $loginData['password']); 72 | } catch (\Exception $e) { 73 | $this->instapp['logger']->error($e->getMessage()); 74 | return $this->login(false); 75 | } 76 | } 77 | 78 | abstract protected function config(); 79 | abstract protected function init(); 80 | } -------------------------------------------------------------------------------- /src/Command/LikeTimelineCommand.php: -------------------------------------------------------------------------------- 1 | setName('like:timeline') 22 | ->setDescription('Like timeline feeds') 23 | ->setHelp('Like all posts in timeline') 24 | 25 | ->addOption('max', null, InputArgument::OPTIONAL, 'Max like count') 26 | ; 27 | } 28 | 29 | protected function init() 30 | { 31 | $this->instapp['like']->maxLikeCount = $this->getLikeCount(); 32 | $this->instapp['like']->waitTime = $this->getWaitTime(); 33 | 34 | $this->instapp['like']->likeTimeline(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/FollowCount.php: -------------------------------------------------------------------------------- 1 | input->getOption('max')) 17 | return $this->input->getOption('max'); 18 | 19 | $default = $default ?: 500; 20 | 21 | $helper = $this->getHelper('question'); 22 | 23 | $question = new Question("Max follow count ({$default}): ", $default); 24 | 25 | return $helper->ask($this->input, $this->output, $question); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/Hashtag.php: -------------------------------------------------------------------------------- 1 | input->getOption('hashtags')) 16 | { 17 | return array_map('trim', explode(',', $this->input->getOption('hashtags'))); 18 | } 19 | 20 | $hashtags = []; 21 | 22 | $helper = $this->getHelper('question'); 23 | 24 | while (true) 25 | { 26 | $question = new Question('Add hashtag (press to stop adding): '); 27 | $hashtag = $helper->ask($this->input, $this->output, $question); 28 | 29 | if (empty($hashtag)) 30 | break; 31 | 32 | $hashtags[] = $hashtag; 33 | } 34 | 35 | return $hashtags; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/LikeCount.php: -------------------------------------------------------------------------------- 1 | input->getOption('max')) 17 | return $this->input->getOption('max'); 18 | 19 | $default = $default ?: 1000; 20 | 21 | $helper = $this->getHelper('question'); 22 | 23 | $question = new Question("Max like count ({$default}): ", $default); 24 | 25 | return $helper->ask($this->input, $this->output, $question); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/Location.php: -------------------------------------------------------------------------------- 1 | input->getOption('locations')) 16 | { 17 | return array_map('trim', explode(',', $this->input->getOption('locations'))); 18 | } 19 | 20 | $locations = []; 21 | 22 | $helper = $this->getHelper('question'); 23 | 24 | while (true) 25 | { 26 | $question = new Question('Add location id (press to stop adding id): '); 27 | $question->setValidator( 28 | function($value) 29 | { 30 | if (!empty($value) && !is_numeric($value)) { 31 | throw new \InvalidArgumentException('The location id must be numeric'); 32 | } 33 | 34 | return $value; 35 | } 36 | ); 37 | $location_id = $helper->ask($this->input, $this->output, $question); 38 | 39 | if (empty($location_id)) 40 | break; 41 | 42 | $locations[] = $location_id; 43 | } 44 | 45 | return $locations; 46 | } 47 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/Login.php: -------------------------------------------------------------------------------- 1 | input->getOption('username'); 18 | $password = $this->input->getOption('password'); 19 | 20 | if (!($useOption && $username && $password)) 21 | { 22 | $helper = $this->getHelper('question'); 23 | 24 | $question = new Question('Account username: '); 25 | $question->setValidator(function ($value) { return is_null($value) ?: $value; }); 26 | 27 | $username = $helper->ask($this->input, $this->output, $question); 28 | 29 | $question = new Question('Account password: '); 30 | $question->setValidator(function ($value) { return is_null($value) ?: $value; }); 31 | $question->setHidden(true); 32 | //$question->setHiddenFallback(false); 33 | 34 | $password = $helper->ask($this->input, $this->output, $question); 35 | } 36 | 37 | return ['username' => $username, 'password' => $password, 'u' => $username, 'p' => $password]; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/Persons.php: -------------------------------------------------------------------------------- 1 | input->getOption('persons')) 17 | { 18 | return array_map('trim', explode(',', $this->input->getOption('persons'))); 19 | } 20 | 21 | $persons = []; 22 | $helper = $this->getHelper('question'); 23 | 24 | while (true) 25 | { 26 | $question = new Question('Add person username or id (press to stop adding person): '); 27 | 28 | $person = $helper->ask($this->input, $this->output, $question); 29 | 30 | if (empty($person) && (count($persons) || !$required)) 31 | break; 32 | 33 | $persons[] = $person; 34 | } 35 | 36 | return $persons; 37 | } 38 | } -------------------------------------------------------------------------------- /src/Command/Traits/Data/WaitTime.php: -------------------------------------------------------------------------------- 1 | input->getOption('wait')) 22 | return $this->input->getOption('wait'); 23 | 24 | $default = $default ?: ($this->waitTime ?: 0); 25 | 26 | $helper = $this->getHelper('question'); 27 | 28 | $question = new Question("Wait time({$default}): ", $default); 29 | 30 | return $helper->ask($this->input, $this->output, $question); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Event/FollowEvent.php: -------------------------------------------------------------------------------- 1 | user = $userInfo; 21 | $this->status = $status; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Event/LikeEvent.php: -------------------------------------------------------------------------------- 1 | item = $item; 21 | $this->status = $status; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Event/LogEvent.php: -------------------------------------------------------------------------------- 1 | message = $message; 21 | $this->type = $type; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Event/LoggedEvent.php: -------------------------------------------------------------------------------- 1 | Event\LoggedEvent::NAME, 11 | 'log' => Event\LogEvent::NAME, 12 | 'follow' => Event\FollowEvent::NAME, 13 | 'like' => Event\LikeEvent::NAME, 14 | ]; 15 | 16 | public static function get($name) 17 | { 18 | if (array_key_exists($name, self::$events)) 19 | return self::$events[$name]; 20 | 21 | throw new EventNotFoundException("Event \"{$name}\" not found"); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Exception/EventNotFoundException.php: -------------------------------------------------------------------------------- 1 | dispatcher = new EventDispatcher(); 39 | } 40 | 41 | /** 42 | * @param string $username 43 | * @param string $password 44 | */ 45 | public function login($username = null, $password = null) 46 | { 47 | if ($username) $this['instagram.username'] = $username; 48 | if ($password) $this['instagram.password'] = $password; 49 | 50 | try { 51 | $this['api']->login($this['instagram.username'], $this['instagram.password']); 52 | } catch (UnknownIdentifierException $e) { 53 | throw new LoginDataRequiredException("Set username and password"); 54 | } catch (InstagramException $e) { 55 | throw new InvalidLoginDataException("Username or password incorrect"); 56 | } 57 | 58 | $this->dispatcher->dispatch(LoggedEvent::NAME, new LoggedEvent()); 59 | } 60 | 61 | /** 62 | * Add listener 63 | * 64 | * @param string $name 65 | * @param callable $listener 66 | * @param integer $priority 67 | * @return Instapp 68 | */ 69 | public function on($name, $listener, $priority = 0) 70 | { 71 | $this->dispatcher->addListener($name, $listener, $priority); 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * @param string $macro 78 | * @param mixed $args 79 | * @return mixed 80 | */ 81 | public function macro($macro, $args) 82 | { 83 | $macro = new $macro($this); 84 | 85 | if (!$macro instanceof Macro) 86 | throw new \InvalidArgumentException('Callable macro class must be instance of Macro.'); 87 | 88 | return call_user_func([$macro, 'run'], $args); 89 | } 90 | 91 | /** 92 | * IDE Helper 93 | * @return User|Follow|Logger|Instagram|Like 94 | */ 95 | public function offsetGet($id) 96 | { 97 | return parent::offsetGet($id); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Macro.php: -------------------------------------------------------------------------------- 1 | app = $app; 19 | } 20 | 21 | /** 22 | * Abstract. 23 | * @return bool 24 | */ 25 | public function run() {} 26 | } -------------------------------------------------------------------------------- /src/Macro/FollowContentsOfPost.php: -------------------------------------------------------------------------------- 1 | post = $post; 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * @param int $followField 34 | * @return bool 35 | */ 36 | public function run($followField = self::ALL) 37 | { 38 | if (self::OWNER & $followField) 39 | { 40 | $this->app['follow']->followUser($this->post->getUser()); 41 | } 42 | 43 | if ((self::USERTAG & $followField) && $this->post->getUsertags()) 44 | { 45 | foreach ($this->post->getUsertags()->getIn() as $tag) 46 | { 47 | $this->app['follow']->followUser($tag->getUser()); 48 | } 49 | } 50 | 51 | if ((self::COMMENT & $followField) && $this->post->getHasMoreComments()) 52 | { 53 | foreach ($this->post->getPreviewComments() as $comment) 54 | { 55 | $this->app['follow']->followUser($comment->getUser()); 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Macro/FollowUserFollowers.php: -------------------------------------------------------------------------------- 1 | userId = $this->app['user']->getIdFromUserdata($user) )) 19 | { 20 | $this->app['logger']->error("Person `{$user}` not found."); 21 | return; 22 | } 23 | 24 | return $this; 25 | } 26 | 27 | /** 28 | * @return bool 29 | */ 30 | public function run() 31 | { 32 | if (!$this->userId) return false; 33 | 34 | foreach ($this->app['api']->people->getFollowers($this->userId)->users as $user) 35 | { 36 | $this->app['follow']->followUser($user); 37 | } 38 | 39 | return true; 40 | } 41 | } -------------------------------------------------------------------------------- /src/Macro/FollowUsersInHashtag.php: -------------------------------------------------------------------------------- 1 | app['api']->hashtag->getFeed($hashtag); 22 | $this->hashtag = $hashtag; 23 | 24 | } catch (InstagramException $e) { 25 | 26 | $this->app['logger']->error("Hashtag not found `{$hashtag}`"); 27 | return; 28 | } 29 | 30 | return $this; 31 | } 32 | 33 | /** 34 | * @return bool 35 | */ 36 | public function run() 37 | { 38 | if (!$this->hashtag) return false; 39 | 40 | $maxId = null; 41 | 42 | do { 43 | try { 44 | $feed = $this->app['api']->hashtag->getFeed($this->hashtag, $maxId); 45 | 46 | } catch (InstagramException $e) { 47 | 48 | $this->app['logger']->error("Hashtag not found `{$this->hashtag}`"); 49 | return false; 50 | } 51 | 52 | foreach ($feed->getItems() as $item) 53 | { 54 | $this->app['follow']->followContentsOfPost($item, FollowContentsOfPost::ALL ^ FollowContentsOfPost::COMMENT); 55 | } 56 | 57 | $maxId = $feed->getNextMaxId(); 58 | } while ($maxId !== null); 59 | 60 | return true; 61 | } 62 | } -------------------------------------------------------------------------------- /src/Macro/FollowUsersInLocation.php: -------------------------------------------------------------------------------- 1 | app['api']->location->getFeed($locationId); 22 | $this->locationId = $locationId; 23 | // todo: test: $ig->request("locations/{$locationId}/related/") 24 | 25 | } catch (InstagramException $e) { 26 | 27 | $this->app['logger']->error("Location not found `{$locationId}`"); 28 | return; 29 | } 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * @return bool 36 | */ 37 | public function run() 38 | { 39 | if (!$this->locationId) return false; 40 | 41 | $maxId = null; 42 | 43 | do { 44 | try { 45 | $location = $this->app['api']->location->getFeed($this->locationId, $maxId); 46 | 47 | } catch (InstagramException $e) { 48 | 49 | $this->app['logger']->error("Location not found `{$this->locationId}`"); 50 | return false; 51 | } 52 | 53 | foreach ($location->getItems() as $item) 54 | { 55 | $this->app['follow']->followContentsOfPost($item, FollowContentsOfPost::ALL ^ FollowContentsOfPost::COMMENT); 56 | } 57 | 58 | $maxId = $location->getNextMaxId(); 59 | } while ($maxId !== null); 60 | 61 | return true; 62 | } 63 | } -------------------------------------------------------------------------------- /src/Macro/LikeAllPostsInTimeline.php: -------------------------------------------------------------------------------- 1 | app['api']->timeline->getTimelineFeed($maxId); 19 | 20 | foreach ($timeline->getFeedItems() as $item) 21 | { 22 | $item = $item->getMediaOrAd(); 23 | if (null !== $item && !$item->getHasLiked()) 24 | { 25 | $this->app['like']->likeMedia($item); 26 | } 27 | } 28 | 29 | $maxId = $timeline->getNextMaxId(); 30 | } 31 | while ($maxId !== null); 32 | 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Provider/FollowServiceProvider.php: -------------------------------------------------------------------------------- 1 | app = $app; 52 | 53 | $this->startedAt = new \DateTime(); 54 | 55 | $this->takeRequested(); 56 | } 57 | 58 | /** 59 | * Follow a user 60 | * @param mixed $user 61 | * @throws MaxFollowCountException 62 | */ 63 | public function followUser($user) 64 | { 65 | if ($this->isRequested($user)) 66 | return; 67 | 68 | if ($this->followCount >= $this->maxFollowCount) 69 | { 70 | try { 71 | throw new MaxFollowCountException("Max follow count ({$this->maxFollowCount}) was reached."); 72 | } catch (MaxFollowCountException $e) { 73 | $this->app['logger']->add($e->getMessage()); 74 | return; 75 | } finally { 76 | $this->status(); 77 | } 78 | } 79 | 80 | $userId = $this->app['user']->getIdFromUserdata($user); 81 | 82 | $this->app['api']->people->follow($userId); 83 | 84 | $this->app->dispatcher->dispatch(FollowEvent::NAME, new FollowEvent($this->app['api']->people->getInfoById($userId)->getUser(), true)); 85 | 86 | $this->app['logger'] 87 | ->add( 88 | sprintf( 89 | '(%s) `%s` - followed!', 90 | ++$this->followCount, 91 | $this->app['user']->getUsername($user) 92 | ) 93 | ) 94 | ; 95 | 96 | $this->addRequested($user); 97 | $this->wait(); 98 | } 99 | 100 | /** 101 | * @return array 102 | */ 103 | public function status() 104 | { 105 | $result = [ 106 | 'followed' => $this->followCount, 107 | 'time' => (new \DateTime())->diff($this->startedAt) 108 | ]; 109 | 110 | $this->app['logger']->end(sprintf( 111 | '%s user requested in %s hours, %s minutes', 112 | $result['followed'], 113 | $result['time']->h, 114 | $result['time']->i 115 | )); 116 | 117 | return $result; 118 | } 119 | 120 | /** 121 | * @param mixed $user 122 | * @return bool 123 | */ 124 | public function isRequested($user) 125 | { 126 | return isset($this->requestedUsers[$this->app['user']->getUsernameFromUserdata($user)]); 127 | } 128 | 129 | /** 130 | * Add requested user 131 | * @param mixed $user 132 | */ 133 | public function addRequested($user) 134 | { 135 | $this->requestedUsers[$this->app['user']->getUsernameFromUserdata($user)] = true; 136 | } 137 | 138 | /** 139 | * Get requested users 140 | * @return array 141 | */ 142 | public function getRequested() 143 | { 144 | return array_keys($this->requestedUsers); 145 | } 146 | 147 | /** 148 | * Take following users and followers 149 | */ 150 | private function takeRequested() 151 | { 152 | foreach ( 153 | array_merge( 154 | $this->app['api']->people->getSelfFollowers()->getUsers(), 155 | $this->app['api']->people->getSelfFollowing()->getUsers() 156 | ) as $user 157 | ) { 158 | $this->addRequested($user); 159 | } 160 | } 161 | 162 | # MACROS 163 | 164 | /** 165 | * Follow users in contents of post 166 | * @param Item $post 167 | * @param integer $field 168 | * @return bool 169 | */ 170 | public function followContentsOfPost(Item $post, $field = FollowContentsOfPost::ALL) 171 | { 172 | return (new FollowContentsOfPost($this->app)) 173 | ->setItem($post) 174 | ->run($field) 175 | ; 176 | } 177 | 178 | /** 179 | * @param integer $locationId 180 | * @return bool 181 | */ 182 | public function followUsersInLocation($locationId) 183 | { 184 | return (new FollowUsersInLocation($this->app)) 185 | ->setLocation($locationId) 186 | ->run() 187 | ; 188 | } 189 | 190 | /** 191 | * @param integer $hashtag 192 | * @return bool 193 | */ 194 | public function followUsersInHashtag($hashtag) 195 | { 196 | return (new FollowUsersInHashtag($this->app)) 197 | ->setHashtag($hashtag) 198 | ->run() 199 | ; 200 | } 201 | 202 | /** 203 | * @param mixed $user 204 | * @return bool 205 | */ 206 | public function followUserFollowers($user) 207 | { 208 | return (new FollowUserFollowers($this->app)) 209 | ->setUser($user) 210 | ->run() 211 | ; 212 | } 213 | } -------------------------------------------------------------------------------- /src/Service/Like.php: -------------------------------------------------------------------------------- 1 | app = $app; 43 | $this->startedAt = new \DateTime(); 44 | } 45 | 46 | /** 47 | * @param Item $item 48 | */ 49 | public function likeMedia(Item $item) 50 | { 51 | if ($this->likeCount >= $this->maxLikeCount) 52 | { 53 | try { 54 | throw new MaxLikeCountException("Max like count ({$this->maxLikeCount}) was reached."); 55 | } catch (MaxLikeCountException $e) { 56 | $this->app['logger']->add($e->getMessage()); 57 | return; 58 | } finally { 59 | $this->status(); 60 | return; 61 | } 62 | } 63 | 64 | $this->app['api']->media->like($item->getId()); 65 | 66 | $this->app->dispatcher->dispatch(LikeEvent::NAME, new LikeEvent($item, true)); 67 | 68 | $this->app['logger']->add(sprintf( 69 | '(%s) - `%s - instagram.com/p/%s` - liked!', 70 | ++$this->likeCount, 71 | $item->getUser()->getUsername(), 72 | $item->getCode() 73 | )); 74 | 75 | $this->wait(); 76 | } 77 | 78 | /** 79 | * @return array 80 | */ 81 | public function status() 82 | { 83 | $result = [ 84 | 'liked' => $this->likeCount, 85 | 'time' => (new \DateTime())->diff($this->startedAt) 86 | ]; 87 | 88 | $this->app['logger']->end(sprintf( 89 | '%s post liked in %s hours, %s minutes', 90 | $result['liked'], 91 | $result['time']->h, 92 | $result['time']->i 93 | )); 94 | 95 | return $result; 96 | } 97 | 98 | # MACROS 99 | 100 | public function likeTimeline() 101 | { 102 | $this->app['logger']->start('Scrolling down :)'); 103 | return (new LikeAllPostsInTimeline($this->app))->run(); 104 | } 105 | } -------------------------------------------------------------------------------- /src/Service/Logger.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Logger 15 | { 16 | const NORMAL = 'normal'; 17 | const INFO = 'info'; 18 | const ERROR = 'error'; 19 | const TITLE = 'title'; 20 | const FOOTER = 'footer'; 21 | 22 | /** @var bool */ 23 | protected $enabled = true; 24 | 25 | /** @var OutputInterface */ 26 | protected $output; 27 | 28 | /** @var Instapp */ 29 | protected $app; 30 | 31 | /** @var string[] */ 32 | protected $type = [null]; 33 | 34 | /** 35 | * Logger constructor. 36 | */ 37 | public function __construct(Instapp $app) 38 | { 39 | $this->app = $app; 40 | $this->output = new ConsoleOutput(); 41 | } 42 | 43 | /** 44 | * @param string|array $message 45 | * @return Logger 46 | */ 47 | public function write($message) 48 | { 49 | if ($this->enabled) 50 | $this->output->writeln($message); 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * @param string|array $message 57 | * @param string $type 58 | * @return Logger 59 | */ 60 | public function add($message = '', $type = self::NORMAL) 61 | { 62 | if (is_array($message)) 63 | foreach ($message as $item) 64 | return $this->add($item, $type); 65 | 66 | if (!empty($message)) 67 | $this->app->dispatcher->dispatch(LogEvent::NAME, new LogEvent($message, $type)); 68 | 69 | switch ($type) 70 | { 71 | case self::ERROR : $message = "{$message}"; break; 72 | case self::INFO : $message = "{$message}"; break; 73 | case self::TITLE : $message = ["{$message}", str_repeat('=', max(array_map('strlen', (array)$message)))]; break; 74 | case self::FOOTER: $message = [str_repeat('=', max(array_map('strlen', (array)$message))), "{$message}"]; break; 75 | case self::NORMAL : break; 76 | default : break; 77 | } 78 | 79 | $this->write($message); 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * @param string|array $message 86 | * @return Logger 87 | */ 88 | public function error($message) 89 | { 90 | return $this->add($message, self::ERROR); 91 | } 92 | 93 | /** 94 | * @param string|array $message 95 | * @return Logger 96 | */ 97 | public function info($message) 98 | { 99 | return $this->add($message, self::INFO); 100 | } 101 | 102 | /** 103 | * @param string|array $message 104 | * @return Logger 105 | */ 106 | public function title($message) 107 | { 108 | return $this->add($message, self::TITLE); 109 | } 110 | 111 | /** 112 | * @param string|array $message 113 | * @return Logger 114 | */ 115 | public function footer($message) 116 | { 117 | return $this->add($message, self::FOOTER); 118 | } 119 | 120 | /** 121 | * @param string|array $message 122 | * @param string $type 123 | * @return Logger 124 | */ 125 | public function log($message, $type = self::NORMAL) 126 | { 127 | return $this 128 | ->add() 129 | ->add($message, $type) 130 | ->add() 131 | ; 132 | } 133 | 134 | /** 135 | * @param string|array $message 136 | * @param string $type 137 | * @return Logger 138 | */ 139 | public function start($message, $type = self::TITLE) 140 | { 141 | $this->type[] = $type; 142 | return $this->add()->add($message, $type); 143 | } 144 | 145 | /** 146 | * @param string|array $message 147 | * @return Logger 148 | */ 149 | public function end($message = '') 150 | { 151 | $this->add($message, self::FOOTER); 152 | 153 | array_shift($this->type); 154 | 155 | return $this; 156 | } 157 | 158 | /** 159 | * Enable logging 160 | */ 161 | public function enable() 162 | { 163 | $this->enabled = true; 164 | } 165 | 166 | /** 167 | * Disable logging 168 | */ 169 | public function disable() 170 | { 171 | $this->enabled = false; 172 | } 173 | } -------------------------------------------------------------------------------- /src/Service/User.php: -------------------------------------------------------------------------------- 1 | app = $app; 18 | } 19 | 20 | /**< 21 | * @param UserModel|string|integer|array $userdata 22 | * @param bool $validate 23 | * @return integer|false user id 24 | * @throws \InvalidArgumentException 25 | */ 26 | public function getIdFromUserdata($userdata, $validate = true) 27 | { 28 | if ($userdata instanceof User && $userdata = $userdata->getUserId() ?: $userdata->getUsername()) 29 | { 30 | return $this->getIdFromUserdata($userdata); 31 | } 32 | elseif (is_object($userdata) && $userdata = @$userdata->getUserId() ?: @($userdata->getId() ?: $userdata->getUsername()) ) 33 | { 34 | return $this->getIdFromUserdata($userdata); 35 | } 36 | elseif (is_array($userdata) && $userdata = $userdata['username'] ?: $userdata['id']) 37 | { 38 | return $this->getIdFromUserdata($userdata); 39 | } 40 | elseif (is_numeric($userdata)) // maybe id 41 | { 42 | if ($validate) 43 | if (!$this->app['api']->people->getInfoById($userdata)->isUser()) 44 | return false; 45 | 46 | return $userdata; 47 | } 48 | elseif (is_string($userdata)) // maybe username 49 | { 50 | try { 51 | return $this->app['api']->people->getUserIdForName($userdata); 52 | } catch (\Exception $e) { 53 | return; 54 | } 55 | } 56 | 57 | throw new \InvalidArgumentException("Invalid argument for getting user id"); 58 | } 59 | 60 | /** 61 | * @param UserModel|string|integer|array $userdata 62 | * @return integer|false user id 63 | * @throws \InvalidArgumentException 64 | */ 65 | public function getUsernameFromUserdata($userdata) 66 | { 67 | if ($userdata instanceof User && $userdata = $userdata->getUserId() ?: $userdata->getUsername()) 68 | { 69 | return $this->getUsernameFromUserdata($userdata); 70 | } 71 | elseif (is_object($userdata) && $userdata = @$userdata->getUserId() ?: @($userdata->getId() ?: $userdata->getUsername()) ) 72 | { 73 | return $this->getUsernameFromUserdata($userdata); 74 | } 75 | elseif (is_array($userdata) && $userdata = $userdata['username'] ?: ($userdata['id'] ?: $userdata['user_id'])) 76 | { 77 | return $this->getUsernameFromUserdata($userdata); 78 | } 79 | elseif (is_numeric($userdata)) 80 | { 81 | if ($id = $this->getIdFromUserdata($userdata)) 82 | { 83 | return $this->app['api']->people->getInfoById($id)->getUser()->getUsername(); 84 | } 85 | } 86 | elseif (is_string($userdata)) 87 | { 88 | return $userdata; 89 | } 90 | 91 | throw new \InvalidArgumentException("Invalid argument for getting user id"); 92 | } 93 | 94 | /** 95 | * @param mixed $user 96 | * @return string username 97 | */ 98 | public function getUsername($user) 99 | { 100 | return $this->app['api']->people->getInfoById($this->getIdFromUserdata($user))->getUser()->getUsername(); 101 | } 102 | } -------------------------------------------------------------------------------- /src/Traits/Wait.php: -------------------------------------------------------------------------------- 1 | app['logger']->add('...'); 15 | sleep($this->waitTime); 16 | } 17 | } --------------------------------------------------------------------------------