├── .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 | 
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 | [](https://t.me/instabotproject)
47 | [](https://paypal.me/okhlopkov/10)
48 | 
49 | [](https://badge.fury.io/py/instabot)
50 | [](https://travis-ci.org/instagrambot/instabot)
51 | [](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 |
--------------------------------------------------------------------------------