Merge pull request #3 from Horhik/v0.3

Merge v0.3 into master
This commit is contained in:
George 2021-11-22 16:37:48 +03:00 committed by GitHub
commit 85a3191426
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 160 additions and 37 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
*~ *~
already_posted.txt already_posted.txt
src/__pycache__/ src/__pycache__/
.venv
.env.sh

0
Dockerfile Normal file → Executable file
View File

72
Docs.md Normal file
View File

@ -0,0 +1,72 @@
# Instagram2Fedi Docs 📜
## How to use
You can use Instagram2Fedi via docker or just like a python script
### With Docker 🐋
Specify your variables in `./env.sh` and then run `./run.sh`
You can modify `docker run` arguments in `./run.sh`
### Just a python script 🐍
Run `pip3 install -r requirements.txt` and then run `./insta2fedi`.
Specify your arguments. You should use `--use-docker 0`.
For example:
``` bash
./insta2fedi --use-docker false --instagram-user <instagram username> --instance <instance domain> --token <OAuth token> --check-interval 10 --post-interval 10 --use-mastodon 4
# will check for new post each 10 seconds
```
## Command line arguments 🖥
`--use-mastodon` - set not positive number (`0`, `-1`...) if your instance don't have max image count limit.
For example, default maximum photo count in mastodon is `4`
---
`--instance` - Your instance url
---
`--instagram-user` - Your instagram user name.
---
`--token` - Your OAuth token
---
`--check-interval` - Interval in seconds how often to check for new posts
---
`--post-interval` - Interval in seconds between posting new fetched posts.
If theres more than one new post, sets with which time interval should it post them
---
`--fetch-count` - How many new posts to select
---
`--use-docker` - If you're running it via docker container, set to `1` or `True`
## Default values ⚙
Default values are:
``` bash
--instance None
--instagram-user None
--token None
--check-interval 3600
--post-interval 3600
--fetch-count 10
--use-mastodon 4
--use-docker True
```

0
LICENSE Normal file → Executable file
View File

18
README.md Normal file → Executable file
View File

