├── .bumpversion.cfg ├── .github ├── DONATE_BITCOIN.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question-others.md ├── PULL_REQUEST_TEMPLATE.md ├── issue-close-app.yml └── stale.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST ├── OLD_README.md ├── Pipfile ├── Pipfile.lock ├── codecov.yml ├── examples ├── approve_thread_requests.py ├── archive_medias.py ├── autopost │ ├── README.md │ ├── auto_post.py │ └── pics.txt ├── black-whitelist │ ├── black_white_lists.py │ ├── blacklist.txt │ ├── whitelist.txt │ └── whitelist_generator.py ├── block_bots.py ├── collect_stats.py ├── comment │ ├── comment_hashtags.py │ ├── comment_your_feed.py │ ├── comments.txt │ ├── comments_emoji.txt │ └── reply_to_media_comments.py ├── comment_medias_by_location.py ├── config │ ├── blacklist.txt │ ├── comments.txt │ ├── followed.txt │ ├── friends.txt │ ├── hashtag_database.txt │ ├── setting_multiscript.txt │ ├── skipped.txt │ ├── unfollowed.txt │ ├── username_database.txt │ └── whitelist.txt ├── delete_all_posts.py ├── direct_send_photo.py ├── download_photos_by_hashtag.py ├── download_photos_by_user.py ├── download_stories.py ├── download_user_videos.py ├── download_your_photos.py ├── filter_blacklist_hashtag_medias.py ├── filter_private_profiles.py ├── follow_last_user_media_likers.py ├── follow_requests.py ├── follow_user_followers.py ├── follow_user_following.py ├── follow_users_by_hashtag.py ├── follow_users_from_file.py ├── get_followers_or_followings_to_file.py ├── get_hashtags_from_keywords.py ├── infinity_feedliker.py ├── infinity_hashtags_follower.py ├── infinity_hashtags_liker.py ├── interact_DM.py ├── like_and_follow_last_user_media_likers.py ├── like_and_follow_media_likers.py ├── like_and_follow_your_last_media_likers.py ├── like_example.py ├── like_hashtags.py ├── like_hashtags_from_file.py ├── like_location_feed.py ├── like_medias_by_location.py ├── like_timeline_feed.py ├── like_user_followers.py ├── like_user_following.py ├── like_users.py ├── like_users_from_file.py ├── like_your_last_media_likers.py ├── message_users.py ├── messages.csv ├── multi_script_CLI.py ├── photos │ ├── README.md │ ├── captions_for_medias.py │ ├── pics.txt │ └── upload_photos.py ├── repost_best_photos_from_users.py ├── repost_photo.py ├── reset_following.py ├── save_unfollowers_into_file.py ├── save_users_followers_into_file.py ├── save_users_following_into_file.py ├── stories │ └── watch_user_likers_stories.py ├── ultimate │ ├── follow_followers.txt │ ├── follow_following.txt │ ├── like_hashtags.txt │ ├── like_users.txt │ └── ultimate.py ├── ultimate_schedule │ ├── config.py │ ├── config │ │ ├── blacklist.txt │ │ ├── comments.txt │ │ ├── followed.txt │ │ ├── friends.txt │ │ ├── hashtag_database.txt │ │ ├── photo_captions.txt │ │ ├── pics.txt │ │ ├── skipped.txt │ │ ├── unfollowed.txt │ │ ├── username_database.txt │ │ └── whitelist.txt │ ├── readme.md │ └── ultimate.py ├── unarchive_your_medias.py ├── unfollow_everyone.py ├── unfollow_non_followers.py ├── unlike_users.py ├── upload_story_photo.py ├── video │ ├── README.md │ ├── captions_for_medias.py │ └── upload_video.py └── welcome_message.py ├── instabot ├── __init__.py ├── api │ ├── __init__.py │ ├── api.py │ ├── api_login.py │ ├── api_photo.py │ ├── api_story.py │ ├── api_video.py │ ├── config.py │ ├── devices.py │ └── prepare.py ├── bot │ ├── __init__.py │ ├── bot.py │ ├── bot_archive.py │ ├── bot_block.py │ ├── bot_checkpoint.py │ ├── bot_comment.py │ ├── bot_delete.py │ ├── bot_direct.py │ ├── bot_filter.py │ ├── bot_follow.py │ ├── bot_get.py │ ├── bot_like.py │ ├── bot_photo.py │ ├── bot_stats.py │ ├── bot_story.py │ ├── bot_support.py │ ├── bot_unfollow.py │ ├── bot_unlike.py │ ├── bot_video.py │ └── state │ │ ├── __init__.py │ │ ├── bot_cache.py │ │ └── bot_state.py ├── singleton.py └── utils.py ├── push.sh ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── test_bot.py ├── test_bot_comment.py ├── test_bot_filter.py ├── test_bot_follow.py ├── test_bot_get.py ├── test_bot_like.py ├── test_bot_support.py ├── test_bot_unlike.py └── test_variables.py ├── tox.ini └── tox_install_command.sh /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.117.0 3 | tag = True 4 | commit = True 5 | message = Change version: {current_version} -> {new_version} [ci skip] 6 | 7 | [bumpversion:file:setup.py] 8 | 9 | [bumpversion:file:instabot/api/api.py] 10 | 11 | [bumpversion:file:instabot/bot/bot.py] 12 | 13 | -------------------------------------------------------------------------------- /.github/DONATE_BITCOIN.md: -------------------------------------------------------------------------------- 1 | # Thank you for your donation! 2 | 3 | All your donations will be used for the project maintainment and development. If you want to donate another cryptocurrency please [contact](https://okhlopkov.com). 4 | 5 | Bitcoin Address: `18NPJHxK9xuYpHDbjdpWdNGVj2dgo9bLx2`. 6 | 7 | ![QR code](https://bitref.com/qr.php?data=18NPJHxK9xuYpHDbjdpWdNGVj2dgo9bLx2) 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ohld] 4 | patreon: morejust 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | custom: https://paypal.me/okhlopkov/10 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve. If you do not use the template, the bug report will be closed 4 | title: "[BUG] CHANGE THIS TITLE TO YOUR ISSUE" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | **Example file** 16 | 17 | ``` 18 | If you ran an example file specify it here. 19 | ``` 20 | 21 | **Describe the bug** 22 | 23 | A clear and concise description of what the bug is. 24 | 25 | **Log** 26 | 27 | ``` 28 | here 29 | ``` 30 | 31 | **To Reproduce** 32 | 33 | (EXAMPLE:) Steps to reproduce the behavior: 34 | 1. Run file: 35 | 2. Edit '...' 36 | 3. Run '....' 37 | 38 | **Expected behavior** 39 | 40 | A clear and concise description of what you expected to happen. 41 | 42 | **Additional context** 43 | 44 | Add any other context about the problem here. 45 | 46 | **Version** 47 | 48 | Python version (`python -v`): 49 | 50 | Operating Systeem: Win/Linux/MacOS 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question-others.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question/Others 3 | about: Not an error or feature request 4 | title: "[QUESTION] CHANGE THIS TITLE TO YOUR QUESTION" 5 | labels: 'question' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 19 | 20 | ### Before submitting an issue, make sure you have: 21 | - [ ] Updated to the lastest version 22 | - [ ] Read the [README](https://github.com/instagrambot/instabot/README.md) 23 | - [ ] [Searched](https://github.com/instagrambot/instabot/search?type=Issues) the bugtracker for similar issues including **closed** ones 24 | - [ ] Reviewed the sample code in [tests](https://github.com/instagrambot/instabot/tree/master/tests) and [examples](https://github.com/instagrambot/instabot/tree/master/examples) 25 | 26 | ### Which example file are you using? 27 | 28 | ``` 29 | example.py 30 | or delete this if it has nothing to do with the code 31 | ``` 32 | 33 | --- 34 | 35 | ### Describe your Question/Issue: 36 | 37 | Please make sure the description is worded well enough to be understood with as much context and examples as possible. 38 | 39 | --- 40 | 41 | Paste the output of ``python -V`` here: 42 | 43 | Code you are using: 44 | 45 | ```python 46 | POST IT HERE 47 | ``` 48 | 49 | Error/Debug Log: 50 | 51 | ``` 52 | POST IT HERE 53 | ``` 54 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | **What this PR does / why we need it**: 8 | 9 | **Which issue(s) this PR fixes**: 10 | 15 | Fixes # 16 | 17 | **Special notes for your reviewer**: 18 | 19 | **Does this PR introduce a user-facing change?**: 20 | 25 | ```release-note 26 | 27 | ``` 28 | 29 | **Additional documentation e.g. usage docs, etc.**: 30 | 31 | 34 | ```docs 35 | 36 | ``` 37 | -------------------------------------------------------------------------------- /.github/issue-close-app.yml: -------------------------------------------------------------------------------- 1 | # CLOSE ISSUE BOT 2 | # --------------- 3 | # A bot which helps you to close issues that don't include some specific contents. 4 | # See how to use it in https://github.com/offu/close-issue-app. 5 | 6 | # Comment that will be sent if an issue is judged to be closed. 7 | comment: >- 8 | This issue has been automatically closed because the issue template is missing or incomplete. 9 | Filling the template is required so standard questions don't need to be asked again each time. 10 | Our ability to provide assistance is greatly hampered if few minutes are not taken to complete the issue template 11 | with the requested information. The details requested potentially affect which options to pursue. The small amount 12 | of time you will spend completing the template will also help the volunteers, providing assistance to you, to reduce 13 | the time required to help you. 14 | 15 | issueConfigs: 16 | # There can be several configs for different kind of issues. 17 | - content: 18 | # template 1: bug report 19 | - "Example file" 20 | - "Log" 21 | - "To Reproduce" 22 | - "Version" 23 | - content: 24 | # template 2: question 25 | - "Before submitting an issue, make sure you have:" 26 | - "Describe your Question/Issue:" 27 | - content: 28 | # template 3: feature_request 29 | - "Is your feature request related to a problem? Please describe" 30 | - "Describe the solution you'd like" 31 | 32 | # Optional configuration: 33 | # 34 | # whether the keywords are case-insensitive 35 | # default value is false, which means keywords are case-sensitive 36 | caseInsensitive: false 37 | # the label that will be added when the bot close an issue 38 | # The bot will only add a label if this property is set. 39 | label: "template missing/incomplete" 40 | # The issue is judged to be legal if it includes all keywords from any of these two configs. 41 | # Or it will be closed by the app. 42 | exception: 43 | - "bruvv" 44 | - "dependabot-preview" 45 | - "ohld" 46 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 60 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - pinned 16 | - security 17 | - enhancement 18 | - "[Status] Maybe Later" 19 | 20 | # Set to true to ignore issues in a project (defaults to false) 21 | exemptProjects: false 22 | 23 | # Set to true to ignore issues in a milestone (defaults to false) 24 | exemptMilestones: false 25 | 26 | # Set to true to ignore issues with an assignee (defaults to false) 27 | exemptAssignees: false 28 | 29 | # Label to use when marking as stale 30 | staleLabel: stale 31 | 32 | # Comment to post when marking as stale. Set to `false` to disable 33 | markComment: > 34 | This issue has been automatically marked as stale because it has not had 35 | recent activity. It will be closed if no further activity occurs. Thank you 36 | for your contributions. 37 | 38 | # Comment to post when removing the stale label. 39 | # unmarkComment: > 40 | # Your comment here. 41 | 42 | # Comment to post when closing a stale Issue or Pull Request. 43 | # closeComment: > 44 | # Your comment here. 45 | 46 | # Limit the number of actions per hour, from 1-30. Default is 30 47 | limitPerRun: 30 48 | 49 | # Limit to only `issues` or `pulls` 50 | # only: issues 51 | 52 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 53 | # pulls: 54 | # daysUntilStale: 30 55 | # markComment: > 56 | # This pull request has been automatically marked as stale because it has not had 57 | # recent activity. It will be closed if no further activity occurs. Thank you 58 | # for your contributions. 59 | 60 | # issues: 61 | # exemptLabels: 62 | # - confirmed 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File with login and password 2 | secret.txt 3 | 4 | # Mac OS X stuff 5 | .DS_Store 6 | 7 | # Files with downloaded data 8 | *.tsv 9 | 10 | # Photos 11 | examples/photos/ 12 | 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | *.py[cod] 16 | *$py.class 17 | 18 | # C extensions 19 | *.so 20 | 21 | # Distribution / packaging 22 | .Python 23 | env/ 24 | build/ 25 | develop-eggs/ 26 | dist/ 27 | downloads/ 28 | eggs/ 29 | .eggs/ 30 | lib/ 31 | lib64/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *,cover 58 | .hypothesis/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # IPython Notebook 82 | .ipynb_checkpoints 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # celery beat schedule file 88 | celerybeat-schedule 89 | 90 | # dotenv 91 | .env 92 | 93 | # virtualenv 94 | venv/ 95 | ENV/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # Bot checkpoints 104 | bot_cp_* 105 | 106 | # JetBrains IDE 107 | .idea 108 | 109 | # nohup 110 | nohup.out 111 | 112 | # VS Code 113 | .vscode 114 | state/ 115 | 116 | # Settings files 117 | *.txt 118 | *.checkpoint 119 | *_uuid_and_cookie.json 120 | 121 | # Qiskit junkies 122 | *qiskit* 123 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs"] 2 | path = docs 3 | url = https://github.com/instagrambot/docs 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.5.0 4 | hooks: 5 | - id: end-of-file-fixer 6 | exclude: '.bumpversion.cfg' 7 | - id: trailing-whitespace 8 | exclude: '.bumpversion.cfg' 9 | - id: check-docstring-first 10 | - id: check-yaml 11 | - id: debug-statements 12 | - id: check-ast 13 | - repo: https://github.com/psf/black 14 | rev: 19.10b0 15 | hooks: 16 | - id: black 17 | language_version: python3 18 | - repo: https://gitlab.com/pycqa/flake8 19 | rev: 3.7.9 20 | hooks: 21 | - id: flake8 22 | args: 23 | - --max-line-length=500 24 | - --ignore=E203,E266,E501,W503,E402 25 | - --max-complexity=31 26 | - --select=B,C,E,F,W,T4,B9 27 | - repo: https://github.com/asottile/pyupgrade 28 | rev: v1.26.2 29 | hooks: 30 | - id: pyupgrade 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: xenial 3 | 4 | python: 5 | - "2.7" 6 | - "3.5" 7 | - "3.6" 8 | - "3.7" 9 | - "3.8" 10 | - "nightly" 11 | - "pypy" 12 | - "pypy3" 13 | 14 | env: 15 | - PRE_COMMIT_CMD="" 16 | 17 | matrix: 18 | include: 19 | - name: "pre-commit" 20 | python: "3.7" 21 | env: 22 | - PRE_COMMIT_CMD="pre-commit run --all-files --show-diff-on-failure" 23 | allow_failures: 24 | - python: "3.8" 25 | - python: "nightly" 26 | 27 | install: 28 | - pip install tox-travis codecov bumpversion pre_commit 29 | 30 | script: 31 | - tox 32 | - $PRE_COMMIT_CMD 33 | 34 | after_success: 35 | - codecov 36 | - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then bumpversion --verbose minor && bash push.sh; fi 37 | 38 | cache: pip 39 | 40 | notifications: 41 | webhooks: https://www.travisbuddy.com/ 42 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at Instabotproject@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Instabot 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | ## How can I help the project? 6 | 7 | You can: 8 | * Put the star into the [Instabot main repository](https://github.com/instagrambot). To do this, click on the star here https://github.com/instagrambot at top right corner. Mind that GitHub registration is required (for free). 9 | * Login to [Telegram Group](https://t.me/instabotproject) and help newcomers to understand the installation and configuration of Instabot. 10 | * Tell everywhere about our project! It will be enough to throw off the link: https://instagrambot.github.io. 11 | * Find bugs and describe them in [Issues](https://github.com/instagrambot/instabot/issues) section, be sure to attach the _screenshots_ and _commands_ that you entered. This will help correct these errors and make Instabot better! 12 | * If you are a developer, correct these bugs and errors! Do this via Pull Request, don't forget the PEP8 standard. 13 | * If you have a brilliant Instabot usage example or even the independent project connected with instagram, [tell us](https://t.me/instabotproject) about it! 14 | 15 | ## Adding the new docs 16 | If you want to add a new documentation page in any language please follow the guide below. 17 | 18 | 1. If your docs have not been written in english, please translate your doc in English too and add it into [en/](https://github.com/instagrambot/docs/blob/master/en/) folder. 19 | 2. Make sure that your doc is written descriptive enough. If you use pictures, please upload them into [img/](https://github.com/instagrambot/docs/blob/master/img/) folder. 20 | 3. Add the link to your doc into the existing docs to make other users find your page. 21 | 4. Create pull request with your docs. 22 | 23 | ## Translate the Docs into your language 24 | 25 | 1. Fork the [repository](https://github.com/instagrambot/instabot). 26 | 2. Create a folder with the name of your country in the abbreviation. 27 | 3. Copy all the files from the `/en/` folder to your earlier created folder. 28 | 4. Translate the files into your language, leaving the file structure of the previous one (paragraphs etc). 29 | 5. Add the link to your docs in the main [README.md](https://github.com/instagrambot/docs/blob/master/README.md) file. Don't forget to add the flag emoji! 30 | 6. Create pull request. 31 | 32 | ***Thank you for supporting the project!*** 33 | 34 | ## For Developers 35 | 36 | Install the dependencies using [pipenv](https://github.com/pypa/pipenv): `pipenv install` 37 | 38 | See `.travis.yml` for the most up to date test and lint commands. 39 | 40 | We use [`pre-commit`](https://pre-commit.com) to keep a consistent code style, so ``pip install pre_commit`` and run 41 | ```bash 42 | pre-commit install # only need to do this once! 43 | ``` 44 | to install the hooks. 45 | These will then automatically run upon each commit. 46 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.2-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY Pipfile . 6 | RUN pip install pipenv 7 | RUN pipenv install 8 | COPY . . 9 | 10 | VOLUME /app/examples/autopost/pics/ 11 | 12 | CMD [ "pipenv", "run", "python", "examples/multi_script_CLI.py"] 13 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | setup.cfg 3 | setup.py 4 | instabot/__init__.py 5 | instabot/utils.py 6 | instabot/api/__init__.py 7 | instabot/api/api.py 8 | instabot/api/api_photo.py 9 | instabot/api/api_video.py 10 | instabot/api/config.py 11 | instabot/api/prepare.py 12 | instabot/bot/__init__.py 13 | instabot/bot/bot.py 14 | instabot/bot/bot_archive.py 15 | instabot/bot/bot_block.py 16 | instabot/bot/bot_checkpoint.py 17 | instabot/bot/bot_comment.py 18 | instabot/bot/bot_filter.py 19 | instabot/bot/bot_follow.py 20 | instabot/bot/bot_get.py 21 | instabot/bot/bot_like.py 22 | instabot/bot/bot_photo.py 23 | instabot/bot/bot_stats.py 24 | instabot/bot/bot_support.py 25 | instabot/bot/bot_unfollow.py 26 | instabot/bot/bot_unlike.py 27 | instabot/bot/bot_video.py 28 | -------------------------------------------------------------------------------- /OLD_README.md: -------------------------------------------------------------------------------- 1 | This code is no longer maintaned. Don't try to run anything from here - you will be easily detected and banned by Instagram. 2 | 3 | Join our [Telegram community](https://t.me/instabotproject) to find more tools, projects and growth hacking methods. 4 | 5 | Read why you shouldn't run any bots: [it is no longer safe](https://likeup.me/bots-are-dead/?utm_source=instabot-github-readme). 6 | 7 | -------------------------- 8 | -------------------------- 9 | -------------------------- 10 | -------------------------- 11 | -------------------------- 12 | -------------------------- 13 | -------------------------- 14 | -------------------------- 15 | -------------------------- 16 | -------------------------- 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | # Instabot 32 | 33 | Free Instagram python bot, [private Instagram API emulator](https://github.com/instagrambot/instabot/tree/master/instabot/api) and a [huge amount of ready-to-use example scripts](https://github.com/instagrambot/instabot/tree/master/examples). 34 | 35 | We are the most used Instagram library on GitHub (according to "Used by" counter). 🎉 Join our [Large Telegram community](https://t.me/instabotproject) for more Instagram tricks. 🚀 36 | 37 | 38 | ### Important 39 | 40 | Right now the usage of this library (as probably others too) may harm your Instagram's profile because of new script-detection algorithms. You were warned. 41 | 42 | --- 43 | ### [Read the Docs](https://instagrambot.github.io/docs/) | [Contribute](https://github.com/instagrambot/docs/blob/master/CONTRIBUTING.md) 44 | --- 45 | 46 | [![Telegram Chat](https://img.shields.io/badge/chat%20on-Telegram-blue.svg)](https://t.me/instabotproject) 47 | [![paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/okhlopkov/10) 48 | ![Python 2.7, 3.5, 3.6, 3.7](https://img.shields.io/badge/python-2.7%2C%203.5%2C%203.6%2C%203.7-blue.svg) 49 | [![PyPI version](https://badge.fury.io/py/instabot.svg)](https://badge.fury.io/py/instabot) 50 | [![Build Status](https://travis-ci.org/instagrambot/instabot.svg?branch=master)](https://travis-ci.org/instagrambot/instabot) 51 | [![codecov](https://codecov.io/gh/instagrambot/instabot/branch/master/graph/badge.svg)](https://codecov.io/gh/instagrambot/instabot) 52 | 53 | ### Installation 54 | Install `instabot` with: 55 | ``` bash 56 | pip install -U instabot 57 | ``` 58 | 59 | #### or check [this](https://instagrambot.github.io/docs/en/#installation) for more details. 60 | 61 | ### Quickstart 62 | 63 | 64 | 65 | Instabot has a lot of predefined filters (like skipping very popular accounts because they will not notice you) which maximizes the overall impact. But you can remove them if you like or even set your parameters: (full list [here](https://github.com/instagrambot/instabot/blob/master/instabot/bot/bot.py#L86)) 66 | 67 | 68 | 69 | JOIN OUR TELEGRAM COMMUNITY OF INSTAGRAM GROWTH HACKERS: https://t.me/instabotproject 70 | 71 | # Terms and conditions 72 | * You will NOT run the code provided in this repo 73 | * You will NOT use this API for marketing purposes (spam, botting, harassment, massive bulk messaging...). 74 | * We do NOT give support to anyone who wants to use this API to send spam or commit other crimes. 75 | * We reserve the right to block any user of this repository that does not meet these conditions. 76 | 77 | # Legal 78 | This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by Instagram, Facebook inc. or any of its affiliates or subsidiaries. This is an independent and unofficial API. Use it at your own risk. 79 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | pytest = "*" 8 | pysocks = "*" 9 | bumpversion = "*" 10 | pytest-cov = '*' 11 | codecov = '*' 12 | 13 | [packages] 14 | tqdm = "*" 15 | requests = "*" 16 | requests-toolbelt = "*" 17 | responses = "*" 18 | six = "*" 19 | huepy = "*" 20 | pytz = "*" 21 | 22 | [requires] 23 | python_version = "3.7" 24 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # configuration related to pull request comments 2 | comment: no # do not comment PR with the result 3 | 4 | coverage: 5 | range: 50..90 # coverage lower than 50 is red, higher than 90 green, between color code 6 | 7 | status: 8 | project: # settings affecting project coverage 9 | default: 10 | target: auto # auto % coverage target 11 | threshold: 5% # allow for 5% reduction of coverage without failing 12 | 13 | # do not run coverage on patch nor changes 14 | patch: false 15 | -------------------------------------------------------------------------------- /examples/approve_thread_requests.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) approve incoming message requests. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | bot.approve_pending_thread_requests() 25 | -------------------------------------------------------------------------------- /examples/archive_medias.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Archive medias. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | from tqdm import tqdm 13 | 14 | sys.path.append(os.path.join(sys.path[0], "../")) 15 | from instabot import Bot # noqa: E402 16 | 17 | 18 | def archive_medias(bot, medias): 19 | for media in tqdm(medias, desc="Medias"): 20 | bot.archive(media) 21 | return True 22 | 23 | 24 | parser = argparse.ArgumentParser(add_help=True) 25 | parser.add_argument("-u", type=str, help="username") 26 | parser.add_argument("-p", type=str, help="password") 27 | parser.add_argument("-proxy", type=str, help="proxy") 28 | parser.add_argument("media_id", type=str, nargs="+", help="media_id") 29 | args = parser.parse_args() 30 | 31 | bot = Bot() 32 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 33 | 34 | archive_medias(bot, args.media_id) 35 | -------------------------------------------------------------------------------- /examples/autopost/README.md: -------------------------------------------------------------------------------- 1 | # auto_post 2 | Allows you to post photos regularly from the local folder 3 | 4 | ## How to run 5 | Run the script 6 | ``` 7 | python auto_post.py 8 | ``` 9 | 10 | ## Settings 11 | - photos are stored in _pics_ folder 12 | - name of the photo should look like "#-Caption-text.jpg" 13 | -- # (number) determines posts ordering 14 | -- "Caption-text" will be used to search for a description file in the same folder with the name "Caption-text.txt" 15 | -- If the description file is not found "Caption-text" is what you will see under your photo in IG (eg. "Caption text") 16 | -- Only _jpg_ files are processed 17 | - list of posted photos stored in _pics.txt_ (in order not to post one photo several times) 18 | - frequency of posting is determined by 19 | ``` 20 | timeout = 24*60*60 # 24 hours 21 | ``` 22 | 23 | ___ 24 | _by @ppleskov_ 25 | -------------------------------------------------------------------------------- /examples/autopost/auto_post.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | import time 5 | from io import open 6 | 7 | sys.path.append(os.path.join(sys.path[0], "../../")) 8 | from instabot import Bot # noqa: E402 9 | 10 | posted_pic_list = [] 11 | try: 12 | with open("pics.txt", "r", encoding="utf8") as f: 13 | posted_pic_list = f.read().splitlines() 14 | except Exception: 15 | posted_pic_list = [] 16 | 17 | timeout = 24 * 60 * 60 # pics will be posted every 24 hours 18 | 19 | bot = Bot() 20 | bot.login() 21 | 22 | while True: 23 | folder_path = "./pics" 24 | pics = glob.glob(folder_path + "/*.jpg") 25 | pics = sorted(pics) 26 | try: 27 | for pic in pics: 28 | if pic in posted_pic_list: 29 | continue 30 | 31 | pic_name = pic[:-4].split("-") 32 | pic_name = "-".join(pic_name[1:]) 33 | 34 | print("upload: " + pic_name) 35 | 36 | description_file = folder_path + "/" + pic_name + ".txt" 37 | 38 | if os.path.isfile(description_file): 39 | with open(description_file, "r") as file: 40 | caption = file.read() 41 | else: 42 | caption = pic_name.replace("-", " ") 43 | 44 | bot.upload_photo(pic, caption=caption) 45 | if bot.api.last_response.status_code != 200: 46 | print(bot.api.last_response) 47 | # snd msg 48 | break 49 | 50 | if pic not in posted_pic_list: 51 | posted_pic_list.append(pic) 52 | with open("pics.txt", "a", encoding="utf8") as f: 53 | f.write(pic + "\n") 54 | 55 | time.sleep(timeout) 56 | 57 | except Exception as e: 58 | print(str(e)) 59 | time.sleep(60) 60 | -------------------------------------------------------------------------------- /examples/autopost/pics.txt: -------------------------------------------------------------------------------- 1 | ./pics/1 First test.jpg 2 | 3 | ./pics/2 Second test.jpg 4 | -------------------------------------------------------------------------------- /examples/black-whitelist/black_white_lists.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) Reads user_ids from blacklist and whitelist 6 | 2) likes several last medias by users in your timeline 7 | 8 | Notes: 9 | blacklist and whitelist files should contain user_ids - each one on the 10 | separate line. 11 | Example: 12 | 1234125 13 | 1234124512 14 | """ 15 | 16 | import argparse 17 | import os 18 | import sys 19 | 20 | from tqdm import tqdm 21 | 22 | sys.path.append(os.path.join(sys.path[0], "../../")) 23 | from instabot import Bot # noqa: E402 24 | 25 | parser = argparse.ArgumentParser(add_help=True) 26 | parser.add_argument("-u", type=str, help="username") 27 | parser.add_argument("-p", type=str, help="password") 28 | parser.add_argument("-proxy", type=str, help="proxy") 29 | args = parser.parse_args() 30 | 31 | bot = Bot(whitelist_file="whitelist.txt", blacklist_file="blacklist.txt") 32 | 33 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 34 | 35 | timeline_medias = bot.get_timeline_medias() 36 | for media in tqdm(timeline_medias, desc="timeline"): 37 | bot.like_user(bot.get_media_owner(media)) 38 | -------------------------------------------------------------------------------- /examples/black-whitelist/blacklist.txt: -------------------------------------------------------------------------------- 1 | 12323 2 | 12323123 3 | -------------------------------------------------------------------------------- /examples/black-whitelist/whitelist.txt: -------------------------------------------------------------------------------- 1 | 352300017 2 | -------------------------------------------------------------------------------- /examples/black-whitelist/whitelist_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Whitelist generator: generates a list of users which 5 | will not be unfollowed. 6 | """ 7 | 8 | import os 9 | import random 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | bot = Bot() 16 | bot.login() 17 | 18 | print( 19 | "This script will generate whitelist.txt file with users" 20 | "who will not be unfollowed by bot. " 21 | "Press Y to add user to whitelist. Ctrl + C to exit." 22 | ) 23 | your_following = bot.following 24 | already_whitelisted = bot.read_list_from_file("whitelist.txt") 25 | rest_users = list(set(your_following) - set(already_whitelisted)) 26 | random.shuffle(rest_users) 27 | with open("whitelist.txt", "a") as f: 28 | for user_id in rest_users: 29 | user_info = bot.get_user_info(user_id) 30 | print(user_info["username"]) 31 | print(user_info["full_name"]) 32 | 33 | input_line = sys.stdin.readline().lower() 34 | if "y" in input_line.lower(): 35 | f.write(str(user_id) + "\n") 36 | print("ADDED.\r") 37 | -------------------------------------------------------------------------------- /examples/block_bots.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Block bots. That makes them unfollow you -> You have clear account. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | args = parser.parse_args() 20 | 21 | 22 | stop_words = ["shop", "store", "free"] 23 | bot = Bot(stop_words=stop_words) 24 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 25 | 26 | bot.logger.info( 27 | "This script will block bots. " 28 | "So they will no longer be your follower. " 29 | "Bots are those users who:\n" 30 | " * follow more than (sample value - change in file) 2000 users\n" 31 | " * have stopwords in user's info: " 32 | " %s " % str(stop_words) 33 | ) 34 | bot.block_bots() 35 | -------------------------------------------------------------------------------- /examples/collect_stats.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Collects the information about your account 5 | every hour in username.tsv file. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | import time 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("user", type=str, nargs="*", help="user") 21 | parser.add_argument("-path", type=str, default="config", help="path") 22 | args = parser.parse_args() 23 | 24 | bot = Bot() 25 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 26 | 27 | delay = 60 * 60 28 | 29 | while True: 30 | bot.save_user_stats(args.user, path=args.path) 31 | time.sleep(delay) 32 | -------------------------------------------------------------------------------- /examples/comment/comment_hashtags.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Dependencies: 5 | You must have a file with comments to post. 6 | The file should have one comment per line. 7 | 8 | Notes: 9 | You can change file and add there your comments. 10 | """ 11 | 12 | import os 13 | import sys 14 | 15 | sys.path.append(os.path.join(sys.path[0], "../../")) 16 | from instabot import Bot # noqa: E402 17 | 18 | if len(sys.argv) < 3: 19 | print("USAGE: Pass a path to the file with comments " "and a hashtag to comment") 20 | print("Example: %s comments_emoji.txt dog cat" % sys.argv[0]) 21 | exit() 22 | 23 | comments_file_name = sys.argv[1] 24 | hashtags = sys.argv[2:] 25 | if not os.path.exists(comments_file_name): 26 | print("Can't find '%s' file." % comments_file_name) 27 | exit() 28 | 29 | bot = Bot(comments_file=comments_file_name) 30 | bot.login() 31 | for hashtag in hashtags: 32 | bot.comment_hashtag(hashtag) 33 | bot.logout() 34 | -------------------------------------------------------------------------------- /examples/comment/comment_your_feed.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Dependencies: 5 | You must have a file with comments to post. 6 | The file should have one comment per line. 7 | 8 | Workflow: 9 | 1) Get your timeline medias 10 | 2) Comment them with random comments from file. 11 | 12 | Notes: 13 | You can change file and add there your comments. 14 | """ 15 | 16 | import os 17 | import sys 18 | 19 | sys.path.append(os.path.join(sys.path[0], "../../")) 20 | from instabot import Bot # noqa: E402 21 | 22 | if len(sys.argv) != 2: 23 | print("USAGE: Pass a path to the file with comments") 24 | print("Example: %s comments_emoji.txt" % sys.argv[0]) 25 | exit() 26 | 27 | comments_file_name = sys.argv[1] 28 | if not os.path.exists(comments_file_name): 29 | print("Can't find '%s' file." % comments_file_name) 30 | exit() 31 | 32 | bot = Bot(comments_file=comments_file_name) 33 | bot.login() 34 | bot.comment_medias(bot.get_timeline_medias()) 35 | bot.logout() 36 | -------------------------------------------------------------------------------- /examples/comment/comments.txt: -------------------------------------------------------------------------------- 1 | Really nice. 2 | I like this. 3 | Nice. 4 | Great capture. 5 | OMG. 6 | Great feed! 7 | Reminds me of something ... 8 | Yes. 9 | Exellent. 10 | Love it. 11 | Good eye. 12 | -------------------------------------------------------------------------------- /examples/comment/comments_emoji.txt: -------------------------------------------------------------------------------- 1 | 😀😀😀 2 | 😃😃😃 3 | 😄😃😀 4 | 😁😁😁 5 | 😆😆😆 6 | 😅😅😅 7 | 😂😂😂 8 | 😊😊😊 9 | 😇😇😇 10 | 🙂🙂🙂🙂🙂 11 | 😎 😎 😎 12 | 😜 😝 😛 13 | 😍😍😍 14 | 😳😳😳 15 | 😱😱😱 16 | 👀 17 | 💪💪💪 18 | 🤘 🤘 🤘 19 | 👍👍👍 20 | -------------------------------------------------------------------------------- /examples/comment/reply_to_media_comments.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | If media is commented, reply to comments 6 | if you didn't reply yet to that user. 7 | """ 8 | from __future__ import unicode_literals 9 | 10 | import argparse 11 | import os 12 | import sys 13 | 14 | from tqdm import tqdm 15 | 16 | sys.path.append(os.path.join(sys.path[0], "../../")) 17 | from instabot import Bot # noqa: E402 18 | 19 | parser = argparse.ArgumentParser(add_help=True) 20 | parser.add_argument("-u", type=str, help="username") 21 | parser.add_argument("-p", type=str, help="password") 22 | parser.add_argument("-proxy", type=str, help="proxy") 23 | parser.add_argument("-comments_file", type=str, help="comments_file", required=True) 24 | parser.add_argument("-link", type=str, help="media_link", required=True) 25 | args = parser.parse_args() 26 | 27 | if not args.comments_file: 28 | print( 29 | "You need to pass a path to the file with comments with option\n" 30 | "-comments_file COMMENTS_FILE_NAME" 31 | ) 32 | exit() 33 | if not args.link: 34 | print("You need to pass the media link with option\n" "-link MEDIA_LINK") 35 | exit() 36 | 37 | if not os.path.exists(args.comments_file): 38 | print("Can't find '{}' file.".format(args.comments_file)) 39 | exit() 40 | 41 | bot = Bot(comments_file=args.comments_file) 42 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 43 | 44 | media_id = bot.get_media_id_from_link(args.link) 45 | comments = bot.get_media_comments(media_id) 46 | if len(comments) == 0: 47 | bot.logger.info("Media `{link}` has got no comments yet.".format(args.link)) 48 | exit() 49 | 50 | commented_users = [] 51 | for comment in tqdm(comments): 52 | replied = False 53 | parent_comment_id = comment["pk"] 54 | user_id = comment["user"]["pk"] 55 | comment_type = comment["type"] 56 | commenter = comment["user"]["username"] 57 | text = comment["text"] 58 | bot.logger.info("Checking comment from `{commenter}`".format(commenter=commenter)) 59 | try: 60 | bot.logger.info("Comment text: `{text}`".format(text=text)) 61 | except Exception as e: 62 | bot.logger.error("{}".format(e)) 63 | # to save time, because you can't reply to yourself 64 | if str(user_id) == bot.user_id: 65 | bot.logger.error("You can't reply to yourself") 66 | continue 67 | if user_id in commented_users: 68 | bot.logger.info("You already replied to this user") 69 | continue 70 | for _comment in comments: 71 | # comments are of type 0 (standard) or type 2 (replies) 72 | if ( 73 | _comment["type"] == 2 74 | and str(_comment["user"]["pk"]) == bot.user_id 75 | and _comment["text"].split(" ")[0][1:] == commenter 76 | ): 77 | bot.logger.info("You already replied to this user.") 78 | replied = True 79 | break 80 | if replied: 81 | continue 82 | comment_txt = "@{username} {text}".format( 83 | username=commenter, text=bot.get_comment() 84 | ) 85 | bot.logger.info( 86 | "Going to reply to `{username}` with text `{text}`".format( 87 | username=commenter, text=comment_txt 88 | ) 89 | ) 90 | if bot.reply_to_comment(media_id, comment_txt, parent_comment_id): 91 | bot.logger.info("Replied to comment.") 92 | commented_users.append(user_id) 93 | -------------------------------------------------------------------------------- /examples/comment_medias_by_location.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | instabot example 4 | 5 | Workflow: 6 | Comment medias by location. 7 | """ 8 | 9 | import argparse 10 | import codecs 11 | import os 12 | import sys 13 | 14 | from tqdm import tqdm 15 | 16 | stdout = sys.stdout 17 | sys.stdout = codecs.getwriter("utf8")(sys.stdout) 18 | 19 | sys.path.append(os.path.join(sys.path[0], "../")) 20 | from instabot import Bot # noqa: E402 21 | 22 | 23 | try: 24 | input = raw_input 25 | except NameError: 26 | pass 27 | 28 | 29 | def comment_location_feed(new_bot, new_location, amount=0): 30 | counter = 0 31 | max_id = "" 32 | with tqdm(total=amount) as pbar: 33 | while counter < amount: 34 | if new_bot.api.get_location_feed( 35 | new_location["location"]["pk"], max_id=max_id 36 | ): 37 | location_feed = new_bot.api.last_json 38 | for media in new_bot.filter_medias( 39 | location_feed["items"][:amount], quiet=True 40 | ): 41 | if bot.comment(media, MESSAGE): 42 | counter += 1 43 | pbar.update(1) 44 | if not location_feed.get("next_max_id"): 45 | return False 46 | max_id = location_feed["next_max_id"] 47 | return True 48 | 49 | 50 | parser = argparse.ArgumentParser(add_help=True) 51 | parser.add_argument("-u", type=str, help="username") 52 | parser.add_argument("-p", type=str, help="password") 53 | parser.add_argument("-amount", type=str, help="amount") 54 | parser.add_argument("-message", type=str, help="message") 55 | parser.add_argument("-proxy", type=str, help="proxy") 56 | parser.add_argument("locations", type=str, nargs="*", help="locations") 57 | args = parser.parse_args() 58 | 59 | try: 60 | print(u"Comment medias by location") 61 | except TypeError: 62 | sys.stdout = stdout 63 | 64 | MESSAGE = args.message or "Hello World!" 65 | 66 | bot = Bot() 67 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 68 | 69 | if args.locations: 70 | for location in args.locations: 71 | print(u"Location: {}".format(location)) 72 | bot.api.search_location(location) 73 | finded_location = bot.api.last_json["items"][0] 74 | if finded_location: 75 | print(u"Found {}".format(finded_location["title"])) 76 | 77 | ncomments = args.amount or input(u"How much comments per location?\n") 78 | comment_location_feed(bot, finded_location, amount=int(ncomments)) 79 | else: 80 | location_name = input(u"Write location name:\n").strip() 81 | bot.api.search_location(location_name) 82 | if not bot.api.last_json["items"]: 83 | print(u"Location was not found") 84 | exit(1) 85 | ncomments = args.amount or input(u"How much comments per location?\n") 86 | ans = True 87 | while ans: 88 | for n, location in enumerate(bot.api.last_json["items"], start=1): 89 | print(u"{}. {}".format(n, location["title"])) 90 | print(u"\n0. Exit\n") 91 | ans = input(u"What place would you want to choose?\n").strip() 92 | if ans == "0": 93 | exit(0) 94 | try: 95 | ans = int(ans) - 1 96 | if ans in range(len(bot.api.last_json["items"])): 97 | comment_location_feed( 98 | bot, bot.api.last_json["items"][ans], amount=int(ncomments) 99 | ) 100 | except ValueError: 101 | print(u"\n Not valid choice. Try again") 102 | -------------------------------------------------------------------------------- /examples/config/blacklist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/blacklist.txt -------------------------------------------------------------------------------- /examples/config/comments.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/comments.txt -------------------------------------------------------------------------------- /examples/config/followed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/followed.txt -------------------------------------------------------------------------------- /examples/config/friends.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/friends.txt -------------------------------------------------------------------------------- /examples/config/hashtag_database.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/hashtag_database.txt -------------------------------------------------------------------------------- /examples/config/setting_multiscript.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/setting_multiscript.txt -------------------------------------------------------------------------------- /examples/config/skipped.txt: -------------------------------------------------------------------------------- 1 | 3875577 2 | -------------------------------------------------------------------------------- /examples/config/unfollowed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/unfollowed.txt -------------------------------------------------------------------------------- /examples/config/username_database.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/username_database.txt -------------------------------------------------------------------------------- /examples/config/whitelist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/config/whitelist.txt -------------------------------------------------------------------------------- /examples/delete_all_posts.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | delete all posts in profile. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | args = parser.parse_args() 20 | 21 | bot = Bot() 22 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 23 | medias = bot.get_total_user_medias(bot.user_id) 24 | bot.delete_medias(medias) 25 | -------------------------------------------------------------------------------- /examples/direct_send_photo.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Send photo to user 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot # noqa: E402 13 | 14 | parser = argparse.ArgumentParser(add_help=True) 15 | parser.add_argument("-u", type=str, help="username") 16 | parser.add_argument("-p", type=str, help="password") 17 | parser.add_argument("user", type=str, nargs="*", help="user") 18 | parser.add_argument("--filepath", required=True) 19 | args = parser.parse_args() 20 | 21 | bot = Bot() 22 | bot.login(username=args.u, password=args.p) 23 | bot.send_photo(args.user, args.filepath) 24 | -------------------------------------------------------------------------------- /examples/download_photos_by_hashtag.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Download media photos with hashtag. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("hashtags", type=str, nargs="+", help="hashtags") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for hashtag in args.hashtags: 26 | medias = bot.get_hashtag_medias(hashtag) 27 | bot.download_photos(medias) 28 | -------------------------------------------------------------------------------- /examples/download_photos_by_user.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Download the specified user's medias 6 | 7 | """ 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("username", type=str, help="@username") 17 | args = parser.parse_args() 18 | 19 | if args.username[0] != "@": # if first character isn't "@" 20 | args.username = "@" + args.username 21 | 22 | bot = Bot() 23 | bot.login() 24 | medias = bot.get_total_user_medias(args.username) 25 | bot.download_photos(medias) 26 | -------------------------------------------------------------------------------- /examples/download_stories.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | sys.path.append(os.path.join(sys.path[0], "../")) 6 | from instabot import Bot # noqa: E402 7 | 8 | parser = argparse.ArgumentParser(add_help=True) 9 | parser.add_argument("-u", type=str, help="username") 10 | parser.add_argument("-p", type=str, help="password") 11 | parser.add_argument("-proxy", type=str, help="proxy") 12 | parser.add_argument("-story_username", type=str, help="story_username") 13 | args = parser.parse_args() 14 | 15 | bot = Bot() 16 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 17 | 18 | bot.download_stories(args.story_username) 19 | -------------------------------------------------------------------------------- /examples/download_user_videos.py: -------------------------------------------------------------------------------- 1 | """ 2 | Download a user videos: 3 | This script could be very useful to download a users videos. 4 | 5 | Dependencies: 6 | pip install -U instabot 7 | Run: 8 | python download_user_videos.py -u username -p password -user user 9 | 10 | Notes: 11 | You can change file and add there your comments. 12 | 13 | Developed by: 14 | Steffan Jensen 15 | http://www.instabotai.com 16 | """ 17 | 18 | import os 19 | import sys 20 | import argparse 21 | from instabot import Bot 22 | 23 | parser = argparse.ArgumentParser(add_help=True) 24 | parser.add_argument("-u", type=str, help="username") 25 | parser.add_argument("-p", type=str, help="password") 26 | parser.add_argument("-proxy", type=str, help="proxy") 27 | parser.add_argument("-user", type=str, help="user") 28 | args = parser.parse_args() 29 | 30 | # in case if you just downloaded zip with sources 31 | sys.path.append(os.path.join(sys.path[0], "../../")) 32 | 33 | 34 | bot = Bot() 35 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 36 | 37 | user_id = bot.get_user_id_from_username(args.user) 38 | user_medias = bot.get_user_medias(user_id, filtration=None) 39 | for media_id in user_medias: 40 | bot.api.media_info(media_id) 41 | json = bot.api.last_json 42 | media_type = json["items"][0]["media_type"] 43 | if media_type == 2: 44 | print("Downloading Video") 45 | bot.download_video(media_id, folder="videos") 46 | else: 47 | print("Not a video") 48 | -------------------------------------------------------------------------------- /examples/download_your_photos.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) Downloads your medias 6 | 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | bot = Bot() 16 | bot.login() 17 | medias = bot.get_total_user_medias(bot.user_id) 18 | bot.download_photos(medias) 19 | -------------------------------------------------------------------------------- /examples/filter_blacklist_hashtag_medias.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot filters out the media with your set blacklist hashtags 3 | 4 | Workflow: 5 | Try to follow a media with your blacklist hashtag in the 6 | description and see how bot filters it out. 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | blacklist_hashtag_input = input("\n Enter a blacklist hashtag: ") 16 | 17 | bot = Bot( 18 | filter_users=True, 19 | filter_private_users=True, 20 | filter_previously_followed=True, 21 | filter_business_accounts=True, 22 | filter_verified_accounts=True, 23 | blacklist_hashtags=[blacklist_hashtag_input], 24 | ) 25 | bot.login() 26 | bot.like_hashtag(blacklist_hashtag_input, amount=2) 27 | -------------------------------------------------------------------------------- /examples/filter_private_profiles.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot filtering private users example 3 | 4 | Workflow: 5 | Try to follow a private user with the bot and see how it 6 | filters that user out. 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | 16 | bot = Bot(filter_users=True, filter_private_users=True) 17 | bot.login() 18 | private_user_input = input("\n Enter a private user: ") 19 | bot.follow(bot.get_user_id_from_username(private_user_input)) 20 | -------------------------------------------------------------------------------- /examples/follow_last_user_media_likers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Follow users who liked the last media of input users. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | from tqdm import tqdm 13 | 14 | sys.path.append(os.path.join(sys.path[0], "../")) 15 | from instabot import Bot # noqa: E402 16 | 17 | parser = argparse.ArgumentParser(add_help=True) 18 | parser.add_argument("-u", type=str, help="username") 19 | parser.add_argument("-p", type=str, help="password") 20 | parser.add_argument("-proxy", type=str, help="proxy") 21 | parser.add_argument("users", type=str, nargs="+", help="users") 22 | args = parser.parse_args() 23 | 24 | bot = Bot(max_follows_per_day=25, follow_delay=30) 25 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 26 | 27 | for username in args.users: 28 | medias = bot.get_user_medias(username, filtration=False) 29 | if medias: 30 | likers = bot.get_media_likers(medias[0]) 31 | for liker in tqdm(likers): 32 | bot.follow(liker) 33 | -------------------------------------------------------------------------------- /examples/follow_requests.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | sys.path.append(os.path.join(sys.path[0], "../")) 6 | from instabot import Bot # noqa: E402 7 | 8 | parser = argparse.ArgumentParser(add_help=True) 9 | parser.add_argument("-u", type=str, help="username") 10 | parser.add_argument("-p", type=str, help="password") 11 | parser.add_argument("-proxy", type=str, help="proxy") 12 | parser.add_argument("-story_username", type=str, help="story_username") 13 | args = parser.parse_args() 14 | 15 | bot = Bot() 16 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 17 | 18 | # (The following functions apply if you have a private account) 19 | 20 | # Approve users that requested to follow you 21 | bot.approve_pending_follow_requests() 22 | 23 | # Reject users that requested to follow you 24 | bot.reject_pending_follow_requests() 25 | -------------------------------------------------------------------------------- /examples/follow_user_followers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Follow user's followers by username. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot( 23 | filter_users=True, 24 | filter_private_users=False, 25 | filter_previously_followed=True, 26 | filter_business_accounts=True, 27 | filter_verified_accounts=True, 28 | ) 29 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 30 | 31 | for username in args.users: 32 | bot.follow_followers(username) 33 | -------------------------------------------------------------------------------- /examples/follow_user_following.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Follow user's following by username. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for username in args.users: 26 | bot.follow_following(username) 27 | -------------------------------------------------------------------------------- /examples/follow_users_by_hashtag.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Follow users who post medias with hashtag. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("hashtags", type=str, nargs="+", help="hashtags") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for hashtag in args.hashtags: 26 | users = bot.get_hashtag_users(hashtag) 27 | bot.follow_users(users) 28 | -------------------------------------------------------------------------------- /examples/follow_users_from_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Take users from input file and follow them. 6 | The file should contain one username per line! 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("filepath", type=str, help="filepath") 21 | args = parser.parse_args() 22 | 23 | bot = Bot(filter_users=False) 24 | users_to_follow = bot.read_list_from_file(args.filepath) 25 | if not users_to_follow: 26 | exit() 27 | else: 28 | print("Found %d users in file." % len(users_to_follow)) 29 | 30 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 31 | 32 | bot.follow_users(users_to_follow) 33 | -------------------------------------------------------------------------------- /examples/get_followers_or_followings_to_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | Workflow: 4 | Get total or filtered followers or followings to file. 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot # noqa: E402 13 | 14 | parser = argparse.ArgumentParser(add_help=True) 15 | # login arguments 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | # required arguments 20 | parser.add_argument( 21 | "-user", 22 | type=str, 23 | help="username whose followers or followings you want to get", 24 | required=True, 25 | ) 26 | parser.add_argument("-get", type=str, help="followers or followings", required=True) 27 | parser.add_argument("-file", type=str, help="speficy filename", required=True) 28 | # optional arguments 29 | 30 | help_msg = ( 31 | "set the total amount of followers/followings to check " 32 | "(if you set filters, returned amount could be less than this)" 33 | ) 34 | 35 | parser.add_argument("-amount", type=int, help=help_msg) 36 | parser.add_argument( 37 | "-overwrite", 38 | action="store_true", 39 | help="add this options to overwrite file if exists", 40 | ) 41 | parser.add_argument( 42 | "-usernames", 43 | action="store_true", 44 | help="add this options to download usernames instead of user_ids", 45 | ) 46 | parser.add_argument( 47 | "-filter_private", 48 | action="store_true", 49 | help="add this options to filter private acccounts", 50 | ) 51 | parser.add_argument( 52 | "-filter_business", 53 | action="store_true", 54 | help="add this options to filter business accounts", 55 | ) 56 | parser.add_argument( 57 | "-filter_verified", 58 | action="store_true", 59 | help="add this options to filter verified accounts", 60 | ) 61 | args = parser.parse_args() 62 | 63 | if args.get != "followers" and args.get != "followings": 64 | print( 65 | "Wrong option! You can get 'followers' or 'followings'.\n" 66 | "Type `python get_followers_or_followings_to_file.py " 67 | "--help` for help and options list" 68 | ) 69 | exit() 70 | 71 | bot = Bot() 72 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 73 | 74 | try: 75 | user_id = bot.get_user_id_from_username(args.user) 76 | except Exception as e: 77 | bot.logger.error("{}".format(e)) 78 | exit() 79 | 80 | bot.api.get_total_followers_or_followings( 81 | user_id=user_id, 82 | amount=args.amount, 83 | which=args.get, 84 | to_file=args.file, 85 | overwrite=args.overwrite, 86 | usernames=args.usernames, 87 | filter_private=args.filter_private, 88 | filter_business=args.filter_business, 89 | filter_verified=args.filter_verified, 90 | ) 91 | -------------------------------------------------------------------------------- /examples/get_hashtags_from_keywords.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import os 4 | import sys 5 | 6 | from tqdm import tqdm 7 | 8 | sys.path.append(os.path.join(sys.path[0], "../")) 9 | from instabot import Bot # noqa: E402 10 | 11 | if len(sys.argv) < 2: 12 | print( 13 | "Please provide keywords separated by space\n" 14 | "Usage:\n" 15 | "python get_hashtags_from_keywords.py keyword1 keyword2 etc" 16 | ) 17 | exit() 18 | 19 | bot = Bot() 20 | bot.login() 21 | 22 | 23 | def dispositions(l): 24 | from itertools import permutations 25 | 26 | print("Generating dispositions...") 27 | res = [] 28 | res += [a for a in l] 29 | length = len(l) 30 | num = 2 31 | while num <= length: 32 | p = list(permutations(l, num)) 33 | res += [" ".join(c) for c in p] 34 | num += 1 35 | print("Generated {} dispositions".format(len(res))) 36 | return res 37 | 38 | 39 | tags = {} 40 | for i in tqdm(dispositions(sys.argv[1:])): 41 | bot.api.search_tags(i) 42 | res = bot.api.last_json 43 | for result in res["results"]: 44 | tags[result["name"]] = result["media_count"] 45 | sorted_by_value = sorted(tags.items(), key=lambda kv: kv[1], reverse=True) 46 | 47 | bot.logger.info("Found {} hashtags".format(len(sorted_by_value))) 48 | for tag in sorted_by_value: 49 | if tag[1] < 1000: 50 | color = "34" 51 | elif tag[1] < 10000: 52 | color = "35" 53 | elif tag[1] < 100000: 54 | color = "31" 55 | elif tag[1] < 1000000: 56 | color = "33" 57 | elif tag[1] < 10000000: 58 | color = "32" 59 | elif tag[1] < 100000000: 60 | color = "36" 61 | else: 62 | color = "30;46" 63 | print( 64 | "\033[{color}mmedias: {count:,} -> TAG: {tag}\033[0m".format( 65 | color=color, tag=tag[0], count=tag[1] 66 | ) 67 | ) 68 | -------------------------------------------------------------------------------- /examples/infinity_feedliker.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like rescent medias from your timeline feed. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | import time 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | wait = 5 * 60 # in seconds 26 | 27 | while True: 28 | bot.like_timeline() 29 | time.sleep(wait) 30 | -------------------------------------------------------------------------------- /examples/infinity_hashtags_follower.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Follow users who post medias with hashtag. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | import time 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("hashtags", type=str, nargs="+", help="hashtags") 21 | args = parser.parse_args() 22 | 23 | bot = Bot() 24 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 25 | 26 | wait = 5 * 60 # in seconds 27 | 28 | while True: 29 | for hashtag in args.hashtags: 30 | users = bot.get_hashtag_users(hashtag) 31 | bot.follow_users(users) 32 | time.sleep(wait) 33 | -------------------------------------------------------------------------------- /examples/infinity_hashtags_liker.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like last images with hashtag. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | import time 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("hashtags", type=str, nargs="+", help="hashtags") 21 | args = parser.parse_args() 22 | 23 | bot = Bot() 24 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 25 | 26 | wait = 5 * 60 # in seconds 27 | 28 | while True: 29 | for hashtag in args.hashtags: 30 | bot.like_hashtag(hashtag) 31 | time.sleep(wait) 32 | -------------------------------------------------------------------------------- /examples/interact_DM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | instabot example 4 | 5 | Workflow: 6 | read and reply your DM 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | try: 17 | input = raw_input 18 | except NameError: 19 | pass 20 | 21 | parser = argparse.ArgumentParser(add_help=True) 22 | parser.add_argument("-u", type=str, help="username") 23 | parser.add_argument("-p", type=str, help="password") 24 | parser.add_argument("-proxy", type=str, help="proxy") 25 | args = parser.parse_args() 26 | 27 | 28 | def choice(message): 29 | get_choice = input(message) 30 | if get_choice == "y": 31 | return True 32 | elif get_choice == "n": 33 | return False 34 | else: 35 | print("Invalid Input") 36 | return choice(message) 37 | 38 | 39 | bot = Bot() 40 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 41 | 42 | if bot.api.get_inbox_v2(): 43 | data = bot.last_json["inbox"]["threads"] 44 | for item in data: 45 | bot.console_print(item["inviter"]["username"], "lightgreen") 46 | user_id = str(item["inviter"]["pk"]) 47 | last_item = item["last_permanent_item"] 48 | item_type = last_item["item_type"] 49 | if item_type == "text": 50 | print(last_item["text"]) 51 | if choice("Do you want to reply to this message?(y/n)"): 52 | text = input("write your message: ") 53 | if choice("send message?(y/n)"): 54 | bot.send_message(text, user_id, thread_id=item["thread_id"]) 55 | continue 56 | else: 57 | print(item_type) 58 | -------------------------------------------------------------------------------- /examples/like_and_follow_last_user_media_likers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like and follow users who liked the last media of input users. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | from tqdm import tqdm 13 | 14 | sys.path.append(os.path.join(sys.path[0], "../")) 15 | from instabot import Bot # noqa: E402 16 | 17 | parser = argparse.ArgumentParser(add_help=True) 18 | parser.add_argument("-u", type=str, help="username") 19 | parser.add_argument("-p", type=str, help="password") 20 | parser.add_argument("-proxy", type=str, help="proxy") 21 | parser.add_argument("users", type=str, nargs="+", help="users") 22 | args = parser.parse_args() 23 | 24 | bot = Bot() 25 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 26 | 27 | for username in args.users: 28 | medias = bot.get_user_medias(username, filtration=False) 29 | if len(medias): 30 | likers = bot.get_media_likers(medias[0]) 31 | for liker in tqdm(likers): 32 | bot.like_user(liker, amount=2) 33 | bot.follow(liker) 34 | -------------------------------------------------------------------------------- /examples/like_and_follow_media_likers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like and follow you last media likers. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import random 11 | import sys 12 | import time 13 | 14 | from tqdm import tqdm 15 | 16 | sys.path.append(os.path.join(sys.path[0], "../")) 17 | from instabot import Bot # noqa: E402 18 | 19 | 20 | def like_and_follow(bot, user_id, nlikes=3): 21 | bot.like_user(user_id, amount=nlikes) 22 | bot.follow(user_id) 23 | return True 24 | 25 | 26 | def like_and_follow_media_likers(bot, media, nlikes=3): 27 | for user in tqdm(bot.get_media_likers(media), desc="Media likers"): 28 | like_and_follow(bot, user, nlikes) 29 | time.sleep(10 + 20 * random.random()) 30 | return True 31 | 32 | 33 | parser = argparse.ArgumentParser(add_help=True) 34 | parser.add_argument("-u", type=str, help="username") 35 | parser.add_argument("-p", type=str, help="password") 36 | parser.add_argument("-proxy", type=str, help="proxy") 37 | parser.add_argument("media_id", type=str, help="media_id") 38 | args = parser.parse_args() 39 | 40 | bot = Bot() 41 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 42 | 43 | like_and_follow_media_likers(bot, args.media_id) 44 | -------------------------------------------------------------------------------- /examples/like_and_follow_your_last_media_likers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like and follow likers of last medias from your timeline feed. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import random 11 | import sys 12 | import time 13 | 14 | from tqdm import tqdm 15 | 16 | sys.path.append(os.path.join(sys.path[0], "../")) 17 | from instabot import Bot # noqa: E402 18 | 19 | 20 | def like_and_follow(bot, user_id, nlikes=3): 21 | bot.like_user(user_id, amount=nlikes) 22 | bot.follow(user_id) 23 | return True 24 | 25 | 26 | def like_and_follow_media_likers(bot, media, nlikes=3): 27 | for user in tqdm(bot.get_media_likers(media), desc="Media likers"): 28 | like_and_follow(bot, user, nlikes) 29 | time.sleep(10 + 20 * random.random()) 30 | return True 31 | 32 | 33 | def like_and_follow_your_feed_likers(bot, nlikes=3): 34 | bot.logger.info("Starting like_and_follow_your_feed_likers") 35 | last_media = bot.get_your_medias()[0] 36 | return like_and_follow_media_likers(bot, last_media, nlikes=nlikes) 37 | 38 | 39 | parser = argparse.ArgumentParser(add_help=True) 40 | parser.add_argument("-u", type=str, help="username") 41 | parser.add_argument("-p", type=str, help="password") 42 | parser.add_argument("-proxy", type=str, help="proxy") 43 | args = parser.parse_args() 44 | 45 | bot = Bot() 46 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 47 | 48 | like_and_follow_your_feed_likers(bot) 49 | -------------------------------------------------------------------------------- /examples/like_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) likes your timeline feed 6 | 2) likes user's feed 7 | 8 | Notes: 9 | 1) You should pass user_id, not username 10 | """ 11 | 12 | import os 13 | import sys 14 | 15 | sys.path.append(os.path.join(sys.path[0], "../")) 16 | from instabot import Bot # noqa: E402 17 | 18 | bot = Bot() 19 | bot.login() 20 | 21 | # like media by a single user_id 22 | bot.like_user("352300017") 23 | 24 | # likes all media from timeline 25 | bot.like_timeline() 26 | 27 | # likes all media from timeline 28 | bot.like_medias(bot.get_timeline_medias()) 29 | 30 | # likes media by hashtag(s) 31 | tags = ["l4l", "selfie"] 32 | 33 | for t in tags: 34 | bot.like_hashtag(t) 35 | -------------------------------------------------------------------------------- /examples/like_hashtags.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like last images with hashtag. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("hashtags", type=str, nargs="+", help="hashtags") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for hashtag in args.hashtags: 26 | bot.like_hashtag(hashtag) 27 | -------------------------------------------------------------------------------- /examples/like_hashtags_from_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like last images with hashtags from file. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("filename", type=str, help="filename") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | hashtags = bot.read_list_from_file(args.filename) 26 | bot.logger.info("Hashtags: " + str(hashtags)) 27 | if not hashtags: 28 | bot.logger.warning("No hastag file specified") 29 | exit() 30 | 31 | for hashtag in hashtags: 32 | bot.like_hashtag(hashtag) 33 | -------------------------------------------------------------------------------- /examples/like_location_feed.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | sys.path.append(os.path.join(sys.path[0], "../")) 6 | from instabot import Bot # noqa: E402 7 | 8 | parser = argparse.ArgumentParser(add_help=True) 9 | parser.add_argument("-u", type=str, help="username") 10 | parser.add_argument("-p", type=str, help="password") 11 | parser.add_argument("-proxy", type=str, help="proxy") 12 | parser.add_argument("-location", type=str, help="location") 13 | parser.add_argument("-amount", type=str, help="amount") 14 | args = parser.parse_args() 15 | 16 | bot = Bot() 17 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 18 | 19 | bot.like_location_feed(args.location, amount=args.amount) 20 | -------------------------------------------------------------------------------- /examples/like_medias_by_location.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | instabot example 4 | 5 | Workflow: 6 | Like medias by location. 7 | """ 8 | 9 | import argparse 10 | import codecs 11 | import os 12 | import sys 13 | 14 | from tqdm import tqdm 15 | 16 | stdout = sys.stdout 17 | sys.stdout = codecs.getwriter("utf8")(sys.stdout) 18 | 19 | sys.path.append(os.path.join(sys.path[0], "../")) 20 | from instabot import Bot # noqa: E402 21 | 22 | 23 | try: 24 | input = raw_input 25 | except NameError: 26 | pass 27 | 28 | 29 | def like_location_feed(new_bot, new_location, amount=0): 30 | counter = 0 31 | max_id = "" 32 | with tqdm(total=amount) as pbar: 33 | while counter < amount: 34 | if new_bot.api.get_location_feed( 35 | new_location["location"]["pk"], max_id=max_id 36 | ): 37 | location_feed = new_bot.api.last_json 38 | for media in new_bot.filter_medias( 39 | location_feed["items"][:amount], quiet=True 40 | ): 41 | if bot.like(media): 42 | counter += 1 43 | pbar.update(1) 44 | if location_feed.get("next_max_id"): 45 | max_id = location_feed["next_max_id"] 46 | else: 47 | return False 48 | return True 49 | 50 | 51 | parser = argparse.ArgumentParser(add_help=True) 52 | parser.add_argument("-u", type=str, help="username") 53 | parser.add_argument("-p", type=str, help="password") 54 | parser.add_argument("-amount", type=str, help="amount") 55 | parser.add_argument("-proxy", type=str, help="proxy") 56 | parser.add_argument("locations", type=str, nargs="*", help="locations") 57 | args = parser.parse_args() 58 | 59 | try: 60 | print(u"Like medias by location") 61 | except TypeError: 62 | sys.stdout = stdout 63 | 64 | bot = Bot() 65 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 66 | 67 | if args.locations: 68 | for location in args.locations: 69 | print(u"Location: {}".format(location)) 70 | bot.api.search_location(location) 71 | finded_location = bot.api.last_json["items"][0] 72 | if finded_location: 73 | print(u"Found {}".format(finded_location["title"])) 74 | 75 | if not args.amount: 76 | nlikes = input(u"How much likes per location?\n") 77 | else: 78 | nlikes = args.amount 79 | like_location_feed(bot, finded_location, amount=int(nlikes)) 80 | else: 81 | location_name = input(u"Write location name:\n").strip() 82 | bot.api.search_location(location_name) 83 | if not bot.api.last_json["items"]: 84 | print(u"Location was not found") 85 | exit(1) 86 | if not args.amount: 87 | nlikes = input(u"How much likes per location?\n") 88 | else: 89 | nlikes = args.amount 90 | ans = True 91 | while ans: 92 | for n, location in enumerate(bot.api.last_json["items"], start=1): 93 | print(u"{}. {}".format(n, location["title"])) 94 | print(u"\n0. Exit\n") 95 | ans = int(input(u"What place would you want to choose?\n").strip()) 96 | if ans == 0: 97 | exit(0) 98 | try: 99 | ans -= 1 100 | if 0 <= ans < len(bot.last_json["items"]): 101 | like_location_feed( 102 | bot, bot.api.last_json["items"][ans], amount=int(nlikes) 103 | ) 104 | except ValueError: 105 | print(u"\n Not valid choice. Try again") 106 | -------------------------------------------------------------------------------- /examples/like_timeline_feed.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like rescent medias from your timeline feed. 6 | """ 7 | 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot # noqa: E402 13 | 14 | 15 | bot = Bot() 16 | bot.login() 17 | bot.like_timeline() 18 | -------------------------------------------------------------------------------- /examples/like_user_followers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like user's, follower's media by user_id. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for username in args.users: 26 | bot.like_followers(username, nlikes=3) 27 | -------------------------------------------------------------------------------- /examples/like_user_following.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like user's, following's media by user_id. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for username in args.users: 26 | bot.like_following(username, nlikes=3) 27 | -------------------------------------------------------------------------------- /examples/like_users.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Like last medias by users. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-l", type=int, help="limit") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("users", type=str, nargs="+", help="users") 21 | args = parser.parse_args() 22 | 23 | bot = Bot() 24 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 25 | 26 | for username in args.users: 27 | bot.like_user(username, amount=args.l, filtration=False) 28 | -------------------------------------------------------------------------------- /examples/like_users_from_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Take users from input file and like them. 6 | The file should contain one username per line! 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | parser.add_argument("filepath", type=str, help="filepath") 21 | parser.add_argument("users", type=str, nargs="+", help="users") 22 | args = parser.parse_args() 23 | 24 | bot = Bot() 25 | users_to_like = bot.read_list_from_file(args.filepath) 26 | if not users_to_like: 27 | exit() 28 | else: 29 | print("Found %d users in file." % len(users_to_like)) 30 | 31 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 32 | 33 | bot.like_users(users_to_like, nlikes=1) 34 | -------------------------------------------------------------------------------- /examples/like_your_last_media_likers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | Workflow: 4 | Like likers of last medias from your timeline feed. 5 | """ 6 | 7 | import argparse 8 | import os 9 | import sys 10 | 11 | from tqdm import tqdm 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | 17 | def like_media_likers(bot, media, nlikes=3): 18 | for user in tqdm(bot.get_media_likers(media), desc="Media likers"): 19 | bot.like_user(user, nlikes) 20 | return True 21 | 22 | 23 | def like_your_feed_likers(bot, nlikes=3): 24 | last_media = bot.get_your_medias()[0] 25 | return like_media_likers(bot, last_media, nlikes=nlikes) 26 | 27 | 28 | parser = argparse.ArgumentParser(add_help=True) 29 | parser.add_argument("-u", type=str, help="username") 30 | parser.add_argument("-p", type=str, help="password") 31 | parser.add_argument("-proxy", type=str, help="proxy") 32 | args = parser.parse_args() 33 | 34 | bot = Bot() 35 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 36 | like_your_feed_likers(bot) 37 | -------------------------------------------------------------------------------- /examples/message_users.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) Ask Message type 6 | 2) Load messages CSV (if needed) 7 | 3) Send message to each users 8 | """ 9 | 10 | import csv 11 | import os 12 | import sys 13 | import time 14 | 15 | from tqdm import tqdm 16 | 17 | sys.path.append(os.path.join(sys.path[0], "../")) 18 | from instabot import Bot # noqa: E402 19 | 20 | instaUsers = ["R1B4Z01D", "KoanMedia"] 21 | directMessage = "Thanks for the example." 22 | 23 | messagesToSend = 100 24 | banDelay = 86400 / messagesToSend 25 | 26 | print("Which type of delivery method? (Type number)") 27 | print("%d: %s" % (0, "Messages From CSV File.")) 28 | print("%d: %s" % (1, "Group Message All Users From List.")) 29 | print("%d: %s" % (2, "Message Each User From List.")) 30 | print("%d: %s" % (3, "Message Each Your Follower.")) 31 | print("%d: %s" % (4, "Message LatestMediaLikers Of A Page")) 32 | 33 | deliveryMethod = int(input()) 34 | 35 | bot = Bot() 36 | bot.login() 37 | 38 | if deliveryMethod == 0: 39 | with open("messages.csv", "rU") as f: 40 | reader = csv.reader(f) 41 | for row in reader: 42 | print("Messaging " + row[0]) 43 | bot.send_message(row[1], row[0]) 44 | print("Waiting " + str(banDelay) + " seconds...") 45 | time.sleep(banDelay) 46 | elif deliveryMethod == 1: 47 | bot.send_message(directMessage, instaUsers) 48 | print("Sent A Group Message To All Users..") 49 | time.sleep(3) 50 | exit() 51 | elif deliveryMethod == 2: 52 | bot.send_messages(directMessage, instaUsers) 53 | print("Sent An Individual Messages To All Users..") 54 | time.sleep(3) 55 | exit() 56 | elif deliveryMethod == 3: 57 | for follower in tqdm(bot.followers): 58 | bot.send_message(directMessage, follower) 59 | print("Sent An Individual Messages To Your Followers..") 60 | time.sleep(3) 61 | exit() 62 | 63 | # new method 64 | elif deliveryMethod == 4: 65 | scrape = input("what page likers do you want to message? :") 66 | with open("scrape.txt", "w") as file: 67 | file.write(scrape) 68 | # usernames to get likers from 69 | pages_to_scrape = bot.read_list_from_file("scrape.txt") 70 | f = open("medialikers.txt", "w") # stored likers in user_ids 71 | for users in pages_to_scrape: 72 | medias = bot.get_user_medias(users, filtration=False) 73 | getlikers = bot.get_media_likers(medias[0]) 74 | for likers in getlikers: 75 | f.write(likers + "\n") 76 | print("succesfully written latest medialikers of" + str(pages_to_scrape)) 77 | f.close() 78 | 79 | # convert passed user-ids to usernames for usablility 80 | print("Reading from medialikers.txt") 81 | wusers = bot.read_list_from_file("medialikers.txt") 82 | with open("usernames.txt", "w") as f: 83 | for user_id in wusers: 84 | username = bot.get_username_from_user_id(user_id) 85 | f.write(username + "\n") 86 | print("succesfully converted " + str(wusers)) 87 | # parse usernames into a list 88 | with open("usernames.txt", encoding="utf-8") as file: 89 | instaUsers4 = [l.strip() for l in file] 90 | bot.send_messages(directMessage, instaUsers4) 91 | print("Sent An Individual Messages To All Users..") 92 | -------------------------------------------------------------------------------- /examples/messages.csv: -------------------------------------------------------------------------------- 1 | R1B4Z01D, "Thank You for the example." 2 | koanmedia, "I love your work." 3 | -------------------------------------------------------------------------------- /examples/photos/README.md: -------------------------------------------------------------------------------- 1 | # upload_photos 2 | Upload photos. Photos will be resized and cropped (if needed) from instabot. 3 | 4 | ## Requirements 5 | To resize/crop/convert photos, module python `Pillow` is required. To install use: 6 | ``` 7 | pip install Pillow 8 | ``` 9 | 10 | ## How to run 11 | - To show help, run 12 | ``` 13 | python upload_photos.py -h 14 | ``` 15 | - To upload one random photo, run the script 16 | ``` 17 | python upload_photos.py 18 | ``` 19 | - To upload a specific photo, run the script 20 | ``` 21 | python upload_photos.py -photo {photo_name} -caption "{your_caption}" 22 | ``` 23 | 24 | ## Settings 25 | - photos are stored in _media_ folder 26 | - photos can be `.jpg`, `.jpeg` or `.png` 27 | - edit _captions_for_medias.py_ to add captions for photos in _media_ folder 28 | - if you don't provide a caption in _captions_for_medias.py_, the script will ask you to write it in CLI 29 | 30 | ## WARNINGS 31 | - Photos will be resized and, if needed, cropped to 32 | - 90:47 (max width 1080 px) if horizontal 33 | - 4:5 (max height 1080 px) if vertical 34 | - 1:1 (1080x1080 px) if square 35 | - After convert and cropping/resizing, a temporary photo be saved to `{photo_name}.CONVERTED.jpg` in _media_ folder 36 | - After failed upload, temporary photo `{photo_name}.CONVERTED.jpg` will be left in _media_ folder for debugging purposes 37 | - After successful upload, temporary photo will be renamed to `{photo_name}.CONVERTED.jpg.REMOVE_ME` in _media_ folder 38 | - Uploaded pics names will be stored in _pics.txt_ 39 | ___ 40 | _by @maxdevblock_ 41 | -------------------------------------------------------------------------------- /examples/photos/captions_for_medias.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | CAPTIONS = { 4 | "01.jpg": "I'm the caption of #horizontal #pic 01 that will be resized " 5 | + "and cropped, with a mention to @maxdevblock", 6 | "02.jpg": "I'm the caption of #vertical #pic 02 that will be resized and" 7 | + " cropped, with a mention to @maxdevblock", 8 | } 9 | -------------------------------------------------------------------------------- /examples/photos/pics.txt: -------------------------------------------------------------------------------- 1 | ./pics/01.jpg 2 | ./pics/02.jpg 3 | -------------------------------------------------------------------------------- /examples/photos/upload_photos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # - * - coding: utf-8 - * - 3 | from __future__ import unicode_literals 4 | 5 | import argparse 6 | import os 7 | import sys 8 | 9 | import captions_for_medias 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../../")) 12 | from instabot import Bot # noqa: E402 13 | 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("-photo", type=str, help="photo name") 20 | parser.add_argument("-caption", type=str, help="caption for photo") 21 | parser.add_argument('-tag', action='append', help='taged user id') 22 | 23 | args = parser.parse_args() 24 | 25 | bot = Bot() 26 | bot.login() 27 | 28 | posted_pic_file = "pics.txt" 29 | 30 | posted_pic_list = [] 31 | caption = "" 32 | 33 | if not os.path.isfile(posted_pic_file): 34 | with open(posted_pic_file, "w"): 35 | pass 36 | else: 37 | with open(posted_pic_file, "r") as f: 38 | posted_pic_list = f.read().splitlines() 39 | 40 | # Get the filenames of the photos in the path -> 41 | if not args.photo: 42 | import glob 43 | 44 | pics = [] 45 | exts = ["jpg", "JPG", "jpeg", "JPEG", "png", "PNG"] 46 | for ext in exts: 47 | pics += [os.path.basename(x) for x in glob.glob("media/*.{}".format(ext))] 48 | from random import shuffle 49 | 50 | shuffle(pics) 51 | else: 52 | pics = [args.photo] 53 | pics = list(set(pics) - set(posted_pic_list)) 54 | if len(pics) == 0: 55 | if not args.photo: 56 | bot.logger.warn("NO MORE PHOTO TO UPLOAD") 57 | exit() 58 | else: 59 | bot.logger.error("The photo `{}` has already been posted".format(pics[0])) 60 | try: 61 | for pic in pics: 62 | bot.logger.info("Checking {}".format(pic)) 63 | if args.caption: 64 | caption = args.caption 65 | else: 66 | if captions_for_medias.CAPTIONS.get(pic): 67 | caption = captions_for_medias.CAPTIONS[pic] 68 | else: 69 | try: 70 | caption = raw_input( 71 | "No caption found for this media. " "Type the caption now: " 72 | ) 73 | except NameError: 74 | caption = input( 75 | "No caption found for this media. " "Type the caption now: " 76 | ) 77 | bot.logger.info( 78 | "Uploading pic `{pic}` with caption: `{caption}`".format( 79 | pic=pic, caption=caption 80 | ) 81 | ) 82 | 83 | # prepare tagged user_id 84 | users_to_tag = [{'user_id': u, 'x': 0.5, 'y': 0.5} for u in args.tag] 85 | 86 | if not bot.upload_photo( 87 | os.path.dirname(os.path.realpath(__file__)) + "/media/" + pic, 88 | caption=caption, 89 | user_tags=users_to_tag 90 | ): 91 | bot.logger.error("Something went wrong...") 92 | break 93 | posted_pic_list.append(pic) 94 | with open(posted_pic_file, "a") as f: 95 | f.write(pic + "\n") 96 | bot.logger.info("Succesfully uploaded: " + pic) 97 | break 98 | except Exception as e: 99 | bot.logger.error("\033[41mERROR...\033[0m") 100 | bot.logger.error(str(e)) 101 | -------------------------------------------------------------------------------- /examples/repost_best_photos_from_users.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | Workflow: 4 | Repost best photos from users to your account 5 | By default bot checks username_database.txt 6 | The file should contain one username per line! 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | import random 13 | 14 | from tqdm import tqdm 15 | 16 | sys.path.append(os.path.join(sys.path[0], "../")) 17 | from instabot import Bot, utils # noqa: E402 18 | 19 | USERNAME_DATABASE = "username_database.txt" 20 | POSTED_MEDIAS = "posted_medias.txt" 21 | 22 | 23 | def repost_best_photos(bot, users, amount=1): 24 | medias = get_not_used_medias_from_users(bot, users) 25 | medias = sort_best_medias(bot, medias, amount) 26 | for media in tqdm(medias, desc="Reposting photos"): 27 | repost_photo(bot, media) 28 | 29 | 30 | def sort_best_medias(bot, media_ids, amount=1): 31 | best_medias = [ 32 | bot.get_media_info(media)[0] 33 | for media in tqdm(media_ids, desc="Getting media info") 34 | ] 35 | best_medias = sorted( 36 | best_medias, key=lambda x: (x["like_count"], x["comment_count"]), reverse=True 37 | ) 38 | return [best_media["id"] for best_media in best_medias[:amount]] 39 | 40 | 41 | def get_not_used_medias_from_users(bot, users=None, users_path=USERNAME_DATABASE): 42 | if not users: 43 | if os.stat(USERNAME_DATABASE).st_size == 0: 44 | bot.logger.warning("No username(s) in thedatabase") 45 | sys.exit() 46 | elif os.path.exists(USERNAME_DATABASE): 47 | users = utils.file(users_path).list 48 | else: 49 | bot.logger.warning("No username database") 50 | sys.exit() 51 | 52 | total_medias = [] 53 | user = random.choice(users) 54 | 55 | medias = bot.get_user_medias(user, filtration=False) 56 | medias = [media for media in medias if not exists_in_posted_medias(media)] 57 | total_medias.extend(medias) 58 | return total_medias 59 | 60 | 61 | def exists_in_posted_medias(new_media_id, path=POSTED_MEDIAS): 62 | medias = utils.file(path).list 63 | return str(new_media_id) in medias 64 | 65 | 66 | def update_posted_medias(new_media_id, path=POSTED_MEDIAS): 67 | medias = utils.file(path) 68 | medias.append(str(new_media_id)) 69 | return True 70 | 71 | 72 | def repost_photo(bot, new_media_id, path=POSTED_MEDIAS): 73 | if exists_in_posted_medias(new_media_id, path): 74 | bot.logger.warning("Media {} was uploaded earlier".format(new_media_id)) 75 | return False 76 | photo_path = bot.download_photo(new_media_id, save_description=True) 77 | if not photo_path or not isinstance(photo_path, str): 78 | # photo_path could be True, False, or a file path. 79 | return False 80 | try: 81 | with open(photo_path[:-3] + "txt", "r") as f: 82 | text = "".join(f.readlines()) 83 | except FileNotFoundError: 84 | try: 85 | with open(photo_path[:-6] + ".txt", "r") as f: 86 | text = "".join(f.readlines()) 87 | except FileNotFoundError: 88 | bot.logger.warning("Cannot find the photo that is downloaded") 89 | pass 90 | if bot.upload_photo(photo_path, text): 91 | update_posted_medias(new_media_id, path) 92 | bot.logger.info("Media_id {} is saved in {}".format(new_media_id, path)) 93 | return True 94 | 95 | 96 | parser = argparse.ArgumentParser(add_help=True) 97 | parser.add_argument("-u", type=str, help="username") 98 | parser.add_argument("-p", type=str, help="password") 99 | parser.add_argument("-proxy", type=str, help="proxy") 100 | parser.add_argument("-file", type=str, help="users filename") 101 | parser.add_argument("-amount", type=int, help="amount", default=1) 102 | parser.add_argument("users", type=str, nargs="*", help="users") 103 | args = parser.parse_args() 104 | 105 | bot = Bot() 106 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 107 | 108 | users = None 109 | if args.users: 110 | users = args.users 111 | elif args.file: 112 | users = utils.file(args.file).list 113 | 114 | repost_best_photos(bot, users, args.amount) 115 | -------------------------------------------------------------------------------- /examples/repost_photo.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) Repost photo to your account 6 | """ 7 | 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot # noqa: E402 13 | from instabot.bot.bot_support import read_list_from_file # noqa: E402 14 | 15 | 16 | def exists_in_posted_medias(new_media_id, path="config/posted_medias.txt"): 17 | medias = read_list_from_file(path) 18 | return new_media_id in medias 19 | 20 | 21 | def update_posted_medias(new_media_id, path="config/posted_medias.txt"): 22 | medias = read_list_from_file(path) 23 | medias.append(str(new_media_id)) 24 | with open(path, "w") as file: 25 | file.writelines("\n".join(medias)) 26 | return True 27 | 28 | 29 | def repost_photo(bot, new_media_id, path="config/posted_medias.txt"): 30 | if exists_in_posted_medias(new_media_id, path): 31 | bot.logger.warning("Media {} was uploaded earlier".format(new_media_id)) 32 | return False 33 | photo_path = bot.download_photo(new_media_id, save_description=True) 34 | if not photo_path: 35 | return False 36 | with open(photo_path[:-3] + "txt", "r") as f: 37 | text = "".join(f.readlines()) 38 | if bot.upload_photo(photo_path, text): 39 | update_posted_medias(new_media_id, path) 40 | bot.logger.info("Media_id {} is saved in {}".format(new_media_id, path)) 41 | 42 | 43 | media_id = "" 44 | 45 | if not media_id: 46 | print("Media id is empty!") 47 | exit(1) 48 | 49 | bot = Bot() 50 | bot.login() 51 | 52 | repost_photo(bot, media_id) 53 | -------------------------------------------------------------------------------- /examples/reset_following.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | This is a account reset tool. 4 | Use this before you start boting. 5 | You can then reset the users you follow to what you had before botting. 6 | 7 | """ 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot # noqa: E402 13 | 14 | 15 | # class of all the tasks 16 | class Task(object): 17 | # getting the user to pick what to do 18 | @staticmethod 19 | def start(bot): 20 | answer = input( 21 | """ 22 | Please select 23 | 1) Create Friends List 24 | Make a list of the users you follow before you follow bot. 25 | 2) Restore Friends List 26 | Unfollow all user accept for the users in your friends list. 27 | 3) Exit 28 | \n 29 | """ 30 | ) 31 | 32 | answer = str(answer) 33 | # make a script 34 | if answer == "1": 35 | Task.one(bot) 36 | 37 | # unfollow your nonfriends 38 | if answer == "2": 39 | Task.two(bot) 40 | 41 | # exit sript 42 | if answer == "3": 43 | exit() 44 | 45 | # invalid input 46 | else: 47 | print("Type 1,2 or 3.") 48 | Task.start(bot) 49 | 50 | # list of followers script 51 | 52 | @staticmethod 53 | def one(bot): 54 | print("Creating List") 55 | friends = bot.following 56 | with open( 57 | "friends_{}.txt".format(bot.username), "w" 58 | ) as file: # writing to the file 59 | for user_id in friends: 60 | file.write(str(user_id) + "\n") 61 | print("Task Done") 62 | Task.start(bot) # go back to the start menu 63 | 64 | # reset following script 65 | 66 | @staticmethod 67 | def two(bot): 68 | friends = bot.read_list_from_file( 69 | "friends_{}.txt".format(bot.username) 70 | ) # getting the list of friends 71 | your_following = bot.following 72 | unfollow = list( 73 | set(your_following) - set(friends) 74 | ) # removing your friends from the list to unfollow 75 | bot.unfollow_users(unfollow) # unfollowing people who're not friends 76 | Task.start(bot) # go back to the start menu 77 | 78 | 79 | bot = Bot() 80 | bot.login() 81 | 82 | # welcome message 83 | print( 84 | """ 85 | Welcome to this bot. 86 | It will now get a list of all of the users you are following. 87 | You will need this if you follow bot your account and you want 88 | to reset your following to just your friends. 89 | """ 90 | ) 91 | Task.start(bot) # running the start script 92 | -------------------------------------------------------------------------------- /examples/save_unfollowers_into_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Save user' unfollowers into a file. 6 | """ 7 | import argparse 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot, utils # noqa: E402 13 | 14 | parser = argparse.ArgumentParser(add_help=False) 15 | parser.add_argument("-u", type=str, help="username") 16 | parser.add_argument("-p", type=str, help="password") 17 | parser.add_argument("-proxy", type=str, help="proxy") 18 | args = parser.parse_args() 19 | 20 | bot = Bot() 21 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 22 | 23 | f = utils.file("config/non-followers.txt") 24 | 25 | non_followers = set(bot.following) - set(bot.followers) - bot.friends_file.set 26 | non_followers = list(non_followers) 27 | non_followers_names = [] 28 | 29 | for user in non_followers: 30 | name = bot.get_username_from_user_id(user) 31 | non_followers_names.append(name) 32 | print(name) 33 | bot.small_delay() 34 | 35 | 36 | f.save_list(non_followers_names) 37 | -------------------------------------------------------------------------------- /examples/save_users_followers_into_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Save users' followers into a file. 6 | """ 7 | import argparse 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot, utils # noqa: E402 13 | 14 | parser = argparse.ArgumentParser(add_help=False) 15 | parser.add_argument("-u", type=str, help="username") 16 | parser.add_argument("-p", type=str, help="password") 17 | parser.add_argument("-proxy", type=str, help="proxy") 18 | parser.add_argument("filename", type=str, help="filename") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | f = utils.file(args.filename) 26 | for username in args.users: 27 | followers = bot.get_user_followers(username) 28 | f.save_list(followers) 29 | -------------------------------------------------------------------------------- /examples/save_users_following_into_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Save users' following into a file. 6 | """ 7 | import argparse 8 | import os 9 | import sys 10 | 11 | sys.path.append(os.path.join(sys.path[0], "../")) 12 | from instabot import Bot, utils # noqa: E402 13 | 14 | parser = argparse.ArgumentParser(add_help=False) 15 | parser.add_argument("-u", type=str, help="username") 16 | parser.add_argument("-p", type=str, help="password") 17 | parser.add_argument("-proxy", type=str, help="proxy") 18 | parser.add_argument("filename", type=str, help="filename") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | f = utils.file(args.filename) 26 | for username in args.users: 27 | following = bot.get_user_following(username) 28 | f.save_list(following) 29 | -------------------------------------------------------------------------------- /examples/stories/watch_user_likers_stories.py: -------------------------------------------------------------------------------- 1 | """ 2 | Watch user likers stories! 3 | This script could be very useful to attract someone's 4 | audience to your account. 5 | 6 | If you will not specify the user_id, the script will use 7 | your likers as targets. 8 | 9 | Dependencies: 10 | pip install -U instabot 11 | 12 | Notes: 13 | You can change file and add there your comments. 14 | """ 15 | 16 | import os 17 | import random 18 | import sys 19 | import time 20 | 21 | # in case if you just downloaded zip with sources 22 | sys.path.append(os.path.join(sys.path[0], "../../")) 23 | from instabot import Bot # noqa: E402 24 | 25 | bot = Bot() 26 | bot.login() 27 | 28 | if len(sys.argv) >= 2: 29 | bot.logger.info( 30 | """ 31 | Going to get '%s' likers and watch their stories 32 | (and stories of their likers too). 33 | """ 34 | % (sys.argv[1]) 35 | ) 36 | user_to_get_likers_of = bot.convert_to_user_id(sys.argv[1]) 37 | else: 38 | bot.logger.info( 39 | """ 40 | Going to get your likers and watch their stories (and stories 41 | of their likers too). You can specify username of another user 42 | to start (by default we use you as a starting point). 43 | """ 44 | ) 45 | user_to_get_likers_of = bot.user_id 46 | 47 | current_user_id = user_to_get_likers_of 48 | while True: 49 | try: 50 | # GET USER FEED 51 | if not bot.api.get_user_feed(current_user_id): 52 | print("Can't get feed of user_id=%s" % current_user_id) 53 | 54 | # GET MEDIA LIKERS 55 | user_media = random.choice(bot.api.last_json["items"]) 56 | if not bot.api.get_media_likers(media_id=user_media["pk"]): 57 | bot.logger.info( 58 | "Can't get media likers of media_id='%s' by user_id='%s'" 59 | % (user_media["id"], current_user_id) 60 | ) 61 | 62 | likers = bot.api.last_json["users"] 63 | liker_ids = [ 64 | str(u["pk"]) 65 | for u in likers 66 | if not u["is_private"] and "latest_reel_media" in u 67 | ][:20] 68 | 69 | # WATCH USERS STORIES 70 | if bot.watch_users_reels(liker_ids): 71 | bot.logger.info("Total stories viewed: %d" % bot.total["stories_viewed"]) 72 | 73 | # CHOOSE RANDOM LIKER TO GRAB HIS LIKERS AND REPEAT 74 | current_user_id = random.choice(liker_ids) 75 | 76 | if random.random() < 0.05: 77 | current_user_id = user_to_get_likers_of 78 | bot.logger.info( 79 | "Sleeping and returning back to original user_id=%s" % current_user_id 80 | ) 81 | time.sleep(90 * random.random() + 60) 82 | 83 | except Exception as e: 84 | # If something went wrong - sleep long and start again 85 | bot.logger.info(e) 86 | current_user_id = user_to_get_likers_of 87 | time.sleep(240 * random.random() + 60) 88 | -------------------------------------------------------------------------------- /examples/ultimate/follow_followers.txt: -------------------------------------------------------------------------------- 1 | ohld 2 | -------------------------------------------------------------------------------- /examples/ultimate/follow_following.txt: -------------------------------------------------------------------------------- 1 | ohld 2 | -------------------------------------------------------------------------------- /examples/ultimate/like_hashtags.txt: -------------------------------------------------------------------------------- 1 | mipt 2 | мфти 3 | -------------------------------------------------------------------------------- /examples/ultimate/like_users.txt: -------------------------------------------------------------------------------- 1 | ohld 2 | lenakolenka 3 | -------------------------------------------------------------------------------- /examples/ultimate/ultimate.py: -------------------------------------------------------------------------------- 1 | """ 2 | ULTIMATE SCRIPT 3 | 4 | It uses data written in files: 5 | * follow_followers.txt 6 | * follow_following.txt 7 | * like_hashtags.txt 8 | * like_users.txt 9 | and do the job. This bot can be run 24/7. 10 | """ 11 | 12 | import os 13 | import sys 14 | 15 | sys.path.append(os.path.join(sys.path[0], "../../")) 16 | from instabot import Bot # noqa: E402 17 | 18 | bot = Bot() 19 | bot.login() 20 | 21 | print("Current script's schedule:") 22 | follow_followers_list = bot.read_list_from_file("follow_followers.txt") 23 | print("Going to follow followers of:", follow_followers_list) 24 | follow_following_list = bot.read_list_from_file("follow_following.txt") 25 | print("Going to follow following of:", follow_following_list) 26 | like_hashtags_list = bot.read_list_from_file("like_hashtags.txt") 27 | print("Going to like hashtags:", like_hashtags_list) 28 | like_users_list = bot.read_list_from_file("like_users.txt") 29 | print("Going to like users:", like_users_list) 30 | 31 | tasks_list = [] 32 | for item in follow_followers_list: 33 | tasks_list.append((bot.follow_followers, {"user_id": item, "nfollows": None})) 34 | for item in follow_following_list: 35 | tasks_list.append((bot.follow_following, {"user_id": item})) 36 | for item in like_hashtags_list: 37 | tasks_list.append((bot.like_hashtag, {"hashtag": item, "amount": None})) 38 | for item in like_users_list: 39 | tasks_list.append((bot.like_user, {"user_id": item, "amount": None})) 40 | 41 | # shuffle(tasks_list) 42 | for func, arg in tasks_list: 43 | func(**arg) 44 | -------------------------------------------------------------------------------- /examples/ultimate_schedule/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | BLACKLIST_FILE = "blacklist.txt" # List of the users you don't want to follow 4 | 5 | WHITELIST_FILE = "whitelist.txt" # List of users you don't want to unfollow 6 | 7 | COMMENTS_FILE = "comments.txt" # Contains random comments posted by the bot 8 | 9 | FRIENDS_FILE = "friends.txt" # Users IDs of friends 10 | 11 | PHOTO_CAPTIONS_FILE = "config/photo_captions.txt" # Captions to put under the photos 12 | 13 | HASHTAGS_FILE = "config/hashtag_database.txt" 14 | # The file containing hashtags you want to track: the bot will like and comment 15 | # photos and follow users using the hashtags in this file 16 | 17 | USERS_FILE = "config/username_database.txt" 18 | # Same as HASHTAGS_FILE, but with users. The bot will follow those users' 19 | # followers and like their posts 20 | 21 | POSTED_PICS_FILE = "config/pics.txt" 22 | # File containing all the photos already posted from the PICS_PATH directory 23 | 24 | PICS_PATH = "config/pics/" 25 | # The path of the directory containing the photos the bot will upload 26 | # NOTE: Being a directory, it must end with '/' 27 | 28 | PICS_HASHTAGS = ( 29 | "#hashtag1 #hashtag2 #hashtag3 #hashtag4 " "#hashtag5 #hashtag6 #hashtag7" 30 | ) 31 | # The bot will comment each photo it posts with the hashtags in PICS_HASHTAGS 32 | # Each string but the last must end with a space 33 | # NOTE: Instagram allows only for a maximum of 30 hashtags per post. 34 | 35 | FOLLOW_MESSAGE = "Follow @my_account for the best photos!" 36 | # The string to insert under the random caption. The bot will construct each 37 | # photo caption like the following -> 38 | # [random caption taken from PHOTO_CAPTIONS] 39 | # FOLLOW_MESSAGE 40 | 41 | NUMBER_OF_FOLLOWERS_TO_FOLLOW = 15 42 | # Specifies the number of people to follow each time the function 43 | # bot.follow_followers gets executed. By default, this function gets 44 | # executed by the bot every 2 days at 11:00. 45 | 46 | NUMBER_OF_NON_FOLLOWERS_TO_UNFOLLOW = 30 47 | # Specifies the number of people to unfollow each time the function 48 | # bot.unfollow_non_followers gets executed. By default, this function 49 | # gets executed every day at 08:00. 50 | 51 | # NOTE: Because the bot follows a bunch of people through job7 (follow 52 | # people by a random hashtag in HASHTAGS_FILE), I recommend setting 53 | # NUMBER_OF_FOLLOWERS_TO_FOLLOW between 15 and 30, and 54 | # NUMBER_OF_NON_FOLLOWERS_TO_UNFOLLOW between 50 and 60. Following and 55 | # unfollowing many people in the same day can cause a temporary 56 | # "follow ban" by Instagram: basically you can't follow or unfollow 57 | # anybody for 24 hours. 58 | -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/blacklist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/blacklist.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/comments.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/comments.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/followed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/followed.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/friends.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/friends.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/hashtag_database.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/hashtag_database.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/photo_captions.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/photo_captions.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/pics.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/pics.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/skipped.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/skipped.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/unfollowed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/unfollowed.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/username_database.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/username_database.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/config/whitelist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/examples/ultimate_schedule/config/whitelist.txt -------------------------------------------------------------------------------- /examples/ultimate_schedule/readme.md: -------------------------------------------------------------------------------- 1 | # Ultimate script with schedule 2 | 3 | This script uses [schedule](https://github.com/dbader/schedule) to do tasks. Just look at the code and change it - it should be clear enough. 4 | 5 | To install _schedule_ module: 6 | ``` 7 | pip install schedule 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/ultimate_schedule/ultimate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import threading 6 | import time 7 | import argparse 8 | import config 9 | 10 | from glob import glob 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../../")) 13 | import schedule # noqa: E402 14 | from instabot import Bot, utils # noqa: E402 15 | 16 | 17 | bot = Bot( 18 | comments_file=config.COMMENTS_FILE, 19 | blacklist_file=config.BLACKLIST_FILE, 20 | whitelist_file=config.WHITELIST_FILE, 21 | friends_file=config.FRIENDS_FILE, 22 | ) 23 | parser = argparse.ArgumentParser(add_help=True) 24 | parser.add_argument("-u", type=str, help="username") 25 | parser.add_argument("-p", type=str, help="password") 26 | parser.add_argument("-proxy", type=str, help="proxy") 27 | args = parser.parse_args() 28 | 29 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 30 | bot.logger.info("ULTIMATE script. Safe to run 24/7!") 31 | 32 | random_user_file = utils.file(config.USERS_FILE) 33 | random_hashtag_file = utils.file(config.HASHTAGS_FILE) 34 | photo_captions_file = utils.file(config.PHOTO_CAPTIONS_FILE) 35 | posted_pic_list = utils.file(config.POSTED_PICS_FILE).list 36 | 37 | pics = sorted([os.path.basename(x) for x in glob(config.PICS_PATH + "/*.jpg")]) 38 | 39 | 40 | def stats(): 41 | bot.save_user_stats(bot.user_id) 42 | 43 | 44 | def like_hashtags(): 45 | bot.like_hashtag(random_hashtag_file.random(), amount=700 // 24) 46 | 47 | 48 | def like_timeline(): 49 | bot.like_timeline(amount=300 // 24) 50 | 51 | 52 | def like_followers_from_random_user_file(): 53 | bot.like_followers(random_user_file.random(), nlikes=3) 54 | 55 | 56 | def follow_followers(): 57 | bot.follow_followers( 58 | random_user_file.random(), nfollows=config.NUMBER_OF_FOLLOWERS_TO_FOLLOW 59 | ) 60 | 61 | 62 | def comment_medias(): 63 | bot.comment_medias(bot.get_timeline_medias()) 64 | 65 | 66 | def unfollow_non_followers(): 67 | bot.unfollow_non_followers( 68 | n_to_unfollows=config.NUMBER_OF_NON_FOLLOWERS_TO_UNFOLLOW 69 | ) 70 | 71 | 72 | def follow_users_from_hashtag_file(): 73 | bot.follow_users(bot.get_hashtag_users(random_hashtag_file.random())) 74 | 75 | 76 | def comment_hashtag(): 77 | hashtag = random_hashtag_file.random() 78 | bot.logger.info("Commenting on hashtag: " + hashtag) 79 | bot.comment_hashtag(hashtag) 80 | 81 | 82 | def upload_pictures(): # Automatically post a pic in 'pics' folder 83 | try: 84 | for pic in pics: 85 | if pic in posted_pic_list: 86 | continue 87 | 88 | caption = photo_captions_file.random() 89 | full_caption = caption + "\n" + config.FOLLOW_MESSAGE 90 | bot.logger.info("Uploading pic with caption: " + caption) 91 | bot.upload_photo(config.PICS_PATH + pic, caption=full_caption) 92 | if bot.api.last_response.status_code != 200: 93 | bot.logger.error("Something went wrong, read the following ->\n") 94 | bot.logger.error(bot.api.last_response) 95 | break 96 | 97 | if pic not in posted_pic_list: 98 | # After posting a pic, comment it with all the 99 | # hashtags specified in config.PICS_HASHTAGS 100 | posted_pic_list.append(pic) 101 | with open("pics.txt", "a") as f: 102 | f.write(pic + "\n") 103 | bot.logger.info("Succesfully uploaded: " + pic) 104 | bot.logger.info("Commenting uploaded photo with hashtags...") 105 | medias = bot.get_your_medias() 106 | last_photo = medias[0] # Get the last photo posted 107 | bot.comment(last_photo, config.PICS_HASHTAGS) 108 | break 109 | except Exception as e: 110 | bot.logger.error("Couldn't upload pic") 111 | bot.logger.error(str(e)) 112 | 113 | 114 | def put_non_followers_on_blacklist(): # put non followers on blacklist 115 | try: 116 | bot.logger.info("Creating non-followers list") 117 | followings = set(bot.following) 118 | followers = set(bot.followers) 119 | friends = bot.friends_file.set # same whitelist (just user ids) 120 | non_followers = followings - followers - friends 121 | for user_id in non_followers: 122 | bot.blacklist_file.append(user_id, allow_duplicates=False) 123 | bot.logger.info("Done.") 124 | except Exception as e: 125 | bot.logger.error("Couldn't update blacklist") 126 | bot.logger.error(str(e)) 127 | 128 | 129 | def run_threaded(job_fn): 130 | job_thread = threading.Thread(target=job_fn) 131 | job_thread.start() 132 | 133 | 134 | schedule.every(1).hour.do(run_threaded, stats) 135 | schedule.every(8).hours.do(run_threaded, like_hashtags) 136 | schedule.every(2).hours.do(run_threaded, like_timeline) 137 | schedule.every(1).days.at("16:00").do( 138 | run_threaded, like_followers_from_random_user_file 139 | ) 140 | schedule.every(2).days.at("11:00").do(run_threaded, follow_followers) 141 | schedule.every(16).hours.do(run_threaded, comment_medias) 142 | schedule.every(1).days.at("08:00").do(run_threaded, unfollow_non_followers) 143 | schedule.every(12).hours.do(run_threaded, follow_users_from_hashtag_file) 144 | schedule.every(6).hours.do(run_threaded, comment_hashtag) 145 | schedule.every(1).days.at("21:28").do(run_threaded, upload_pictures) 146 | schedule.every(4).days.at("07:50").do(run_threaded, put_non_followers_on_blacklist) 147 | 148 | while True: 149 | schedule.run_pending() 150 | time.sleep(1) 151 | -------------------------------------------------------------------------------- /examples/unarchive_your_medias.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) Unarchives your last medias 6 | 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | bot = Bot() 16 | bot.login() 17 | medias = bot.get_archived_medias() 18 | bot.unarchive_medias(medias) 19 | -------------------------------------------------------------------------------- /examples/unfollow_everyone.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) unfollows every from your account. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | bot.unfollow_everyone() 25 | -------------------------------------------------------------------------------- /examples/unfollow_non_followers.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | 1) unfollows users that don't follow you. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | 16 | parser = argparse.ArgumentParser(add_help=True) 17 | parser.add_argument("-u", type=str, help="username") 18 | parser.add_argument("-p", type=str, help="password") 19 | parser.add_argument("-proxy", type=str, help="proxy") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | bot.unfollow_non_followers() 25 | -------------------------------------------------------------------------------- /examples/unlike_users.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | 4 | Workflow: 5 | Unlike last media's of users. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | sys.path.append(os.path.join(sys.path[0], "../")) 13 | from instabot import Bot # noqa: E402 14 | 15 | parser = argparse.ArgumentParser(add_help=True) 16 | parser.add_argument("-u", type=str, help="username") 17 | parser.add_argument("-p", type=str, help="password") 18 | parser.add_argument("-proxy", type=str, help="proxy") 19 | parser.add_argument("users", type=str, nargs="+", help="users") 20 | args = parser.parse_args() 21 | 22 | bot = Bot() 23 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 24 | 25 | for username in args.users: 26 | bot.unlike_user(username) 27 | -------------------------------------------------------------------------------- /examples/upload_story_photo.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | sys.path.append(os.path.join(sys.path[0], "../")) 6 | from instabot import Bot # noqa: E402 7 | 8 | parser = argparse.ArgumentParser(add_help=True) 9 | parser.add_argument("-u", type=str, help="username") 10 | parser.add_argument("-p", type=str, help="password") 11 | parser.add_argument("-proxy", type=str, help="proxy") 12 | parser.add_argument("-photo", type=str, help="photo name like 'picture.jpg' ") 13 | args = parser.parse_args() 14 | 15 | bot = Bot() 16 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 17 | 18 | # Publish a new story with the given photo 19 | bot.upload_story_photo(args.photo) 20 | -------------------------------------------------------------------------------- /examples/video/README.md: -------------------------------------------------------------------------------- 1 | # upload_video 2 | Upload video. Videos will be resized and cropped (if needed) from instabot and thumbnail will be created. 3 | 4 | ## Requirements 5 | To resize/crop/convert videos, module python `moviepy` is required. To install use: 6 | ``` 7 | pip install moviepy 8 | ``` 9 | You may need also to 10 | ``` 11 | pip install --upgrade setuptools 12 | ``` 13 | and 14 | ``` 15 | pip install numpy --upgrade --ignore-installed 16 | ``` 17 | Other system requirements will be automatically installed by python `moviepy` module itself at first usage. 18 | 19 | ## How to run 20 | - To show help, run 21 | ``` 22 | python upload_video.py -h 23 | ``` 24 | - To upload one random video, run the script 25 | ``` 26 | python upload_video.py 27 | ``` 28 | - To upload a speficic video, run the script 29 | ``` 30 | python upload_video.py -video {video_name} -caption "{your_caption}" 31 | ``` 32 | 33 | ## Settings 34 | - videos are stored in _media_ folder 35 | - videos can be `.mp4` and `.mov` (not tested with other formats) 36 | - edit _captions_for_medias.py_ to add captions for video in _media_ folder 37 | - if you don't provide a caption in _captions_for_medias.py_, the script will ask you to write it in CLI 38 | 39 | ## WARNINGS 40 | - Videos, if needed, will be resized and cropped to 41 | - 90:47 (max width 1080 px) if horizontal 42 | - 4:5 (max height 1080 px) if vertical 43 | - 1:1 (1080x1080 px) if square 44 | - After convert and cropping/resizing, a temporary video will be saved to `{video_name}.CONVERTED.mp4` in _media_ folder 45 | - After thumbnail creation, a temporary picture will be saved to `{video_name}.jpg` in _media_ folder 46 | - After failed upload, temporary video and thumbnail will be left in _media_ folder for debugging purposes 47 | - After succefull upload, temporary video and tumbnail will be renamed to `{name}.REMOVE_ME` in _media_ folder 48 | - Uploaded video names will be stored in _videos.txt_ 49 | ___ 50 | _by @maxdevblock_ 51 | -------------------------------------------------------------------------------- /examples/video/captions_for_medias.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | CAPTIONS = { 4 | "60s.mp4": "I'm the caption of #vertical #mp4 #video that will be " 5 | + "cropped only and cut at 30 seconds from start, with a mention " 6 | + "to @maxdevblock", 7 | "02.mov": "I'm the caption of #horizontal #mov #video that will be" 8 | + " resized and cropped but not cut, with a mention to @maxdevblock", 9 | } 10 | -------------------------------------------------------------------------------- /examples/video/upload_video.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import argparse 4 | import os 5 | import sys 6 | 7 | import captions_for_medias 8 | 9 | sys.path.append(os.path.join(sys.path[0], "../../")) 10 | from instabot import Bot # noqa: E402 11 | 12 | 13 | parser = argparse.ArgumentParser(add_help=True) 14 | parser.add_argument("-u", type=str, help="username") 15 | parser.add_argument("-p", type=str, help="password") 16 | parser.add_argument("-proxy", type=str, help="proxy") 17 | parser.add_argument("-video", type=str, help="video name") 18 | parser.add_argument("-caption", type=str, help="caption for video") 19 | args = parser.parse_args() 20 | 21 | bot = Bot() 22 | bot.login() 23 | 24 | posted_video_file = "videos.txt" 25 | 26 | posted_video_list = [] 27 | caption = "" 28 | 29 | if not os.path.isfile(posted_video_file): 30 | with open(posted_video_file, "w"): 31 | pass 32 | else: 33 | with open(posted_video_file, "r") as f: 34 | posted_video_list = f.read().splitlines() 35 | 36 | # Get the filenames of the videos in the path -> 37 | if not args.video: 38 | import glob 39 | 40 | videos = [] 41 | exts = ["mp4", "MP4", "mov", "MOV"] 42 | for ext in exts: 43 | videos += [os.path.basename(x) for x in glob.glob("media/*.{}".format(ext))] 44 | from random import shuffle 45 | 46 | shuffle(videos) 47 | else: 48 | videos = [args.video] 49 | videos = list(set(videos) - set(posted_video_list)) 50 | if len(videos) == 0: 51 | if not args.video: 52 | bot.logger.warning("NO MORE VIDEO TO UPLOAD") 53 | exit() 54 | else: 55 | bot.logger.error("The video `{}` has already been posted".format(videos[0])) 56 | try: 57 | for video in videos: 58 | bot.logger.info("Checking {}".format(video)) 59 | if args.caption: 60 | caption = args.caption 61 | else: 62 | if captions_for_medias.CAPTIONS.get(video): 63 | caption = captions_for_medias.CAPTIONS[video] 64 | else: 65 | try: 66 | caption = raw_input( 67 | "No caption found for this media. " "Type the caption now: " 68 | ) 69 | except NameError: 70 | caption = input( 71 | "No caption found for this media. " "Type the caption now: " 72 | ) 73 | bot.logger.info( 74 | "Uploading video `{video}` with caption: `{caption}`".format( 75 | video=video, caption=caption 76 | ) 77 | ) 78 | if not bot.upload_video( 79 | os.path.dirname(os.path.realpath(__file__)) + "/media/" + video, 80 | caption=caption, 81 | ): 82 | bot.logger.error("Something went wrong...") 83 | break 84 | posted_video_list.append(video) 85 | with open(posted_video_file, "a") as f: 86 | f.write(video + "\n") 87 | bot.logger.info("Succesfully uploaded: " + video) 88 | break 89 | except Exception as e: 90 | bot.logger.error("\033[41mERROR...\033[0m") 91 | bot.logger.error(str(e)) 92 | -------------------------------------------------------------------------------- /examples/welcome_message.py: -------------------------------------------------------------------------------- 1 | """ 2 | instabot example 3 | Workflow: 4 | Welcome message for new followers. 5 | """ 6 | import argparse 7 | import datetime 8 | import os 9 | import sys 10 | import time 11 | 12 | 13 | sys.path.append(os.path.join(sys.path[0], "../")) 14 | from instabot import Bot # noqa: E402 15 | 16 | RETRY_DELAY = 60 17 | DELAY = 30 * 60 18 | 19 | 20 | def get_recent_followers(bot, from_time): 21 | followers = [] 22 | ok = bot.api.get_recent_activity() 23 | if not ok: 24 | raise ValueError("failed to get activity") 25 | activity = bot.api.last_json 26 | for feed in [activity["new_stories"], activity["old_stories"]]: 27 | for event in feed: 28 | if event.get("args", {}).get("text", "").endswith("started following you."): 29 | follow_time = datetime.datetime.utcfromtimestamp( 30 | event["args"]["timestamp"] 31 | ) 32 | if follow_time < from_time: 33 | continue 34 | followers.append( 35 | { 36 | "user_id": event["args"]["profile_id"], 37 | "username": event["args"]["profile_name"], 38 | "follow_time": follow_time, 39 | } 40 | ) 41 | return followers 42 | 43 | 44 | def main(): 45 | parser = argparse.ArgumentParser(add_help=True) 46 | parser.add_argument("-u", type=str, help="username") 47 | parser.add_argument("-p", type=str, help="password") 48 | parser.add_argument("-proxy", type=str, help="proxy") 49 | parser.add_argument( 50 | "-message", 51 | type=str, 52 | nargs="?", 53 | help="message text", 54 | default="Hi, thanks for reaching me", 55 | ) 56 | args = parser.parse_args() 57 | 58 | bot = Bot() 59 | bot.login(username=args.u, password=args.p, proxy=args.proxy) 60 | 61 | start_time = datetime.datetime.utcnow() 62 | 63 | while True: 64 | try: 65 | new_followers = get_recent_followers(bot, start_time) 66 | except ValueError as err: 67 | print(err) 68 | time.sleep(RETRY_DELAY) 69 | continue 70 | 71 | if new_followers: 72 | print( 73 | "Found new followers. Count: {count}".format(count=len(new_followers)) 74 | ) 75 | 76 | for follower in new_followers: 77 | print("New follower: {}".format(follower["username"])) 78 | bot.send_message(args.message, str(follower["user_id"])) 79 | 80 | start_time = datetime.datetime.utcnow() 81 | time.sleep(DELAY) 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /instabot/__init__.py: -------------------------------------------------------------------------------- 1 | from . import utils 2 | from .bot import Bot 3 | from .api import API 4 | 5 | __version__ = "0.105.0" 6 | __all__ = ["utils", "API", "Bot"] 7 | -------------------------------------------------------------------------------- /instabot/api/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | __all__ = ["API"] 4 | -------------------------------------------------------------------------------- /instabot/api/api_story.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import json 4 | import os 5 | import shutil 6 | import time 7 | from random import randint 8 | 9 | from requests_toolbelt import MultipartEncoder 10 | 11 | from . import config 12 | from .api_photo import get_image_size, stories_shaper 13 | 14 | 15 | def download_story(self, filename, story_url, username): 16 | path = "stories/{}".format(username) 17 | if not os.path.exists(path): 18 | os.makedirs(path) 19 | fname = os.path.join(path, filename) 20 | if os.path.exists(fname): # already exists 21 | self.logger.info("Stories already downloaded...") 22 | return os.path.abspath(fname) 23 | response = self.session.get(story_url, stream=True) 24 | if response.status_code == 200: 25 | with open(fname, "wb") as f: 26 | response.raw.decode_content = True 27 | shutil.copyfileobj(response.raw, f) 28 | return os.path.abspath(fname) 29 | 30 | 31 | def upload_story_photo(self, photo, upload_id=None): 32 | if upload_id is None: 33 | upload_id = str(int(time.time() * 1000)) 34 | photo = stories_shaper(photo) 35 | if not photo: 36 | return False 37 | 38 | with open(photo, "rb") as f: 39 | photo_bytes = f.read() 40 | 41 | data = { 42 | "upload_id": upload_id, 43 | "_uuid": self.uuid, 44 | "_csrftoken": self.token, 45 | "image_compression": '{"lib_name":"jt","lib_version":"1.3.0",' 46 | + 'quality":"87"}', 47 | "photo": ( 48 | "pending_media_%s.jpg" % upload_id, 49 | photo_bytes, 50 | "application/octet-stream", 51 | {"Content-Transfer-Encoding": "binary"}, 52 | ), 53 | } 54 | m = MultipartEncoder(data, boundary=self.uuid) 55 | self.session.headers.update( 56 | { 57 | "Accept-Encoding": "gzip, deflate", 58 | "Content-type": m.content_type, 59 | "Connection": "close", 60 | "User-Agent": self.user_agent, 61 | } 62 | ) 63 | response = self.session.post(config.API_URL + "upload/photo/", data=m.to_string()) 64 | 65 | if response.status_code == 200: 66 | upload_id = json.loads(response.text).get("upload_id") 67 | if self.configure_story(upload_id, photo): 68 | # self.expose() 69 | return True 70 | return False 71 | 72 | 73 | def configure_story(self, upload_id, photo): 74 | (w, h) = get_image_size(photo) 75 | data = self.json_data( 76 | { 77 | "source_type": 4, 78 | "upload_id": upload_id, 79 | "story_media_creation_date": str(int(time.time()) - randint(11, 20)), 80 | "client_shared_at": str(int(time.time()) - randint(3, 10)), 81 | "client_timestamp": str(int(time.time())), 82 | "configure_mode": 1, # 1 - REEL_SHARE, 2 - DIRECT_STORY_SHARE 83 | "device": self.device_settings, 84 | "edits": { 85 | "crop_original_size": [w * 1.0, h * 1.0], 86 | "crop_center": [0.0, 0.0], 87 | "crop_zoom": 1.3333334, 88 | }, 89 | "extra": {"source_width": w, "source_height": h}, 90 | } 91 | ) 92 | return self.send_request("media/configure_to_story/?", data) 93 | -------------------------------------------------------------------------------- /instabot/api/devices.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | APP_VERSION = "136.0.0.34.124" 4 | VERSION_CODE = "208061712" 5 | DEVICES = { 6 | "one_plus_7": { 7 | "app_version": APP_VERSION, 8 | "android_version": "29", 9 | "android_release": "10.0", 10 | "dpi": "420dpi", 11 | "resolution": "1080x2340", 12 | "manufacturer": "OnePlus", 13 | "device": "GM1903", 14 | "model": "OnePlus7", 15 | "cpu": "qcom", 16 | "version_code": VERSION_CODE, 17 | }, 18 | "one_plus_3": { 19 | "app_version": APP_VERSION, 20 | "android_version": "28", 21 | "android_release": "9.0", 22 | "dpi": "420dpi", 23 | "resolution": "1080x1920", 24 | "manufacturer": "OnePlus", 25 | "device": "ONEPLUS A3003", 26 | "model": "OnePlus3", 27 | "cpu": "qcom", 28 | "version_code": VERSION_CODE, 29 | }, 30 | # Released on March 2016 31 | "samsung_galaxy_s7": { 32 | "app_version": APP_VERSION, 33 | "android_version": "26", 34 | "android_release": "8.0", 35 | "dpi": "640dpi", 36 | "resolution": "1440x2560", 37 | "manufacturer": "samsung", 38 | "device": "SM-G930F", 39 | "model": "herolte", 40 | "cpu": "samsungexynos8890", 41 | "version_code": VERSION_CODE, 42 | }, 43 | # Released on January 2017 44 | "huawei_mate_9_pro": { 45 | "app_version": APP_VERSION, 46 | "android_version": "24", 47 | "android_release": "7.0", 48 | "dpi": "640dpi", 49 | "resolution": "1440x2560", 50 | "manufacturer": "HUAWEI", 51 | "device": "LON-L29", 52 | "model": "HWLON", 53 | "cpu": "hi3660", 54 | "version_code": VERSION_CODE, 55 | }, 56 | # Released on February 2018 57 | "samsung_galaxy_s9_plus": { 58 | "app_version": APP_VERSION, 59 | "android_version": "28", 60 | "android_release": "9.0", 61 | "dpi": "640dpi", 62 | "resolution": "1440x2560", 63 | "manufacturer": "samsung", 64 | "device": "SM-G965F", 65 | "model": "star2qltecs", 66 | "cpu": "samsungexynos9810", 67 | "version_code": VERSION_CODE, 68 | }, 69 | # Released on November 2016 70 | "one_plus_3t": { 71 | "app_version": APP_VERSION, 72 | "android_version": "26", 73 | "android_release": "8.0", 74 | "dpi": "380dpi", 75 | "resolution": "1080x1920", 76 | "manufacturer": "OnePlus", 77 | "device": "ONEPLUS A3010", 78 | "model": "OnePlus3T", 79 | "cpu": "qcom", 80 | "version_code": VERSION_CODE, 81 | }, 82 | # Released on April 2016 83 | "lg_g5": { 84 | "app_version": APP_VERSION, 85 | "android_version": "23", 86 | "android_release": "6.0.1", 87 | "dpi": "640dpi", 88 | "resolution": "1440x2392", 89 | "manufacturer": "LGE/lge", 90 | "device": "RS988", 91 | "model": "h1", 92 | "cpu": "h1", 93 | "version_code": VERSION_CODE, 94 | }, 95 | # Released on June 2016 96 | "zte_axon_7": { 97 | "app_version": APP_VERSION, 98 | "android_version": "23", 99 | "android_release": "6.0.1", 100 | "dpi": "640dpi", 101 | "resolution": "1440x2560", 102 | "manufacturer": "ZTE", 103 | "device": "ZTE A2017U", 104 | "model": "ailsa_ii", 105 | "cpu": "qcom", 106 | "version_code": VERSION_CODE, 107 | }, 108 | # Released on March 2016 109 | "samsung_galaxy_s7_edge": { 110 | "app_version": APP_VERSION, 111 | "android_version": "23", 112 | "android_release": "6.0.1", 113 | "dpi": "640dpi", 114 | "resolution": "1440x2560", 115 | "manufacturer": "samsung", 116 | "device": "SM-G935", 117 | "model": "hero2lte", 118 | "cpu": "samsungexynos8890", 119 | "version_code": VERSION_CODE, 120 | }, 121 | } 122 | DEFAULT_DEVICE = random.choice(list(DEVICES.keys())) 123 | -------------------------------------------------------------------------------- /instabot/api/prepare.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import getpass 4 | import os 5 | import sys 6 | 7 | DEFAULT_SECRET_DIR = os.path.abspath(os.getcwd()) 8 | 9 | 10 | def get_credential_file(base_path=DEFAULT_SECRET_DIR): 11 | return base_path + "/config/secret.txt" 12 | 13 | 14 | def add_credentials(base_path): 15 | SECRET_FILE = get_credential_file(base_path) 16 | with open(SECRET_FILE, "a") as f: 17 | print("Enter your login: ") 18 | f.write(str(sys.stdin.readline().strip()) + ":") 19 | print( 20 | "Enter your password: (it will not be shown due to security " 21 | + "reasons - just start typing and press Enter)" 22 | ) 23 | f.write(getpass.getpass() + "\n") 24 | 25 | 26 | def get_credentials(base_path, username=None): 27 | SECRET_FILE = get_credential_file(base_path) 28 | """Returns login and password stored in `secret.txt`.""" 29 | while not check_secret(): 30 | pass 31 | while True: 32 | try: 33 | with open(SECRET_FILE, "r") as f: 34 | lines = [line.strip().split(":", 2) for line in f.readlines()] 35 | except ValueError: 36 | msg = "Problem with opening `{}`, will remove the file." 37 | raise Exception(msg.format(SECRET_FILE)) 38 | if username is not None: 39 | for login, password in lines: 40 | if login == username.strip(): 41 | return login, password 42 | print("Which account do you want to use? (Type number)") 43 | for ind, (login, password) in enumerate(lines): 44 | print("%d: %s" % (ind + 1, login)) 45 | print("%d: %s" % (0, "add another account.")) 46 | print("%d: %s" % (-1, "delete all accounts.")) 47 | try: 48 | ind = int(sys.stdin.readline()) 49 | if ind == 0: 50 | add_credentials(base_path) 51 | continue 52 | elif ind == -1: 53 | delete_credentials(base_path) 54 | check_secret(base_path) 55 | continue 56 | elif 0 <= ind - 1 < len(lines): 57 | return lines[ind - 1] 58 | except Exception: 59 | print("Wrong input, enter the number of the account to use.") 60 | 61 | 62 | def check_secret(base_path): 63 | SECRET_FILE = get_credential_file(base_path) 64 | while True: 65 | if os.path.exists(SECRET_FILE): 66 | with open(SECRET_FILE, "r") as f: 67 | try: 68 | login, password = f.readline().strip().split(":") 69 | if len(login) < 4 or len(password) < 6: 70 | 71 | print( 72 | "Data in `secret.txt` file is invalid. " 73 | "We will delete it and try again." 74 | ) 75 | 76 | os.remove(SECRET_FILE) 77 | else: 78 | return True 79 | except Exception: 80 | print("Your file is broken. We will delete it " + "and try again.") 81 | os.remove(SECRET_FILE) 82 | else: 83 | print( 84 | "We need to create a text file '%s' where " 85 | "we will store your login and password from Instagram." % SECRET_FILE 86 | ) 87 | print("Don't worry. It will be stored locally.") 88 | while True: 89 | add_credentials(base_path) 90 | print("Do you want to add another account? (y/n)") 91 | if "y" not in sys.stdin.readline(): 92 | break 93 | 94 | 95 | def delete_credentials(base_path): 96 | SECRET_FILE = get_credential_file(base_path) 97 | if os.path.exists(SECRET_FILE): 98 | os.remove(SECRET_FILE) 99 | 100 | 101 | if __name__ == "__main__": 102 | check_secret() 103 | -------------------------------------------------------------------------------- /instabot/bot/__init__.py: -------------------------------------------------------------------------------- 1 | from .bot import Bot 2 | 3 | __all__ = ["Bot"] 4 | -------------------------------------------------------------------------------- /instabot/bot/bot_archive.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | 3 | 4 | def archive(self, media_id, undo=False): 5 | self.small_delay() 6 | media = self.get_media_info(media_id) 7 | media = media[0] if isinstance(media, list) else media 8 | if self.api.archive_media(media, undo): 9 | self.total["archived"] += int(not undo) 10 | self.total["unarchived"] += int(undo) 11 | return True 12 | self.logger.info( 13 | "Media id %s is not %s.", media_id, "unarchived" if undo else "archived" 14 | ) 15 | return False 16 | 17 | 18 | def archive_medias(self, medias): 19 | broken_items = [] 20 | if not medias: 21 | self.logger.info("Nothing to archive.") 22 | return broken_items 23 | self.logger.info("Going to archive %d medias." % (len(medias))) 24 | for media in tqdm(medias): 25 | if not self.archive(media): 26 | self.error_delay() 27 | broken_items = medias[medias.index(media) :] 28 | break 29 | self.logger.info("DONE: Total archived %d medias." % self.total["archived"]) 30 | return broken_items 31 | 32 | 33 | def unarchive_medias(self, medias): 34 | broken_items = [] 35 | if not medias: 36 | self.logger.info("Nothing to unarchive.") 37 | return broken_items 38 | self.logger.info("Going to unarchive %d medias." % (len(medias))) 39 | for media in tqdm(medias): 40 | if not self.unarchive(media): 41 | self.error_delay() 42 | broken_items = medias[medias.index(media) :] 43 | break 44 | self.logger.info("DONE: Total unarchived %d medias." % self.total["unarchived"]) 45 | return broken_items 46 | -------------------------------------------------------------------------------- /instabot/bot/bot_block.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from tqdm import tqdm 4 | 5 | 6 | def block(self, user_id): 7 | user_id = self.convert_to_user_id(user_id) 8 | if self.check_not_bot(user_id): 9 | return True 10 | if not self.reached_limit("blocks"): 11 | self.delay("block") 12 | if self.api.block(user_id): 13 | self.total["blocks"] += 1 14 | return True 15 | else: 16 | self.logger.info("Out of blocks for today.") 17 | return False 18 | 19 | 20 | def unblock(self, user_id): 21 | user_id = self.convert_to_user_id(user_id) 22 | if not self.reached_limit("unblocks"): 23 | self.delay("unblock") 24 | if self.api.unblock(user_id): 25 | self.total["unblocks"] += 1 26 | return True 27 | else: 28 | self.logger.info("Out of blocks for today.") 29 | return False 30 | 31 | 32 | def block_users(self, user_ids): 33 | broken_items = [] 34 | self.logger.info("Going to block %d users." % len(user_ids)) 35 | for user_id in tqdm(user_ids): 36 | if not self.block(user_id): 37 | self.error_delay() 38 | broken_items = user_ids[user_ids.index(user_id) :] 39 | break 40 | self.logger.info("DONE: Total blocked %d users." % self.total["blocks"]) 41 | return broken_items 42 | 43 | 44 | def unblock_users(self, user_ids): 45 | broken_items = [] 46 | self.logger.info("Going to unblock %d users." % len(user_ids)) 47 | for user_id in tqdm(user_ids): 48 | if not self.unblock(user_id): 49 | self.error_delay() 50 | broken_items.append(user_id) 51 | self.logger.info("DONE: Total unblocked %d users." % self.total["unblocks"]) 52 | return broken_items 53 | 54 | 55 | def block_bots(self): 56 | self.logger.info("Going to block bots.") 57 | your_followers = self.followers 58 | your_likers = self.get_user_likers(self.user_id) 59 | not_likers = list(set(your_followers) - set(your_likers)) 60 | random.shuffle(not_likers) 61 | for user in tqdm(not_likers): 62 | if not self.check_not_bot(user): 63 | self.logger.info( 64 | "Found bot: " 65 | "https://instagram.com/%s/" % self.get_user_info(user)["username"] 66 | ) 67 | self.block(user) 68 | -------------------------------------------------------------------------------- /instabot/bot/bot_checkpoint.py: -------------------------------------------------------------------------------- 1 | """ 2 | Instabot Checkpoint methods. 3 | """ 4 | 5 | import os 6 | import pickle 7 | from datetime import datetime 8 | 9 | CHECKPOINT_PATH = "{fname}.checkpoint" 10 | 11 | 12 | class Checkpoint(object): 13 | """ 14 | Checkpoint for instabot.Bot class which can store: 15 | .total[] - all Bot's counters 16 | .blocked_actions[] - Bot's blocked actions 17 | .following (list of user_ids) 18 | .followers (list of user_ids) 19 | .date (of checkpoint creation) 20 | """ 21 | 22 | def __init__(self, bot): 23 | self.total = {} 24 | for k in bot.total: 25 | self.total[k] = bot.total[k] 26 | self.blocked_actions = {} 27 | for k in bot.blocked_actions: 28 | self.blocked_actions[k] = bot.blocked_actions[k] 29 | self.start_time = bot.start_time 30 | self.date = datetime.now() 31 | self.total_requests = bot.api.total_requests 32 | # self.bot = bot 33 | 34 | def fill_following(self, bot): 35 | self._following = [item["pk"] for item in bot.api.get_total_self_followings()] 36 | 37 | def fill_followers(self, bot): 38 | self._followers = [item["pk"] for item in bot.api.get_total_self_followers()] 39 | 40 | def dump(self): 41 | return (self.total, self.blocked_actions, self.total_requests, self.start_time) 42 | 43 | 44 | def save_checkpoint(self): 45 | checkpoint = Checkpoint(self) 46 | fname = CHECKPOINT_PATH.format(fname=self.api.username) 47 | fname = os.path.join(self.base_path, fname) 48 | self.logger.debug("Saving Checkpoint file to: {}".format(fname)) 49 | with open(fname, "wb") as f: 50 | pickle.dump(checkpoint, f, -1) 51 | return True 52 | 53 | 54 | def load_checkpoint(self): 55 | try: 56 | fname = CHECKPOINT_PATH.format(fname=self.api.username) 57 | fname = os.path.join(self.base_path, fname) 58 | self.logger.debug("Loading Checkpoint file from: {}".format(fname)) 59 | with open(fname, "rb") as f: 60 | checkpoint = pickle.load(f) 61 | if isinstance(checkpoint, Checkpoint): 62 | return checkpoint.dump() 63 | else: 64 | os.remove(fname) 65 | except Exception: 66 | pass 67 | return None 68 | -------------------------------------------------------------------------------- /instabot/bot/bot_comment.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bot functions to generate and post a comments. 3 | 4 | Instructions to file with comments: 5 | one line - one comment. 6 | 7 | Example: 8 | lol 9 | kek 10 | 11 | """ 12 | from tqdm import tqdm 13 | 14 | 15 | def comment(self, media_id, comment_text): 16 | if self.is_commented(media_id): 17 | return True 18 | if not self.reached_limit("comments"): 19 | if self.blocked_actions["comments"]: 20 | self.logger.warning("YOUR `COMMENT` ACTION IS BLOCKED") 21 | if self.blocked_actions_protection: 22 | from datetime import timedelta 23 | 24 | next_reset = (self.start_time.date() + timedelta(days=1)).strftime( 25 | "%Y-%m-%d %H:%M:%S" 26 | ) 27 | self.logger.warning( 28 | ( 29 | "blocked_actions_protection ACTIVE. " 30 | "Skipping `comment` action till, at least, {}." 31 | ).format(next_reset) 32 | ) 33 | return False 34 | self.delay("comment") 35 | _r = self.api.comment(media_id, comment_text) 36 | if _r == "feedback_required": 37 | self.logger.error("`Comment` action has been BLOCKED...!!!") 38 | return False 39 | if _r: 40 | self.total["comments"] += 1 41 | return True 42 | else: 43 | self.logger.info("Out of comments for today.") 44 | return False 45 | 46 | 47 | def reply_to_comment(self, media_id, comment_text, parent_comment_id): 48 | if not self.is_commented(media_id): 49 | self.logger.info("Media is not commented yet, nothing to answer to...") 50 | return False 51 | if not self.reached_limit("comments"): 52 | if self.blocked_actions["comments"]: 53 | self.logger.warning("YOUR `COMMENT` ACTION IS BLOCKED") 54 | if self.blocked_actions_protection: 55 | self.logger.warning( 56 | "blocked_actions_protection ACTIVE. " "Skipping `comment` action." 57 | ) 58 | return False 59 | self.delay("comment") 60 | media_owner = self.get_media_owner(media_id) 61 | comment_text = comment_text.replace( 62 | "[[username]]", self.get_username_from_user_id(media_owner) 63 | ) 64 | if comment_text[0] != "@": 65 | msg = ( 66 | "A reply must start with mention, so '@' must be the " 67 | "1st char, followed by the username you're replying to" 68 | ) 69 | self.logger.error(msg) 70 | return False 71 | if comment_text.split(" ")[0][1:] == self.get_username_from_user_id( 72 | self.user_id 73 | ): 74 | self.logger.error("You can't reply to yourself") 75 | return False 76 | _r = self.api.reply_to_comment(media_id, comment_text, parent_comment_id) 77 | if _r == "feedback_required": 78 | self.logger.error("`Comment` action has been BLOCKED...!!!") 79 | return False 80 | if _r: 81 | self.logger.info( 82 | "Replied to comment {} of media {}".format(parent_comment_id, media_id) 83 | ) 84 | self.total["comments"] += 1 85 | return True 86 | else: 87 | self.logger.info("Out of comments for today.") 88 | return False 89 | 90 | 91 | def comment_medias(self, medias): 92 | broken_items = [] 93 | self.logger.info("Going to comment %d medias." % (len(medias))) 94 | for media in tqdm(medias): 95 | if not self.check_media(media): 96 | continue 97 | if not self.is_commented(media): 98 | text = self.get_comment() 99 | self.logger.info("Commented with text: %s" % text) 100 | if not self.comment(media, text): 101 | self.delay("comment") 102 | broken_items = medias[medias.index(media) :] 103 | break 104 | self.logger.info("DONE: Total commented on %d medias. " % self.total["comments"]) 105 | return broken_items 106 | 107 | 108 | def comment_hashtag(self, hashtag, amount=None): 109 | self.logger.info("Going to comment medias by %s hashtag" % hashtag) 110 | medias = self.get_total_hashtag_medias(hashtag, amount) 111 | return self.comment_medias(medias) 112 | 113 | 114 | def comment_user(self, user_id, amount=None): 115 | """ Comments last user_id's medias """ 116 | if not self.check_user(user_id): 117 | return False 118 | self.logger.info("Going to comment user_%s's feed:" % user_id) 119 | user_id = self.convert_to_user_id(user_id) 120 | medias = self.get_user_medias(user_id, is_comment=True) 121 | if not medias: 122 | self.logger.info( 123 | "None medias received: account is closed or" "medias have been filtered." 124 | ) 125 | return False 126 | return self.comment_medias(medias[:amount]) 127 | 128 | 129 | def comment_users(self, user_ids, ncomments=None): 130 | for user_id in user_ids: 131 | if self.reached_limit("comments"): 132 | self.logger.info("Out of comments for today.") 133 | return 134 | self.comment_user(user_id, amount=ncomments) 135 | 136 | 137 | def comment_geotag(self, geotag): 138 | # TODO: comment every media from geotag 139 | pass 140 | 141 | 142 | def is_commented(self, media_id): 143 | return self.user_id in self.get_media_commenters(media_id) 144 | -------------------------------------------------------------------------------- /instabot/bot/bot_delete.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | 3 | 4 | def delete_media(self, media_id): 5 | self.small_delay() 6 | media = self.get_media_info(media_id) 7 | media = media[0] if isinstance(media, list) else media 8 | if self.api.delete_media(media): 9 | return True 10 | self.logger.info("Media with {} is not {}.".format(media.get("id"), "deleted")) 11 | return False 12 | 13 | 14 | def delete_medias(self, medias): 15 | broken_items = [] 16 | if not medias: 17 | self.logger.info("Nothing to delete.") 18 | return broken_items 19 | self.logger.info("Going to delete %d medias." % (len(medias))) 20 | for media in tqdm(medias): 21 | if not self.delete_media(media): 22 | self.error_delay() 23 | broken_items = medias[medias.index(media) :] 24 | break 25 | self.logger.info( 26 | "DONE: Total deleted %d medias." % (len(medias) - len(broken_items)) 27 | ) 28 | return broken_items 29 | 30 | 31 | def delete_comment(self, media_id, comment_id): 32 | if self.api.delete_comment(media_id, comment_id): 33 | self.small_delay() 34 | return True 35 | self.logger.info( 36 | "Comment with {} in media {} is not deleted.".format(comment_id, media_id) 37 | ) 38 | return False 39 | -------------------------------------------------------------------------------- /instabot/bot/bot_photo.py: -------------------------------------------------------------------------------- 1 | import os 2 | from io import open 3 | 4 | from tqdm import tqdm 5 | 6 | 7 | def upload_photo( 8 | self, photo, caption=None, upload_id=None, from_video=False, options={}, user_tags=None, is_sidecar=False 9 | ): 10 | """Upload photo to Instagram 11 | 12 | @param photo Path to photo file (String) 13 | @param caption Media description (String) 14 | @param upload_id Unique upload_id (String). When None, then 15 | generate automatically 16 | @param from_video A flag that signals whether the photo is loaded from 17 | the video or by itself (Boolean, DEPRECATED: not used) 18 | @param options Object with difference options, e.g. 19 | configure_timeout, rename (Dict) 20 | Designed to reduce the number of function arguments! 21 | This is the simplest request object. 22 | @param user_tags Tag other users (List) 23 | usertags = [ 24 | {"user_id": user_id, "position": [x, y]} 25 | ] 26 | @param is_sidecar An album element (Boolean) 27 | 28 | @return Object with state of uploading to Instagram (or False), Dict for is_sidecar 29 | """ 30 | self.small_delay() 31 | result = self.api.upload_photo( 32 | photo, caption, upload_id, from_video, options=options, user_tags=user_tags, is_sidecar=is_sidecar 33 | ) 34 | if not result: 35 | self.logger.info("Photo '{}' is not uploaded.".format(photo)) 36 | return False 37 | self.logger.info("Photo '{}' is uploaded.".format(photo)) 38 | return result 39 | 40 | 41 | def upload_album( 42 | self, photos, caption=None, upload_id=None, from_video=False, options={}, user_tags=None 43 | ): 44 | """Upload album to Instagram 45 | 46 | @param photos List of paths to photo files (List of strings) 47 | @param caption Media description (String) 48 | @param upload_id Unique upload_id (String). When None, then 49 | generate automatically 50 | @param from_video A flag that signals whether the photo is loaded from 51 | the video or by itself (Boolean, DEPRECATED: not used) 52 | @param options Object with difference options, e.g. 53 | configure_timeout, rename (Dict) 54 | Designed to reduce the number of function arguments! 55 | This is the simplest request object. 56 | @param user_tags 57 | 58 | @return Boolean 59 | """ 60 | self.small_delay() 61 | result = self.api.upload_album( 62 | photos, caption, upload_id, from_video, options=options, user_tags=user_tags 63 | ) 64 | if not result: 65 | self.logger.info("Photos are not uploaded.") 66 | return False 67 | self.logger.info("Photo are uploaded.") 68 | return result 69 | 70 | 71 | def download_photo( 72 | self, media_id, folder="photos", filename=None, save_description=False 73 | ): 74 | self.small_delay() 75 | 76 | if not os.path.exists(folder): 77 | os.makedirs(folder) 78 | 79 | if save_description: 80 | media = self.get_media_info(media_id)[0] 81 | caption = media["caption"]["text"] if media["caption"] else "" 82 | username = media["user"]["username"] 83 | fname = os.path.join(folder, "{}_{}.txt".format(username, media_id)) 84 | with open(fname, encoding="utf8", mode="w") as f: 85 | f.write(caption) 86 | 87 | try: 88 | return self.api.download_photo(media_id, filename, False, folder) 89 | except Exception: 90 | self.logger.info("Media with `{}` is not downloaded.".format(media_id)) 91 | return False 92 | 93 | 94 | def download_photos(self, medias, folder, save_description=False): 95 | broken_items = [] 96 | 97 | if not medias: 98 | self.logger.info("Nothing to downloads.") 99 | return broken_items 100 | 101 | self.logger.info("Going to download {} medias.".format(len(medias))) 102 | 103 | for media in tqdm(medias): 104 | if not self.download_photo(media, folder, save_description=save_description): 105 | self.error_delay() 106 | broken_items = medias[medias.index(media) :] 107 | return broken_items 108 | -------------------------------------------------------------------------------- /instabot/bot/bot_stats.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | 5 | def get_tsv_line(dictionary): 6 | line = "" 7 | for key in sorted(dictionary): 8 | line += str(dictionary[key]) + "\t" 9 | return line[:-1] + "\n" 10 | 11 | 12 | def get_header_line(dictionary): 13 | line = "\t".join(sorted(dictionary)) 14 | return line + "\n" 15 | 16 | 17 | def ensure_dir(file_path): 18 | directory = os.path.dirname(file_path) 19 | if not os.path.exists(directory) and directory: 20 | os.makedirs(directory) 21 | 22 | 23 | def dump_data(data, path): 24 | ensure_dir(path) 25 | if not os.path.exists(path): 26 | with open(path, "w") as f: 27 | f.write(get_header_line(data)) 28 | f.write(get_tsv_line(data)) 29 | else: 30 | with open(path, "a") as f: 31 | f.write(get_tsv_line(data)) 32 | 33 | 34 | def save_user_stats(self, username, path=""): 35 | if not username: 36 | username = self.api.username 37 | user_id = self.convert_to_user_id(username) 38 | infodict = self.get_user_info(user_id, use_cache=False) 39 | if infodict: 40 | data_to_save = { 41 | "date": str(datetime.datetime.now().replace(microsecond=0)), 42 | "followers": int(infodict["follower_count"]), 43 | "following": int(infodict["following_count"]), 44 | "medias": int(infodict["media_count"]), 45 | } 46 | file_path = os.path.join(path, "%s.tsv" % username) 47 | dump_data(data_to_save, file_path) 48 | self.logger.info("Stats saved at %s." % data_to_save["date"]) 49 | return False 50 | -------------------------------------------------------------------------------- /instabot/bot/bot_story.py: -------------------------------------------------------------------------------- 1 | def download_stories(self, username): 2 | user_id = self.get_user_id_from_username(username) 3 | list_image, list_video = self.get_user_stories(user_id) 4 | if list_image == [] and list_video == []: 5 | self.logger.error( 6 | ( 7 | "Make sure that '{}' is NOT private and that " "posted some stories" 8 | ).format(username) 9 | ) 10 | return False 11 | self.logger.info("Downloading stories...") 12 | for story_url in list_image: 13 | filename = story_url.split("/")[-1].split(".")[0] + ".jpg" 14 | self.api.download_story(filename, story_url, username) 15 | for story_url in list_video: 16 | filename = story_url.split("/")[-1].split(".")[0] + ".mp4" 17 | self.api.download_story(filename, story_url, username) 18 | 19 | 20 | def upload_story_photo(self, photo, upload_id=None): 21 | self.small_delay() 22 | if self.api.upload_story_photo(photo, upload_id): 23 | self.logger.info("Photo '{}' is uploaded as Story.".format(photo)) 24 | return True 25 | self.logger.info("Photo '{}' is not uploaded.".format(photo)) 26 | return False 27 | 28 | 29 | def watch_users_reels(self, user_ids, max_users=100): 30 | """ 31 | user_ids - the list of user_id to get their stories 32 | max_users - max amount of users to get stories from. 33 | 34 | It seems like Instagram doesn't allow to get stories 35 | from more that 100 users at once. 36 | """ 37 | 38 | # In case of only one user were passed 39 | if not isinstance(user_ids, list): 40 | user_ids = [user_ids] 41 | 42 | # Get users reels 43 | reels = self.api.get_users_reel(user_ids[:max_users]) 44 | 45 | # Filter to have users with at least 1 reel 46 | if isinstance(reels, list): 47 | # strange output 48 | return False 49 | 50 | reels = {k: v for k, v in reels.items() if "items" in v and len(v["items"]) > 0} 51 | 52 | # Filter reels that were not seen before 53 | unseen_reels = [] 54 | for _, reels_data in reels.items(): 55 | last_reel_seen_at = reels_data["seen"] if "seen" in reels_data else 0 56 | unseen_reels.extend( 57 | [r for r in reels_data["items"] if r["taken_at"] > last_reel_seen_at] 58 | ) 59 | 60 | # See reels that were not seen before 61 | # TODO: add counters for watched stories 62 | if self.api.see_reels(unseen_reels): 63 | self.total["stories_viewed"] += len(unseen_reels) 64 | return True 65 | return False 66 | -------------------------------------------------------------------------------- /instabot/bot/bot_support.py: -------------------------------------------------------------------------------- 1 | """ 2 | Support instabot's methods. 3 | """ 4 | 5 | from __future__ import unicode_literals 6 | 7 | import codecs 8 | import os 9 | import re 10 | import sys 11 | 12 | import huepy 13 | 14 | 15 | def check_if_file_exists(file_path, quiet=False): 16 | if not os.path.exists(file_path): 17 | if not quiet: 18 | print("Can't find '%s' file." % file_path) 19 | return False 20 | return True 21 | 22 | 23 | def read_list_from_file(file_path, quiet=False): 24 | """ 25 | Reads list from file. One line - one item. 26 | Returns the list if file items. 27 | """ 28 | try: 29 | if not check_if_file_exists(file_path, quiet=quiet): 30 | return [] 31 | with codecs.open(file_path, "r", encoding="utf-8") as f: 32 | content = f.readlines() 33 | if sys.version_info[0] < 3: 34 | content = [str(item.encode("utf8")) for item in content] 35 | content = [item.strip() for item in content] 36 | return [i for i in content if i] 37 | except Exception as exception: 38 | print(str(exception)) 39 | return [] 40 | 41 | 42 | def console_print(self, text, color=None): 43 | if self.verbosity: 44 | text = "\n" + text 45 | if color is not None: 46 | text = getattr(huepy, color)(text) 47 | print(text) 48 | 49 | 50 | def extract_urls(text): 51 | url_regex = ( 52 | r"((?:(?:http|https|Http|Https|rtsp|Rtsp)://" 53 | r"(?:(?:[a-zA-Z0-9$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:%[a-fA-F0-9]" 54 | r"{2})){1,64}(?::(?:[a-zA-Z0-9$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|" 55 | r"(?:%[a-fA-F0-9]{2})){1,25})?@)?)?(?:(?:(?:[a-zA-Z0-9" 56 | r"\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\_][a-zA-Z0-9" 57 | r"\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\_\-]{0,64}\.)+(?:(?:aero|" 58 | r"arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|" 59 | r"(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])" 60 | r"|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i" 61 | r"[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil" 62 | r"|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])" 63 | r"|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnort" 64 | r"uvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]" 65 | r"|w[fs]|(?:\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE|" 66 | r"\u0438\u0441\u043F\u044B\u0442\u0430\u043D\u0438\u0435|\u0440\u0444|" 67 | r"\u0441\u0440\u0431|\u05D8\u05E2\u05E1\u05D8|" 68 | r"\u0622\u0632\u0645\u0627\u06CC\u0634\u06CC|" 69 | r"\u0625\u062E\u062A\u0628\u0627\u0631|\u0627\u0644\u0627\u0631\u062F" 70 | r"\u0646|\u0627\u0644\u062C\u0632\u0627\u0626\u0631|" 71 | r"\u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629|" 72 | r"\u0627\u0644\u0645\u063A\u0631\u0628|\u0627\u0645\u0627\u0631\u0627" 73 | r"\u062A|\u0628\u06BE\u0627\u0631\u062A|\u062A\u0648\u0646\u0633|" 74 | r"\u0633\u0648\u0631\u064A\u0629|\u0641\u0644\u0633\u0637\u064A\u0646|" 75 | r"\u0642\u0637\u0631|\u0645\u0635\u0631|" 76 | r"\u092A\u0930\u0940\u0915\u094D\u0937\u093E|\u092D\u093E\u0930\u0924|" 77 | r"\u09AD\u09BE\u09B0\u09A4|\u0A2D\u0A3E\u0A30\u0A24|" 78 | r"\u0AAD\u0ABE\u0AB0\u0AA4|\u0B87\u0BA8\u0BCD\u0BA4\u0BBF\u0BAF\u0BBE|" 79 | r"\u0B87\u0BB2\u0B99\u0BCD\u0B95\u0BC8|" 80 | r"\u0B9A\u0BBF\u0B99\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0BC2\u0BB0\u0BCD|" 81 | r"\u0BAA\u0BB0\u0BBF\u0B9F\u0BCD\u0B9A\u0BC8|\u0C2D\u0C3E\u0C30\u0C24" 82 | r"\u0C4D|\u0DBD\u0D82\u0D9A\u0DCF|\u0E44\u0E17\u0E22|\u30C6\u30B9" 83 | r"\u30C8|\u4E2D\u56FD|\u4E2D\u570B|\u53F0\u6E7E|\u53F0\u7063|\u65B0" 84 | r"\u52A0\u5761|\u6D4B\u8BD5|\u6E2C\u8A66|\u9999\u6E2F|\uD14C\uC2A4" 85 | r"\uD2B8|\uD55C\uAD6D|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--3e0b707e" 86 | r"|xn--45brj9c|xn--80akhbyknj4f|xn--90a3ac|xn--9t4b11yi5a|xn--clchc0ea" 87 | r"0b2g2a9gcd|xn--deba0ad|xn--fiqs8s|xn--fiqz9s|xn--fpcrj9c3d|xn--" 88 | r"fzc2c9e2c|xn--g6w251d|xn--gecrj9c|xn--h2brj9c|xn--hgbk6aj7f53bba|xn" 89 | r"--hlcj6aya9esc7a|xn--j6w193g|xn--jxalpdlp|xn--kgbechtv|xn--kprw13d|" 90 | r"xn--kpry57d|xn--lgbbat1ad8j|xn--mgbaam7a8h|xn--mgbayh7gpa|" 91 | r"xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgberp4a5d4ar|xn--o3cw4h|" 92 | r"xn--ogbpf8fl|xn--p1ai|xn--pgbs0dh|xn--s9brj9c|xn--wgbh1c|xn--wgbl6a|" 93 | r"xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--yfro4i67o|xn--ygbi2ammx|" 94 | r"xn--zckzah|xxx)|y[et]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|" 95 | r"[0-1][0-9]{2}|[1-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]" 96 | r"{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9]" 97 | r"[0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|" 98 | r"[0-9])))(?::\d{1,5})?(?:/(?:(?:[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF" 99 | r"\uFDF0-\uFFEF\;\/\?\:\@\&\=\#\~\-\.\+\!\*\'\(\)\,\_])|" 100 | r"(?:%[a-fA-F0-9]{2}))*)?)(?:\b|$)" 101 | ) # noqa 102 | urls = re.findall(url_regex, text) 103 | 104 | return urls 105 | -------------------------------------------------------------------------------- /instabot/bot/bot_unfollow.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | import time 3 | 4 | 5 | def unfollow(self, user_id): 6 | user_id = self.convert_to_user_id(user_id) 7 | user_info = self.get_user_info(user_id) 8 | 9 | if not user_info: 10 | self.logger.info("Can't get user_id=%s info" % str(user_id)) 11 | return False # No user_info 12 | 13 | username = user_info.get("username") 14 | 15 | if self.log_follow_unfollow: 16 | msg = "Going to unfollow `user_id` {} with username {}.".format( 17 | user_id, username 18 | ) 19 | self.logger.info(msg) 20 | else: 21 | self.console_print( 22 | "===> Going to unfollow `user_id`: {} with username: {}".format( 23 | user_id, username 24 | ) 25 | ) 26 | 27 | if self.check_user(user_id, unfollowing=True): 28 | return True # whitelisted user 29 | if not self.reached_limit("unfollows"): 30 | if self.blocked_actions["unfollows"]: 31 | self.logger.warning("YOUR `UNFOLLOW` ACTION IS BLOCKED") 32 | if self.blocked_actions_protection: 33 | self.logger.warning( 34 | "blocked_actions_protection ACTIVE. " "Skipping `unfollow` action." 35 | ) 36 | return False 37 | self.delay("unfollow") 38 | _r = self.api.unfollow(user_id) 39 | if _r == "feedback_required": 40 | self.logger.error("`Unfollow` action has been BLOCKED...!!!") 41 | if not self.blocked_actions_sleep: 42 | if self.blocked_actions_protection: 43 | self.logger.warning( 44 | "Activating blocked actions \ 45 | protection for `Unfollow` action." 46 | ) 47 | self.blocked_actions["unfollows"] = True 48 | else: 49 | if ( 50 | self.sleeping_actions["unfollows"] 51 | and self.blocked_actions_protection 52 | ): 53 | self.logger.warning( 54 | "This is the second blocked \ 55 | `Unfollow` action." 56 | ) 57 | self.logger.warning( 58 | "Activating blocked actions \ 59 | protection for `Unfollow` action." 60 | ) 61 | self.sleeping_actions["unfollows"] = False 62 | self.blocked_actions["unfollows"] = True 63 | else: 64 | self.logger.info( 65 | "`Unfollow` action is going to sleep \ 66 | for %s seconds." 67 | % self.blocked_actions_sleep_delay 68 | ) 69 | self.sleeping_actions["unfollows"] = True 70 | time.sleep(self.blocked_actions_sleep_delay) 71 | return False 72 | if _r: 73 | if self.log_follow_unfollow: 74 | msg = "Unfollowed `user_id` {} with username {}".format( 75 | user_id, username 76 | ) 77 | self.logger.info(msg) 78 | else: 79 | msg = "===> Unfollowed, `user_id`: {}, user_name: {}" 80 | self.console_print(msg.format(user_id, username), "yellow") 81 | self.unfollowed_file.append(user_id) 82 | self.total["unfollows"] += 1 83 | if user_id in self.following: 84 | self.following.remove(user_id) 85 | if self.blocked_actions_sleep and self.sleeping_actions["unfollows"]: 86 | self.logger.info("`Unfollow` action is no longer sleeping.") 87 | self.sleeping_actions["unfollows"] = False 88 | return True 89 | else: 90 | self.logger.info("Out of unfollows for today.") 91 | return False 92 | 93 | 94 | def unfollow_users(self, user_ids): 95 | broken_items = [] 96 | self.logger.info("Going to unfollow {} users.".format(len(user_ids))) 97 | user_ids = set(map(str, user_ids)) 98 | filtered_user_ids = list(set(user_ids) - set(self.whitelist)) 99 | if len(filtered_user_ids) != len(user_ids): 100 | self.logger.info( 101 | "After filtration by whitelist {} users left.".format( 102 | len(filtered_user_ids) 103 | ) 104 | ) 105 | for user_id in tqdm(filtered_user_ids, desc="Processed users"): 106 | if not self.unfollow(user_id): 107 | self.error_delay() 108 | i = filtered_user_ids.index(user_id) 109 | broken_items = filtered_user_ids[i:] 110 | break 111 | self.logger.info("DONE: Total unfollowed {} users.".format(self.total["unfollows"])) 112 | return broken_items 113 | 114 | 115 | def unfollow_non_followers(self, n_to_unfollows=None): 116 | self.logger.info("Unfollowing non-followers.") 117 | self.console_print(" ===> Start unfollowing non-followers <===", "red") 118 | non_followers = set(self.following) - set(self.followers) - self.friends_file.set 119 | non_followers = list(non_followers) 120 | for user_id in tqdm(non_followers[:n_to_unfollows]): 121 | if self.reached_limit("unfollows"): 122 | self.logger.info("Out of unfollows for today.") 123 | break 124 | self.unfollow(user_id) 125 | self.console_print(" ===> Unfollow non-followers done! <===", "red") 126 | 127 | 128 | def unfollow_everyone(self): 129 | self.unfollow_users(self.following) 130 | -------------------------------------------------------------------------------- /instabot/bot/bot_unlike.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | 3 | 4 | def unlike(self, media_id): 5 | if not self.reached_limit("unlikes"): 6 | self.delay("unlike") 7 | if self.api.unlike(media_id): 8 | self.total["unlikes"] += 1 9 | return True 10 | else: 11 | self.logger.info("Out of unlikes for today.") 12 | return False 13 | 14 | 15 | def unlike_comment(self, comment_id): 16 | if self.api.unlike_comment(comment_id): 17 | return True 18 | return False 19 | 20 | 21 | def unlike_media_comments(self, media_id): 22 | broken_items = [] 23 | media_comments = self.get_media_comments(media_id) 24 | comment_ids = [item["pk"] for item in media_comments if item["has_liked_comment"]] 25 | 26 | if not comment_ids: 27 | self.logger.info( 28 | "None comments received: comments not found" 29 | " or comments have been filtered." 30 | ) 31 | return broken_items 32 | 33 | self.logger.info("Going to unlike %d comments." % (len(comment_ids))) 34 | 35 | for comment in tqdm(comment_ids): 36 | if not self.unlike_comment(comment): 37 | self.error_delay() 38 | broken_items = comment_ids[comment_ids.index(comment) :] 39 | self.logger.info( 40 | "DONE: Unliked {count} comments.".format( 41 | count=len(comment_ids) - len(broken_items) 42 | ) 43 | ) 44 | return broken_items 45 | 46 | 47 | def unlike_medias(self, medias): 48 | broken_items = [] 49 | self.logger.info("Going to unlike %d medias." % (len(medias))) 50 | for media in tqdm(medias): 51 | if not self.unlike(media): 52 | self.error_delay() 53 | broken_items = medias[medias.index(media) :] 54 | break 55 | self.logger.info("DONE: Total unliked %d medias." % self.total["unlikes"]) 56 | return broken_items 57 | 58 | 59 | def unlike_user(self, user_id): 60 | self.logger.info("Going to unlike user %s's feed:" % user_id) 61 | user_id = self.convert_to_user_id(user_id) 62 | medias = self.get_user_medias(user_id, filtration=False) 63 | return self.unlike_medias(medias) 64 | -------------------------------------------------------------------------------- /instabot/bot/bot_video.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def upload_video(self, video, caption="", thumbnail=None, options={}): 5 | """Upload video to Instagram 6 | 7 | @param video Path to video file (String) 8 | @param caption Media description (String) 9 | @param thumbnail Path to thumbnail for video (String). When None, then 10 | thumbnail is generate automatically 11 | @param options Object with difference options, e.g. configure_timeout, 12 | rename_thumbnail, rename (Dict) 13 | Designed to reduce the number of function arguments! 14 | 15 | @return Object with state of uploading to Instagram (or False) 16 | """ 17 | self.small_delay() 18 | self.logger.info("Started uploading '{video}'".format(video=video)) 19 | result = self.api.upload_video( 20 | video, caption=caption, thumbnail=thumbnail, options=options 21 | ) 22 | if not result: 23 | self.logger.info("Video '{}' is not {} .".format(video, "uploaded")) 24 | return False 25 | self.logger.info("Video '{video}' uploaded".format(video=video)) 26 | return result 27 | 28 | 29 | def download_video( 30 | self, media_id, folder="videos", filename=None, save_description=False 31 | ): 32 | self.small_delay() 33 | if not os.path.exists(folder): 34 | os.makedirs(folder) 35 | if save_description: 36 | media = self.get_media_info(media_id)[0] 37 | caption = media["caption"]["text"] if media["caption"] else "" 38 | username = media["user"]["username"] 39 | fname = os.path.join(folder, "{}_{}.txt".format(username, media_id)) 40 | with open(fname, encoding="utf8", mode="w") as f: 41 | f.write(caption) 42 | try: 43 | return self.api.download_video(media_id, filename, False, folder) 44 | except Exception: 45 | self.logger.info("Media with `{}` is not downloaded.".format(media_id)) 46 | return False 47 | -------------------------------------------------------------------------------- /instabot/bot/state/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/instabot/bot/state/__init__.py -------------------------------------------------------------------------------- /instabot/bot/state/bot_cache.py: -------------------------------------------------------------------------------- 1 | from instabot.singleton import Singleton 2 | 3 | 4 | class BotCache(object): 5 | __metaclass__ = Singleton 6 | 7 | def __init__(self): 8 | self.following = None 9 | self.followers = None 10 | self.user_infos = {} # User info cache 11 | self.usernames = {} # `username` to `user_id` mapping 12 | 13 | def __repr__(self): 14 | return self.__dict__ 15 | -------------------------------------------------------------------------------- /instabot/bot/state/bot_state.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from instabot.singleton import Singleton 4 | 5 | 6 | class BotState(object): 7 | __metaclass__ = Singleton 8 | 9 | def __init__(self): 10 | self.start_time = datetime.datetime.now() 11 | self.total = dict.fromkeys( 12 | [ 13 | "likes", 14 | "unlikes", 15 | "follows", 16 | "unfollows", 17 | "comments", 18 | "blocks", 19 | "unblocks", 20 | "messages", 21 | "archived", 22 | "unarchived", 23 | "stories_viewed", 24 | ], 25 | 0, 26 | ) 27 | self.blocked_actions = dict.fromkeys( 28 | [ 29 | "likes", 30 | "unlikes", 31 | "follows", 32 | "unfollows", 33 | "comments", 34 | "blocks", 35 | "unblocks", 36 | "messages", 37 | ], 38 | False, 39 | ) 40 | self.sleeping_actions = dict.fromkeys( 41 | [ 42 | "likes", 43 | "unlikes", 44 | "follows", 45 | "unfollows", 46 | "comments", 47 | "blocks", 48 | "unblocks", 49 | "messages", 50 | ], 51 | False, 52 | ) 53 | self.last = dict.fromkeys( 54 | [ 55 | "like", 56 | "unlike", 57 | "follow", 58 | "unfollow", 59 | "comment", 60 | "block", 61 | "unblock", 62 | "message", 63 | ], 64 | 0, 65 | ) 66 | 67 | def __repr__(self): 68 | return self.__dict__ 69 | -------------------------------------------------------------------------------- /instabot/singleton.py: -------------------------------------------------------------------------------- 1 | class Singleton(type): 2 | 3 | _instances = {} 4 | 5 | def __call__(cls, *args, **kwargs): 6 | if cls not in cls._instances: 7 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 8 | return cls._instances[cls] 9 | -------------------------------------------------------------------------------- /instabot/utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | from collections import OrderedDict 3 | 4 | from huepy import bold, green, orange 5 | 6 | 7 | class file(object): 8 | def __init__(self, fname, verbose=True): 9 | self.fname = fname 10 | self.verbose = verbose 11 | open(self.fname, "a").close() 12 | 13 | @property 14 | def list(self): 15 | with open(self.fname, "r") as f: 16 | lines = [x.strip("\n") for x in f.readlines()] 17 | return [x for x in lines if x] 18 | 19 | @property 20 | def set(self): 21 | return set(self.list) 22 | 23 | def __iter__(self): 24 | for i in self.list: 25 | yield next(iter(i)) 26 | 27 | def __len__(self): 28 | return len(self.list) 29 | 30 | def append(self, item, allow_duplicates=False): 31 | if self.verbose: 32 | msg = "Adding '{}' to `{}`.".format(item, self.fname) 33 | print(bold(green(msg))) 34 | 35 | if not allow_duplicates and str(item) in self.list: 36 | msg = "'{}' already in `{}`.".format(item, self.fname) 37 | print(bold(orange(msg))) 38 | return 39 | 40 | with open(self.fname, "a") as f: 41 | f.write("{item}\n".format(item=item)) 42 | 43 | def remove(self, x): 44 | x = str(x) 45 | items = self.list 46 | if x in items: 47 | items.remove(x) 48 | msg = "Removing '{}' from `{}`.".format(x, self.fname) 49 | print(bold(green(msg))) 50 | self.save_list(items) 51 | 52 | def random(self): 53 | return random.choice(self.list) 54 | 55 | def remove_duplicates(self): 56 | return list(OrderedDict.fromkeys(self.list)) 57 | 58 | def save_list(self, items): 59 | with open(self.fname, "w") as f: 60 | for item in items: 61 | f.write("{item}\n".format(item=item)) 62 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | LAST_COMMIT_MESSAGE="$(git log --no-merges -2 --pretty=%B)" 2 | git config --global user.email "travis@travis-ci.org" 3 | git config --global user.name "Travis CI" 4 | git tag -a "${COMMIT_MESSAGE}" -m "${LAST_COMMIT_MESSAGE}" -m "[ci skip]" 5 | git remote remove origin 6 | git remote add origin https://${GITHUB_TOKEN}@github.com/instagrambot/instabot.git 7 | git push origin --tags HEAD:master 8 | 9 | 10 | # build and push to PYPI 11 | python setup.py sdist 12 | pip install twine 13 | twine upload -u $PYPI_USERNAME -p $PYPI_PASSWORD dist/* 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi>=2019.11.28 2 | chardet>=3.0.4 3 | future>=0.18.2 4 | huepy>=1.2.1 5 | idna>=2.8 6 | pysocks>=1.7.1 7 | pytz>=2019.3 8 | requests>=2.22.0 9 | requests-toolbelt>=0.9.1 10 | responses>=0.10.9 11 | schedule>=0.6.0 12 | six>=1.14.0 13 | tqdm>=4.41.1 14 | urllib3>=1.25.7 15 | mock>=3.0.5 16 | moviepy>=1.0.1 17 | Pillow>=6.2.2 18 | pytest>=4.6.9 19 | rsa>=4.0 20 | pycryptodome>=3.9.7 21 | pycryptodomex>=3.9.7 22 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = build,.git,.tox,./tests/,venv 3 | max-line-length = 500 4 | 5 | [metadata] 6 | description-file = README.md 7 | 8 | [coverage:run] 9 | branch = True 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from codecs import open 2 | from os import path 3 | 4 | from setuptools import find_packages, setup 5 | 6 | here = path.abspath(path.dirname(__file__)) 7 | 8 | # Get the long description from the README file 9 | with open(path.join(here, "README.md"), encoding="utf-8") as f: 10 | long_description = f.read() 11 | 12 | setup( 13 | name="instabot", 14 | version="0.117.0", 15 | description="Instagram bot scripts for promotion and API python wrapper.", 16 | long_description=long_description, 17 | author="Daniil Okhlopkov, Evgeny Kemerov", 18 | author_email="danokhlopkov@gmail.com, eskemerov@gmail.com", 19 | license="Apache Software License 2.0", 20 | url="https://github.com/instagrambot/instabot", 21 | keywords=["instagram", "bot", "api", "wrapper"], 22 | install_requires=[ 23 | "certifi>=2019.11.28", 24 | "chardet>=3.0.4", 25 | "future>=0.18.2", 26 | "huepy>=1.2.1", 27 | "idna>=2.8", 28 | "pysocks>=1.7.1", 29 | "pytz>=2019.3", 30 | "requests>=2.22.0", 31 | "requests-toolbelt>=0.9.1", 32 | "responses>=0.10.9", 33 | "schedule>=0.6.0", 34 | "six>=1.14.0", 35 | "tqdm>=4.41.1", 36 | "urllib3>=1.25.7", 37 | "mock>=3.0.5", 38 | "moviepy>=1.0.1", 39 | "Pillow>=6.2.2", 40 | "pytest>=4.6.9", 41 | "pycryptodome>=3.9.7", 42 | ], 43 | classifiers=[ 44 | # How mature is this project? Common values are 45 | "Development Status :: 5 - Production/Stable", 46 | # Indicate who your project is intended for 47 | "Intended Audience :: Information Technology", 48 | # Pick your license as you wish (should match "license" above) 49 | "License :: OSI Approved :: Apache Software License", 50 | # Specify the Python versions you support here. In particular, ensure 51 | # that you indicate whether you support Python 2, Python 3 or both. 52 | "Programming Language :: Python :: 2", 53 | "Programming Language :: Python :: 2.7", 54 | "Programming Language :: Python :: 3", 55 | "Programming Language :: Python :: 3.5", 56 | "Programming Language :: Python :: 3.6", 57 | "Programming Language :: Python :: 3.7", 58 | ], 59 | packages=find_packages(), 60 | ) 61 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohld/igbot/d68b55015322898c3842c92dcc5989581043252e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_bot.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | 5 | from instabot import Bot 6 | 7 | try: 8 | from unittest.mock import Mock, patch 9 | except ImportError: 10 | from mock import Mock, patch 11 | 12 | 13 | class TestBot: 14 | def setup(self): 15 | self.USER_ID = 1234567 16 | self.USERNAME = "test_username" 17 | self.PASSWORD = "test_password" 18 | self.FULLNAME = "test_full_name" 19 | self.TOKEN = "abcdef123456" 20 | self.bot = Bot( 21 | max_likes_per_day=1000, 22 | max_unlikes_per_day=1000, 23 | max_follows_per_day=350, 24 | max_unfollows_per_day=350, 25 | max_comments_per_day=100, 26 | max_blocks_per_day=100, 27 | max_unblocks_per_day=100, 28 | max_likes_to_like=100, 29 | min_likes_to_like=20, 30 | max_messages_per_day=300, 31 | like_delay=10, 32 | unlike_delay=10, 33 | follow_delay=30, 34 | unfollow_delay=30, 35 | comment_delay=60, 36 | block_delay=30, 37 | unblock_delay=30, 38 | message_delay=60, 39 | blocked_actions_sleep_delay=300, 40 | save_logfile=False, 41 | ) 42 | self.prepare_api(self.bot) 43 | self.bot.reset_counters() 44 | self.bot.reset_cache() 45 | 46 | def prepare_api(self, bot): 47 | bot.api.is_logged_in = True 48 | bot.api.session = requests.Session() 49 | 50 | cookies = Mock() 51 | cookies.return_value = {"csrftoken": self.TOKEN, "ds_user_id": self.USER_ID} 52 | bot.api.session.cookies.get_dict = cookies 53 | bot.api.set_user(self.USERNAME, self.PASSWORD) 54 | 55 | 56 | class TestBotAPI(TestBot): 57 | @patch("instabot.API.load_uuid_and_cookie") 58 | def test_login(self, load_cookie_mock): 59 | self.bot = Bot(save_logfile=False) 60 | 61 | load_cookie_mock.side_effect = Exception() 62 | 63 | def mockreturn(*args, **kwargs): 64 | r = Mock() 65 | r.status_code = 200 66 | r.text = '{"status": "ok"}' 67 | return r 68 | 69 | def mockreturn_login(*args, **kwargs): 70 | r = Mock() 71 | r.status_code = 200 72 | r.text = json.dumps( 73 | { 74 | "logged_in_user": { 75 | "pk": self.USER_ID, 76 | "username": self.USERNAME, 77 | "full_name": self.FULLNAME, 78 | }, 79 | "status": "ok", 80 | } 81 | ) 82 | return r 83 | 84 | with patch("requests.Session") as Session: 85 | instance = Session.return_value 86 | instance.get.return_value = mockreturn() 87 | instance.post.return_value = mockreturn_login() 88 | instance.cookies = requests.cookies.RequestsCookieJar() 89 | instance.cookies.update( 90 | {"csrftoken": self.TOKEN, "ds_user_id": self.USER_ID} 91 | ) 92 | 93 | # this should be fixed acording to the new end_points 94 | 95 | # assert self.bot.api.login( 96 | # username=self.USERNAME, 97 | # password=self.PASSWORD, 98 | # use_cookie=False, 99 | # use_uuid=False, 100 | # set_device=False, 101 | # ) 102 | 103 | # assert self.bot.api.username == self.USERNAME 104 | # assert self.bot.user_id == self.USER_ID 105 | # assert self.bot.api.is_logged_in 106 | # assert self.bot.api.uuid 107 | # assert self.bot.api.token 108 | 109 | def test_generate_uuid(self): 110 | from uuid import UUID 111 | 112 | generated_uuid = self.bot.api.generate_UUID(True) 113 | 114 | assert isinstance(UUID(generated_uuid), UUID) 115 | assert UUID(generated_uuid).hex == generated_uuid.replace("-", "") 116 | 117 | def test_set_user(self): 118 | test_username = "abcdef" 119 | test_password = "passwordabc" 120 | self.bot.api.set_user(test_username, test_password) 121 | 122 | assert self.bot.api.username == test_username 123 | assert self.bot.api.password == test_password 124 | assert hasattr(self.bot.api, "uuid") 125 | 126 | def test_reset_counters(self): 127 | keys = [ 128 | "liked", 129 | "unliked", 130 | "followed", 131 | "messages", 132 | "unfollowed", 133 | "commented", 134 | "blocked", 135 | "unblocked", 136 | ] 137 | for key in keys: 138 | self.bot.total[key] = 1 139 | assert self.bot.total[key] == 1 140 | self.bot.reset_counters() 141 | for key in keys: 142 | assert self.bot.total[key] == 0 143 | -------------------------------------------------------------------------------- /tests/test_bot_comment.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import responses 3 | 4 | from instabot.api.config import API_URL 5 | 6 | from .test_bot import TestBot 7 | from .test_variables import TEST_CAPTION_ITEM, TEST_COMMENT_ITEM 8 | 9 | try: 10 | from unittest.mock import patch 11 | except ImportError: 12 | from mock import patch 13 | 14 | 15 | class TestBotGet(TestBot): 16 | @responses.activate 17 | @pytest.mark.parametrize( 18 | "blocked_actions_protection,blocked_actions", 19 | [(True, True), (True, False), (False, True), (False, False)], 20 | ) 21 | @patch("time.sleep", return_value=None) 22 | def test_comment_feedback( 23 | self, patched_time_sleep, blocked_actions_protection, blocked_actions 24 | ): 25 | self.bot.blocked_actions_protection = blocked_actions_protection 26 | self.bot.blocked_actions["comments"] = blocked_actions 27 | media_id = 1234567890 28 | comment_txt = "Yeah great!" 29 | 30 | TEST_COMMENT_ITEM["user"]["pk"] = self.bot.user_id + 1 31 | 32 | results = 3 33 | response_data = { 34 | "caption": TEST_CAPTION_ITEM, 35 | "caption_is_edited": False, 36 | "comment_count": results, 37 | "comment_likes_enabled": True, 38 | "comments": [TEST_COMMENT_ITEM for _ in range(results)], 39 | "has_more_comments": False, 40 | "has_more_headload_comments": False, 41 | "media_header_display": "none", 42 | "preview_comments": [], 43 | "status": "ok", 44 | } 45 | responses.add( 46 | responses.GET, 47 | "{api_url}media/{media_id}/comments/?".format( 48 | api_url=API_URL, media_id=media_id 49 | ), 50 | json=response_data, 51 | status=200, 52 | ) 53 | 54 | response_data = { 55 | "message": "feedback_required", 56 | "spam": True, 57 | "feedback_title": "Sorry, this feature isn't available right now", 58 | "feedback_message": "An error occurred while processing this " 59 | + "request. Please try again later. We restrict certain content " 60 | + "and actions to protect our community. Tell us if you think we " 61 | + "made a mistake.", 62 | "feedback_url": "repute/report_problem/instagram_comment/", 63 | "feedback_appeal_label": "Report problem", 64 | "feedback_ignore_label": "OK", 65 | "feedback_action": "report_problem", 66 | "status": "fail", 67 | } 68 | responses.add( 69 | responses.POST, 70 | "{api_url}media/{media_id}/comment/".format( 71 | api_url=API_URL, media_id=media_id 72 | ), 73 | json=response_data, 74 | status=400, 75 | ) 76 | 77 | assert not self.bot.comment(media_id, comment_txt) 78 | 79 | @responses.activate 80 | @pytest.mark.parametrize( 81 | "blocked_actions_protection,blocked_actions", [(True, False), (False, False)] 82 | ) 83 | @patch("time.sleep", return_value=None) 84 | def test_comment( 85 | self, patched_time_sleep, blocked_actions_protection, blocked_actions 86 | ): 87 | self.bot.blocked_actions_protection = blocked_actions_protection 88 | self.bot.blocked_actions["comments"] = blocked_actions 89 | media_id = 1234567890 90 | comment_txt = "Yeah great!" 91 | 92 | TEST_COMMENT_ITEM["user"]["pk"] = self.bot.user_id + 1 93 | 94 | results = 3 95 | response_data = { 96 | "caption": TEST_CAPTION_ITEM, 97 | "caption_is_edited": False, 98 | "comment_count": results, 99 | "comment_likes_enabled": True, 100 | "comments": [TEST_COMMENT_ITEM for _ in range(results)], 101 | "has_more_comments": False, 102 | "has_more_headload_comments": False, 103 | "media_header_display": "none", 104 | "preview_comments": [], 105 | "status": "ok", 106 | } 107 | responses.add( 108 | responses.GET, 109 | "{api_url}media/{media_id}/comments/?".format( 110 | api_url=API_URL, media_id=media_id 111 | ), 112 | json=response_data, 113 | status=200, 114 | ) 115 | 116 | response_data = {"status": "ok"} 117 | responses.add( 118 | responses.POST, 119 | "{api_url}media/{media_id}/comment/".format( 120 | api_url=API_URL, media_id=media_id 121 | ), 122 | json=response_data, 123 | status=200, 124 | ) 125 | 126 | assert self.bot.comment(media_id, comment_txt) 127 | -------------------------------------------------------------------------------- /tests/test_bot_filter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import responses 3 | 4 | from instabot.api.config import API_URL 5 | 6 | from .test_bot import TestBot 7 | from .test_variables import TEST_USERNAME_INFO_ITEM 8 | 9 | try: 10 | from unittest.mock import patch 11 | except ImportError: 12 | from mock import patch 13 | 14 | 15 | class TestBotFilter(TestBot): 16 | @pytest.mark.parametrize( 17 | "filter_users,filter_business_accounts," + "filter_verified_accounts,expected", 18 | [ 19 | (False, False, False, True), 20 | (True, False, False, True), 21 | (True, True, False, False), 22 | (True, False, True, False), 23 | (True, True, True, False), 24 | ], 25 | ) 26 | @responses.activate 27 | @patch("time.sleep", return_value=None) 28 | def test_check_user( 29 | self, 30 | patched_time_sleep, 31 | filter_users, 32 | filter_business_accounts, 33 | filter_verified_accounts, 34 | expected, 35 | ): 36 | self.bot.filter_users = filter_users 37 | self.bot.filter_business_accounts = filter_business_accounts 38 | self.bot.filter_verified_accounts = filter_verified_accounts 39 | self.bot._following = [1] 40 | 41 | user_id = TEST_USERNAME_INFO_ITEM["pk"] 42 | TEST_USERNAME_INFO_ITEM["is_verified"] = True 43 | TEST_USERNAME_INFO_ITEM["is_business"] = True 44 | 45 | response_data = {"status": "ok", "user": TEST_USERNAME_INFO_ITEM} 46 | responses.add( 47 | responses.GET, 48 | "{api_url}users/{user_id}/info/".format(api_url=API_URL, user_id=user_id), 49 | status=200, 50 | json=response_data, 51 | ) 52 | 53 | result = self.bot.check_user(user_id) 54 | 55 | assert result == expected 56 | -------------------------------------------------------------------------------- /tests/test_bot_support.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import os 5 | import sys 6 | 7 | import pytest 8 | 9 | from .test_bot import TestBot 10 | 11 | 12 | class TestBotSupport(TestBot): 13 | @pytest.mark.parametrize( 14 | "url,result", 15 | [ 16 | ("https://google.com", ["https://google.com"]), 17 | ("google.com", ["google.com"]), 18 | ("google.com/search?q=instabot", ["google.com/search?q=instabot"]), 19 | ( 20 | "https://google.com/search?q=instabot", 21 | ["https://google.com/search?q=instabot"], 22 | ), 23 | ("мвд.рф", ["мвд.рф"]), 24 | ("https://мвд.рф", ["https://мвд.рф"]), 25 | ("http://мвд.рф/news/", ["http://мвд.рф/news/"]), 26 | ( 27 | "hello, google.com/search?q=test and bing.com", 28 | ["google.com/search?q=test", "bing.com"], 29 | ), 30 | ], 31 | ) 32 | def test_extract_urls(self, url, result): 33 | assert self.bot.extract_urls(url) == result 34 | 35 | def test_check_if_file_exist(self): 36 | test_file = open("test", "w") 37 | 38 | assert self.bot.check_if_file_exists("test") 39 | 40 | test_file.close() 41 | os.remove("test") 42 | 43 | def test_check_if_file_exist_fail(self): 44 | assert not self.bot.check_if_file_exists("test") 45 | 46 | @pytest.mark.parametrize( 47 | "verbosity,text,result", [(True, "test", "test"), (False, "test", "")] 48 | ) 49 | def test_console_print(self, verbosity, text, result): 50 | self.bot.verbosity = verbosity 51 | try: 52 | if sys.version_info > (3,): 53 | from io import StringIO 54 | else: 55 | from StringIO import StringIO 56 | saved_stdout = sys.stdout 57 | out = StringIO() 58 | sys.stdout = out 59 | 60 | self.bot.console_print(text) 61 | 62 | output = out.getvalue().strip() 63 | assert output == result 64 | finally: 65 | sys.stdout = saved_stdout 66 | -------------------------------------------------------------------------------- /tests/test_bot_unlike.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import responses 3 | 4 | from instabot.api.config import API_URL 5 | 6 | from .test_bot import TestBot 7 | from .test_variables import ( 8 | TEST_CAPTION_ITEM, 9 | TEST_COMMENT_ITEM, 10 | TEST_PHOTO_ITEM, 11 | TEST_USERNAME_INFO_ITEM, 12 | ) 13 | 14 | try: 15 | from unittest.mock import patch 16 | except ImportError: 17 | from mock import patch 18 | 19 | 20 | class TestBotFilter(TestBot): 21 | @responses.activate 22 | @pytest.mark.parametrize( 23 | "media_id,total,max_per_day", 24 | [ 25 | [111111, 1, 2], 26 | [111111, 2, 2], 27 | [111111, 3, 2], 28 | ["111111", 1, 2], 29 | ["111111", 2, 2], 30 | ["111111", 3, 2], 31 | ], 32 | ) 33 | @patch("time.sleep", return_value=None) 34 | def test_unlike(self, patched_time_sleep, media_id, total, max_per_day): 35 | self.bot.total["unlikes"] = total 36 | self.bot.max_per_day["unlikes"] = max_per_day 37 | responses.add( 38 | responses.POST, 39 | "{api_url}media/{media_id}/unlike/".format( 40 | api_url=API_URL, media_id=media_id 41 | ), 42 | json="{'status': 'ok'}", 43 | status=200, 44 | ) 45 | _r = self.bot.unlike(media_id) 46 | test_r = _r if total < max_per_day else not _r 47 | test_unliked = ( 48 | self.bot.total["unlikes"] == total + 1 49 | if total < max_per_day 50 | else self.bot.total["unlikes"] == total 51 | ) 52 | assert test_r and test_unliked 53 | 54 | @responses.activate 55 | @pytest.mark.parametrize("comment_id", [111111, "111111"]) 56 | @patch("time.sleep", return_value=None) 57 | def test_unlike_comment(self, patched_time_sleep, comment_id): 58 | responses.add( 59 | responses.POST, 60 | "{api_url}media/{comment_id}/comment_unlike/".format( 61 | api_url=API_URL, comment_id=comment_id 62 | ), 63 | json="{'status': 'ok'}", 64 | status=200, 65 | ) 66 | _r = self.bot.unlike_comment(comment_id) 67 | assert _r 68 | 69 | @responses.activate 70 | @pytest.mark.parametrize( 71 | "media_ids,total,max_per_day", 72 | [ 73 | [[111111, 222222], 1, 3], 74 | [[111111, 222222], 2, 3], 75 | [[111111, 222222], 3, 3], 76 | [["111111", "222222"], 1, 3], 77 | [["111111", "222222"], 2, 3], 78 | [["111111", "222222"], 3, 3], 79 | ], 80 | ) 81 | @patch("time.sleep", return_value=None) 82 | def test_unlike_medias(self, patched_time_sleep, media_ids, total, max_per_day): 83 | self.bot.total["unlikes"] = total 84 | self.bot.max_per_day["unlikes"] = max_per_day 85 | for media_id in media_ids: 86 | responses.add( 87 | responses.POST, 88 | "{api_url}media/{media_id}/unlike/".format( 89 | api_url=API_URL, media_id=media_id 90 | ), 91 | json="{'status': 'ok'}", 92 | status=200, 93 | ) 94 | broken_items = self.bot.unlike_medias(media_ids) 95 | test_unliked = self.bot.total["unlikes"] == max_per_day 96 | test_broken = len(broken_items) == len(media_ids) - (max_per_day - total) 97 | assert test_unliked and test_broken 98 | 99 | @responses.activate 100 | @patch("time.sleep", return_value=None) 101 | def test_unlike_media_comments(self, patched_time_sleep): 102 | my_test_comment_items = [] 103 | results = 5 104 | for i in range(results): 105 | my_test_comment_items.append(TEST_COMMENT_ITEM.copy()) 106 | my_test_comment_items[i]["pk"] = TEST_COMMENT_ITEM["pk"] + i 107 | if i % 2: 108 | my_test_comment_items[i]["has_liked_comment"] = False 109 | else: 110 | my_test_comment_items[i]["has_liked_comment"] = True 111 | media_id = 1234567890 112 | response_data = { 113 | "caption": TEST_CAPTION_ITEM, 114 | "caption_is_edited": False, 115 | "comment_count": results, 116 | "comment_likes_enabled": True, 117 | "comments": my_test_comment_items, 118 | "has_more_comments": False, 119 | "has_more_headload_comments": False, 120 | "media_header_display": "none", 121 | "preview_comments": [], 122 | "status": "ok", 123 | } 124 | responses.add( 125 | responses.GET, 126 | "{api_url}media/{media_id}/comments/?".format( 127 | api_url=API_URL, media_id=media_id 128 | ), 129 | json=response_data, 130 | status=200, 131 | ) 132 | for my_test_comment_item in my_test_comment_items: 133 | responses.add( 134 | responses.POST, 135 | "{api_url}media/{comment_id}/comment_unlike/".format( 136 | api_url=API_URL, comment_id=my_test_comment_item["pk"] 137 | ), 138 | json="{'status': 'ok'}", 139 | status=200, 140 | ) 141 | broken_items = self.bot.unlike_media_comments(media_id) 142 | assert broken_items == [] 143 | 144 | @responses.activate 145 | @patch("time.sleep", return_value=None) 146 | def test_unlike_user(self, patched_time_sleep): 147 | unliked_at_start = self.bot.total["unlikes"] 148 | user_id = 1234567890 149 | response_data = {"status": "ok", "user": TEST_USERNAME_INFO_ITEM} 150 | responses.add( 151 | responses.GET, 152 | "{api_url}users/{username}/usernameinfo/".format( 153 | api_url=API_URL, username=user_id 154 | ), 155 | status=200, 156 | json=response_data, 157 | ) 158 | my_test_photo_items = [] 159 | results = 5 160 | for i in range(results): 161 | my_test_photo_items.append(TEST_PHOTO_ITEM.copy()) 162 | my_test_photo_items[i]["pk"] = TEST_PHOTO_ITEM["id"] + i 163 | if i % 2: 164 | my_test_photo_items[i]["has_liked"] = False 165 | else: 166 | my_test_photo_items[i]["has_liked"] = True 167 | response_data = { 168 | "auto_load_more_enabled": True, 169 | "num_results": results, 170 | "status": "ok", 171 | "more_available": False, 172 | "items": my_test_photo_items, 173 | } 174 | responses.add( 175 | responses.GET, 176 | "{api_url}feed/user/{user_id}/".format(api_url=API_URL, user_id=user_id), 177 | json=response_data, 178 | status=200, 179 | ) 180 | for my_test_photo_item in my_test_photo_items: 181 | responses.add( 182 | responses.POST, 183 | "{api_url}media/{media_id}/unlike/".format( 184 | api_url=API_URL, media_id=my_test_photo_item["id"] 185 | ), 186 | json="{'status': 'ok'}", 187 | status=200, 188 | ) 189 | broken_items = self.bot.unlike_user(user_id) 190 | test_unliked = self.bot.total["unlikes"] == unliked_at_start + len( 191 | my_test_photo_items 192 | ) 193 | test_broken = broken_items == [] 194 | assert test_broken and test_unliked 195 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{27,34,35,36,37,38,py,py3}, 3 | skip_missing_interpreters = true 4 | 5 | [testenv] 6 | # Coverage doesn't work on PyPy 7 | deps = 8 | pytest 9 | pytest-mock 10 | py{27,34,35,36,37,38}: coverage_pth 11 | setenv = 12 | COVERAGE_PROCESS_START=.coveragerc 13 | commands = {posargs:py.test} 14 | 15 | [flake8] 16 | ignore = D203 17 | 18 | [coverage:run] 19 | branch = True 20 | source = instabot 21 | parallel = True 22 | -------------------------------------------------------------------------------- /tox_install_command.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pip install pipenv 4 | pipenv install --dev --skip-lock 5 | --------------------------------------------------------------------------------