Skip to content

Commit

Permalink
Merge remote-tracking branch 'github/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
diezo committed Mar 9, 2024
2 parents d655469 + 882d1a1 commit 03698f6
Show file tree
Hide file tree
Showing 8 changed files with 817 additions and 101 deletions.
79 changes: 79 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: "Check Pull Request"

on:
pull_request:
branches:
- main
- master

jobs:
lint:
name: "Checking format"
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install pipenv
run: |
python -m pip install --upgrade pipenv wheel
- id: cache-pipenv
uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}
- name: Install dependencies
if: steps.cache-pipenv.outputs.cache-hit != 'true'
run: |
pipenv install --dev
- name: Run lint
run: |
pipenv run format || (echo "::error file={name},line={line},endLine={endLine},title={title}::{message}" && exit 1)
test:
name: "Running tests"
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install pipenv
run: |
python -m pip install --upgrade pipenv wheel
- id: cache-pipenv
uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}
- name: Install dependencies
if: steps.cache-pipenv.outputs.cache-hit != 'true'
run: |
pipenv install --dev
- name: Run test suite
run: |
pipenv run coverage
- name: Get Cover
uses: orgoro/[email protected]
with:
coverageFile: coverage.xml
token: ${{ secrets.PR_GITHUB_TOKEN }}

install-test:
name: "Install"
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install project
run: |
python -m pip install ./
5 changes: 4 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ name = "pypi"
[packages]
requests = "*"
moviepy = "*"
pillow = "*"
cryptography = "*"
pyotp = "*"
ntplib = "*"
pyquery = "*"

[dev-packages]
autopep8 = "*"
coverage = "*"

[requires]
python_version = "3.10"

[scripts]
test = "python -m unittest discover -s test_ensta/ -t ."
format = "autopep8 --in-place --recursive ."
format = "autopep8 --exit-code ."
coverage = "sh -c 'coverage run -m unittest discover -s test_ensta/ -t . && coverage xml'"
524 changes: 524 additions & 0 deletions Pipfile.lock

Large diffs are not rendered by default.

33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ Both **authenticated** and **anonymous** requests are supported.