@ -1,22 +1,24 @@
# Instagram2Fedi <span><img width="50px" src="https://upload.wikimedia.org/wikipedia/commons/9/93/Fediverse_logo_proposal.svg"></span> # Instagram2Fedi <span><img width="50px" src="https://upload.wikimedia.org/wikipedia/commons/9/93/Fediverse_logo_proposal.svg"></span>
Simple python 🐍 script for crossposting from instagram to Mastodon/Pixelfed Simple tool for crossposting posts from instagram to Mastodon/Pixelfed.
## Installing ## Using without docker
See [Docs.md](./Docs.md)
Just clone repo, build a docker container and run it ## Using with Docker
Just clone repo, specify variables and run it.
You can write all needed variables in `./env.sh` and then do `source ./run.sh`
``` bash ``` bash
git clone https://github.com/horhik/instagram2fedi git clone https://github.com/horhik/instagram2fedi
cd instagram2fedi cd instagram2fedi
docker build -t $YOUR_CONTAINER_NAME . nano ./env.sh
docker container run -it -d -v $(pwd):/app $YOUR_CONTAINER_NAME $I2M_INSTAGRAM_USER $I2M_INSTANCE $I2M_TOKEN source ./run.sh
``` ```
You can write all needed variables in `./env.sh` and then do `source ./run.sh`
![screenshot](./img.png)
![image](https://user-images.githubusercontent.com/46262811/131577640-a3103ff2-af37-422d-96f1-60f1acdef939.png)

7
env.sh
View File

@ -1,8 +1,13 @@
#!/bin/sh #!b/in/sh
# Required
export YOUR_CONTAINER_NAME= export YOUR_CONTAINER_NAME=
export I2M_INSTAGRAM_USER= export I2M_INSTAGRAM_USER=
export I2M_INSTANCE= export I2M_INSTANCE=
export I2M_TOKEN= export I2M_TOKEN=
export I2M_CHECK_INTERVAL=3600 #1 hour
export I2M_POST_INTERVAL=3600 #1 hour
export I2M_USE_MASTODON=4 #max carousel length is 4, if there's no limit set to -1
export I2M_FETCH_COUNT=5 # how many instagram posts to fetch per check_interval

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

2
insta2fedi Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
python3 src/main.py $@

3
requirements.txt Executable file
View File

@ -0,0 +1,3 @@
Mastodon.py==1.5.1
colorama==0.4.4
instaloader==4.8.1

2
run.sh
View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
source ./env.sh source ./env.sh
docker build -t $YOUR_CONTAINER_NAME .; docker container run -it -v $(pwd):/app $YOUR_CONTAINER_NAME --instagram-user $I2M_INSTAGRAM_USER --instance $I2M_INSTANCE --token $I2M_TOKEN docker build -t $YOUR_CONTAINER_NAME .; docker container run -it -v $(pwd):/app $YOUR_CONTAINER_NAME --use-docker 1 --instagram-user $I2M_INSTAGRAM_USER --instance $I2M_INSTANCE --token $I2M_TOKEN --check-interval $I2M_CHECK_INTERVAL --post-interval $I2M_POST_INTERVAL --fetch-count $I2M_FETCH_COUNT --use-mastodon $I2M_USE_MASTODON

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import datetime
from colorama import Fore, Back, Style from colorama import Fore, Back, Style
def process_arguments(args, defaults): def process_arguments(args, defaults):
count = 1 count = 1
@ -11,28 +13,24 @@ def process_arguments(args, defaults):
defaults["token"] = args[count + 1] defaults["token"] = args[count + 1]
elif (args[count] == "--check-interval"): elif (args[count] == "--check-interval"):
defaults["check-interval"] = args[count + 1] defaults["check-interval"] = int(args[count + 1])
elif (args[count] == "--post-interval"): elif (args[count] == "--post-interval"):
defaults["post-interval"] = args[count + 1] defaults["post-interval"] = int(args[count + 1])
elif (args[count] == "--fetch-count"): elif (args[count] == "--fetch-count"):
defaults["fetch-count"] = args[count + 1] defaults["fetch-count"] = int(args[count + 1])
elif (args[count] == "--using-mastodon"): elif (args[count] == "--use-mastodon"):
defaults["carousel-limit"] = int(args[count + 1]) defaults["carousel-limit"] = int(args[count + 1])
elif (args[count] == "--use-docker"):
defaults["use-docker"] = args[count + 1]
else: else:
print(Fore.RED + '❗ -> Wrong Argument Name!...') print(Fore.RED + '❗ -> Wrong Argument Name!...')
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
count +=2 count +=2
return defaults return defaults
#fuck this shit im out''
#teenagers scare the living shit out of me
#deeeespaaaacito quero esperanto de despacito
#хорошо всё будет хорошо
#и камнем вниииз
#u kinda smell *smif* like a BAKA
#Yeren Yegaaaaaaa!!!!!!!!!

View File

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from colorama import Fore, Back, Style from colorama import Fore, Back, Style
import datetime
def split_array(arr, size): def split_array(arr, size):
count = len(arr) // size + 1 count = len(arr) // size + 1
@ -14,9 +16,10 @@ def try_to_get_carousel(array, post):
return urls return urls
print(Fore.GREEN + "🎠 > Found carousel!") print(Fore.GREEN + "🎠 > Found carousel!")
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
except Exception as e: except Exception as e:
print(Fore.RED + "🎠💥 > No carousel :( \n", e) print(Fore.RED + "🎠💥 > No carousel :( \n", e)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
return array return array

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import os import os
import sys import sys
import time import time
import datetime
import json import json
from mastodon import Mastodon from mastodon import Mastodon
from colorama import Fore, Back, Style from colorama import Fore, Back, Style
@ -11,9 +13,6 @@ from arguments import process_arguments
from network import get_new_posts from network import get_new_posts
id_filename = "/app/already_posted.txt"
with open(id_filename, "a") as f:
f.write("\n")
print(sys.argv) print(sys.argv)
print("ARGUMENTS") print("ARGUMENTS")
@ -24,13 +23,24 @@ default_settings = {
"check-interval": 3600, "check-interval": 3600,
"post-interval": 3600, "post-interval": 3600,
"fetch-count" : 10, "fetch-count" : 10,
"carousel-limit": 4 "carousel-limit": 4,
"use-docker": True
} }
settings = process_arguments(sys.argv, default_settings) settings = process_arguments(sys.argv, default_settings)
print(settings) print(settings)
agree = [1, True, "true", "True", "yes", "Yes"]
if (agree.count(settings["use-docker"])):
id_filename = "/app/already_posted.txt"
else:
id_filename = "./already_posted.txt"
with open(id_filename, "a") as f:
f.write("\n")
fetched_user = settings["instagram-user"] fetched_user = settings["instagram-user"]
mastodon_instance = settings["instance"] mastodon_instance = settings["instance"]
mastodon_token = settings["token"] mastodon_token = settings["token"]
@ -42,19 +52,16 @@ post_interval = settings["post-interval"]#1m
using_mastodon = settings["carousel-limit"] > 0; using_mastodon = settings["carousel-limit"] > 0;
mastodon_carousel_size = settings["carousel-limit"] mastodon_carousel_size = settings["carousel-limit"]
print(Fore.GREEN + '🚀 > Connecting to Instagram...')
print(Style.RESET_ALL)
L = Instaloader()
profile = Profile.from_username(L.context, fetched_user)
print(Fore.GREEN + '🚀 > Connecting to Mastodon/Pixelfed...') print(Fore.GREEN + '🚀 > Connecting to Mastodon/Pixelfed...')
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
mastodon = Mastodon( mastodon = Mastodon(
access_token = mastodon_token, access_token = mastodon_token,
api_base_url = mastodon_instance api_base_url = mastodon_instance
# api_base_url = 'https://pixelfed.tokyo/' # api_base_url = 'https://pixelfed.tokyo/'
) )
while True: while True:
get_new_posts(mastodon, profile, mastodon_carousel_size, post_limit, id_filename, using_mastodon, mastodon_carousel_size, post_interval, fetched_user) get_new_posts(mastodon, mastodon_carousel_size, post_limit, id_filename, using_mastodon, mastodon_carousel_size, post_interval, fetched_user)
time.sleep(time_interval_sec) time.sleep(time_interval_sec)

View File

@ -1,44 +1,63 @@
# -*- coding: utf-8 -*-
from colorama import Fore, Back, Style from colorama import Fore, Back, Style
import requests import requests
import time import time
import datetime
from already_posted import already_posted, mark_as_posted from already_posted import already_posted, mark_as_posted
from converters import split_array, try_to_get_carousel from converters import split_array, try_to_get_carousel
import hashlib import hashlib
from instaloader import Profile, Instaloader, LatestStamps
def get_instagram_user(user):
L = Instaloader()
print(Fore.GREEN + '🚀 > Connecting to Instagram...')
print(Style.RESET_ALL)
print(datetime.datetime.now())
return Profile.from_username(L.context, user)
def get_image(url): def get_image(url):
try: try:
print(Fore.YELLOW + "🚀 > Downloading Image...", url) print(Fore.YELLOW + "🚀 > Downloading Image...", url)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
response = requests.get(url) response = requests.get(url)
response.raw.decode_content = True response.raw.decode_content = True
print(Fore.GREEN + "✨ > Downloaded!") print(Fore.GREEN + "✨ > Downloaded!")
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
return response.content return response.content
except Exception as e: except Exception as e:
print(Fore.RED + "💥 > Failed to download image. \n", e) print(Fore.RED + "💥 > Failed to download image. \n", e)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
def upload_image_to_mastodon(url, mastodon): def upload_image_to_mastodon(url, mastodon):
try: try:
print(Fore.YELLOW + "🐘 > Uploading Image...") print(Fore.YELLOW + "🐘 > Uploading Image...")
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
media = mastodon.media_post(media_file = get_image(url), mime_type = "image/jpeg") # sending image to mastodon media = mastodon.media_post(media_file = get_image(url), mime_type = "image/jpeg") # sending image to mastodon
print(Fore.GREEN + "✨ > Uploaded!") print(Fore.GREEN + "✨ > Uploaded!")
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
return media["id"] return media["id"]
except Exception as e: except Exception as e:
print(Fore.RED + "💥 > failed to upload image to mastodon. \n", e) print(Fore.RED + "💥 > failed to upload image to mastodon. \n", e)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
def toot(urls, title, mastodon, fetched_user ): def toot(urls, title, mastodon, fetched_user ):
try: try:
print(Fore.YELLOW + "🐘 > Creating Toot...", title) print(Fore.YELLOW + "🐘 > Creating Toot...", title)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
ids = [] ids = []
for url in urls: for url in urls:
ids.append(upload_image_to_mastodon(url, mastodon)) ids.append(upload_image_to_mastodon(url, mastodon))
@ -51,28 +70,38 @@ def toot(urls, title, mastodon, fetched_user ):
except Exception as e: except Exception as e:
print(Fore.RED + "😿 > Failed to create toot \n", e) print(Fore.RED + "😿 > Failed to create toot \n", e)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
def get_new_posts(mastodon, profile, mastodon_carousel_size, post_limit, already_posted_path, using_mastodon, carousel_size, post_interval, fetched_user): def get_new_posts(mastodon, mastodon_carousel_size, post_limit, already_posted_path, using_mastodon, carousel_size, post_interval, fetched_user):
# fetching user profile to get new posts
profile = get_instagram_user(fetched_user)
# get list of all posts
posts = profile.get_posts() posts = profile.get_posts()
stupidcounter = 0 stupidcounter = 0
for post in posts: for post in posts:
stupidcounter += 1
url_arr = try_to_get_carousel([post.url], post) url_arr = try_to_get_carousel([post.url], post)
if stupidcounter <= post_limit: # checking only `post_limit` last posts
if stupidcounter < post_limit:
stupidcounter += 1
if already_posted(str(post.mediaid), already_posted_path): if already_posted(str(post.mediaid), already_posted_path):
print(Fore.YELLOW + "🐘 > Already Posted ", post.url) print(Fore.YELLOW + "🐘 > Already Posted ", post.url)
print(Style.RESET_ALL) print(Style.RESET_ALL)
print(datetime.datetime.now())
continue continue
print("Posting... ", post.url) print("Posting... ", post.url)
print(datetime.datetime.now())
if using_mastodon: if using_mastodon:
urls_arr = split_array(url_arr, carousel_size) urls_arr = split_array(url_arr, carousel_size)
for urls in urls_arr: for urls in urls_arr:
toot(urls, post.caption, mastodon, fetched_user) toot(urls, post.caption, mastodon, fetched_user)
else: else:
toot(url_arr, post.caption, mastodon, fetched_user) toot(url_arr, post.caption, mastodon, fetched_user)
mark_as_posted(str(post.mediaid), already_posted_path) mark_as_posted(str(post.mediaid), already_posted_path)
time.sleep(post_interval) time.sleep(post_interval)
else: else:
return break
print(Fore.GREEN + "✨ > Fetched All")
print(Style.RESET_ALL)
print(datetime.datetime.now())