[<img style="margin-top: 10px" src="https://raw.githubusercontent.com/diezo/Ensta/master/assets/coffee.svg" width="180"/>](https://buymeacoffee.com/sonii)

## <img src="https://raw.githubusercontent.com/diezo/Ensta/master/assets/colorful-instagram-icon-vintage-style-art-vector-illustration_836950-30.jpg" width="23"> Account Creator
Download an Instagram [**Account Creator**](https://sonii.gumroad.com/l/account-creator/EARLY20) written in Python.

- Auto-generates **DuckDuckGo Private Email Addresses**.
- Auto-fetches OTP from **ProtonMail Inbox**.
- Auto-updates Profile Picture to an **AI-Generated Human Face**.
- Sets a random **AI-Generated Biography** on account creation.

Creator should only be used for legitimate purposes. It's strictly not for spam.

## Installation
Python [**3.10**](https://www.python.org/downloads/) or later is required.

Expand Down Expand Up @@ -134,27 +144,27 @@ from ensta import Host

host = Host(username, password)

upload = host.get_upload_id("Picture.jpg")
upload = host.upload_image("Picture.jpg")

host.upload_photo(upload, caption="Travelling 🌆")
host.pub_photo(upload, caption="Travelling 🌆")
```

</details>

<details>

<summary>Upload Multiple Photos (Single Post)</summary><br>
<summary>Upload Multiple Medias (Single Post)</summary><br>

```python
from ensta import Host

host = Host(username, password)

upload1 = host.get_upload_id("First.jpg")
upload2 = host.get_upload_id("Second.jpg")
upload3 = host.get_upload_id("Third.jpg")
upload1 = host.upload_image("First.jpg")
upload2 = host.upload_image("Second.jpg")
upload3 = host.upload_video_for_carousel("Video.mp4", thumbnail="Thumbnail.jpg")

host.upload_photos([upload1, upload2, upload3], caption="Travelling 🌆")
host.pub_carousel([upload1, upload2, upload3], caption="Travelling 🌆")
```

</details>
Expand All @@ -168,9 +178,10 @@ from ensta import Host

host = Host(username, password)

host.upload_reel(
video_path="Video.mp4",
thumbnail_path="Thumbnail.jpg",
video_id = host.upload_video_for_reel("Video.mp4", thumbnail="Thumbnail.jpg")

host.pub_reel(
video_id,
caption="Enjoying the winter! ⛄"
)
```
Expand Down Expand Up @@ -521,7 +532,7 @@ Ask questions, discuss upcoming features and meet other developers.
## Support Me
Wish to support this project? Please consider buying me a coffee here:

[<img src="https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg" width="170"/>](https://buymeacoffee.com/sonii)
[<img src="https://raw.githubusercontent.com/diezo/Ensta/master/assets/coffee.svg" width="170"/>](https://buymeacoffee.com/sonii)

## Contributors
[![Contributors](https://contrib.rocks/image?anon=1&repo=diezo/ensta&)](https://github.com/diezo/ensta/graphs/contributors)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 96 additions & 3 deletions ensta/Guest.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,100 @@ def posts(self, username: str, count: int = 0, __session__: requests.Session | N
yield None
raise NetworkError("HTTP Response is not a valid JSON.")

@staticmethod
def __process_post_data(data: dict) -> Post:
def reels(
self,
uid: str | int,
count: int = 0,
__session__: requests.Session | None = None,
) -> Generator[Post, None, None]:
"""
Generates a list of target's reels of specified size.
:param uid: Target's user ID
:param count: Amount of reels to fetch
:param __session__: (Optional) Custom request session object
:return: Generator which yields each reel's data
"""

uid = str(uid).replace(" ", "")

request_headers = {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9",
"sec-ch-prefers-color-scheme": self.preferred_color_scheme,
"sec-ch-ua": self.user_agent,
"sec-ch-ua-full-version-list": self.user_agent,
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-ch-ua-platform-version": '"15.0.0"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"viewport-width": "1475",
"x-asbd-id": "129477",
"x-csrftoken": self.csrf_token,
"x-ig-app-id": self.insta_app_id,
"x-ig-www-claim": self.x_ig_www_claim,
"x-requested-with": "XMLHttpRequest",
"Referer": "https://www.instagram.com/",
"Referrer-Policy": "strict-origin-when-cross-origin",
}

generated_count = 0
max_id = None

while True:
try:
session: requests.Session = __session__
if __session__ is None:
session: requests.Session = self.request_session

http_response = session.post(
"https://www.instagram.com/api/v1/clips/user/",
data={
"target_user_id": user_id,
"page_size": "50",
"include_feed_video": "true",
"max_id": max_id,
},
headers=request_headers,
)

response_json = http_response.json()

if "status" not in response_json or "items" not in response_json:
yield None
raise NetworkError(
"HTTP response doesn't include 'status' or 'items' node."
)

if response_json["status"] != "ok":
yield None

if response_json.get("message", "") == "checkpoint_required":
raise RateLimitedError(
"IP Temporarily Flagged: Wait for some time, or switch "
"to a different WiFi Network, or use proxies."
)

raise NetworkError("Request failed.")

for each_item in response_json["items"]:
if generated_count < count or count == 0:
yield self.__process_post_data(each_item["media"], reel=True)
generated_count += 1

if (generated_count < count or count == 0) and response_json.get(
"paging_info", {}
).get("more_available", False):
max_id = response_json["paging_info"]["max_id"]
else:
return None
except JSONDecodeError:
yield None
raise NetworkError("HTTP Response is not a valid JSON.")

@staticmethod
def __process_post_data(data: dict, reel: bool = False) -> Post:
caption: dict | None = data.get("caption", None)

caption_text = ""
Expand Down Expand Up @@ -353,7 +444,9 @@ def __process_post_data(data: dict) -> Post:
)

return Post(
share_url=f"https://www.instagram.com/p/{data.get('code', '')}",
share_url=f"https://www.instagram.com/reel/{data.get('code', '')}"
if reel
else f"https://www.instagram.com/p/{data.get('code', '')}/",
taken_at=data.get("taken_at", 0),
post_id=data.get("pk", ""),
media_type=data.get("media_type", 0),
Expand Down
Loading

0 comments on commit 03698f6

Please sign in to comment.