diff --git a/.gitignore b/.gitignore index 22a6ee2..34415e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *~ already_posted.txt src/__pycache__/ +.venv +.env.sh diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 diff --git a/Docs.md b/Docs.md new file mode 100644 index 0000000..9de05bf --- /dev/null +++ b/Docs.md @@ -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 --instance --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 +``` diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 4938d5a..95f7f40 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ # Instagram2Fedi -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 git clone https://github.com/horhik/instagram2fedi cd instagram2fedi -docker build -t $YOUR_CONTAINER_NAME . -docker container run -it -d -v $(pwd):/app $YOUR_CONTAINER_NAME $I2M_INSTAGRAM_USER $I2M_INSTANCE $I2M_TOKEN +nano ./env.sh +source ./run.sh ``` -You can write all needed variables in `./env.sh` and then do `source ./run.sh` - -![image](https://user-images.githubusercontent.com/46262811/131577640-a3103ff2-af37-422d-96f1-60f1acdef939.png) +![screenshot](./img.png) diff --git a/env.sh b/env.sh index c58af16..14a5e28 100755 --- a/env.sh +++ b/env.sh @@ -1,8 +1,13 @@ -#!/bin/sh +#!b/in/sh +# Required export YOUR_CONTAINER_NAME= export I2M_INSTAGRAM_USER= export I2M_INSTANCE= 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 diff --git a/img.png b/img.png new file mode 100644 index 0000000..4287e60 Binary files /dev/null and b/img.png differ diff --git a/insta2fedi b/insta2fedi new file mode 100755 index 0000000..fb884ba --- /dev/null +++ b/insta2fedi @@ -0,0 +1,2 @@ +#!/bin/sh +python3 src/main.py $@ diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..0616f0c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Mastodon.py==1.5.1 +colorama==0.4.4 +instaloader==4.8.1 diff --git a/run.sh b/run.sh index 37b0e2c..5cd1f0d 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ #!/bin/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 diff --git a/src/arguments.py b/src/arguments.py index 3e2afe1..15a5dbd 100644 --- a/src/arguments.py +++ b/src/arguments.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- +import datetime from colorama import Fore, Back, Style def process_arguments(args, defaults): count = 1 @@ -11,28 +13,24 @@ def process_arguments(args, defaults): defaults["token"] = args[count + 1] elif (args[count] == "--check-interval"): - defaults["check-interval"] = args[count + 1] + defaults["check-interval"] = int(args[count + 1]) elif (args[count] == "--post-interval"): - defaults["post-interval"] = args[count + 1] + defaults["post-interval"] = int(args[count + 1]) 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]) + elif (args[count] == "--use-docker"): + defaults["use-docker"] = args[count + 1] else: print(Fore.RED + 'โ— -> Wrong Argument Name!...') print(Style.RESET_ALL) + print(datetime.datetime.now()) count +=2 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!!!!!!!!! diff --git a/src/converters.py b/src/converters.py index cd86475..dd4d277 100644 --- a/src/converters.py +++ b/src/converters.py @@ -1,4 +1,6 @@ +# -*- coding: utf-8 -*- from colorama import Fore, Back, Style +import datetime def split_array(arr, size): count = len(arr) // size + 1 @@ -14,9 +16,10 @@ def try_to_get_carousel(array, post): return urls print(Fore.GREEN + "๐ŸŽ  > Found carousel!") print(Style.RESET_ALL) + print(datetime.datetime.now()) except Exception as e: print(Fore.RED + "๐ŸŽ ๐Ÿ’ฅ > No carousel :( \n", e) print(Style.RESET_ALL) + print(datetime.datetime.now()) return array - diff --git a/src/main.py b/src/main.py index 24c04b1..02b3dd7 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,8 @@ +# -*- coding: utf-8 -*- import os import sys import time +import datetime import json from mastodon import Mastodon from colorama import Fore, Back, Style @@ -11,9 +13,6 @@ from arguments import process_arguments 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("ARGUMENTS") @@ -24,13 +23,24 @@ default_settings = { "check-interval": 3600, "post-interval": 3600, "fetch-count" : 10, - "carousel-limit": 4 + "carousel-limit": 4, + "use-docker": True } settings = process_arguments(sys.argv, default_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"] mastodon_instance = settings["instance"] mastodon_token = settings["token"] @@ -42,19 +52,16 @@ post_interval = settings["post-interval"]#1m using_mastodon = settings["carousel-limit"] > 0; 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(Style.RESET_ALL) +print(datetime.datetime.now()) mastodon = Mastodon( access_token = mastodon_token, api_base_url = mastodon_instance # api_base_url = 'https://pixelfed.tokyo/' ) 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) diff --git a/src/network.py b/src/network.py index b9ca47b..45765e1 100644 --- a/src/network.py +++ b/src/network.py @@ -1,44 +1,63 @@ +# -*- coding: utf-8 -*- from colorama import Fore, Back, Style import requests import time +import datetime from already_posted import already_posted, mark_as_posted from converters import split_array, try_to_get_carousel 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): try: print(Fore.YELLOW + "๐Ÿš€ > Downloading Image...", url) print(Style.RESET_ALL) + print(datetime.datetime.now()) response = requests.get(url) response.raw.decode_content = True - + print(Fore.GREEN + "โœจ > Downloaded!") print(Style.RESET_ALL) + print(datetime.datetime.now()) return response.content except Exception as e: print(Fore.RED + "๐Ÿ’ฅ > Failed to download image. \n", e) print(Style.RESET_ALL) + print(datetime.datetime.now()) def upload_image_to_mastodon(url, mastodon): try: print(Fore.YELLOW + "๐Ÿ˜ > Uploading Image...") 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 print(Fore.GREEN + "โœจ > Uploaded!") print(Style.RESET_ALL) + print(datetime.datetime.now()) return media["id"] except Exception as e: print(Fore.RED + "๐Ÿ’ฅ > failed to upload image to mastodon. \n", e) print(Style.RESET_ALL) + print(datetime.datetime.now()) def toot(urls, title, mastodon, fetched_user ): try: print(Fore.YELLOW + "๐Ÿ˜ > Creating Toot...", title) print(Style.RESET_ALL) + print(datetime.datetime.now()) ids = [] for url in urls: ids.append(upload_image_to_mastodon(url, mastodon)) @@ -51,28 +70,38 @@ def toot(urls, title, mastodon, fetched_user ): except Exception as e: print(Fore.RED + "๐Ÿ˜ฟ > Failed to create toot \n", e) 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() stupidcounter = 0 for post in posts: - stupidcounter += 1 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): print(Fore.YELLOW + "๐Ÿ˜ > Already Posted ", post.url) print(Style.RESET_ALL) + print(datetime.datetime.now()) continue print("Posting... ", post.url) + print(datetime.datetime.now()) if using_mastodon: urls_arr = split_array(url_arr, carousel_size) for urls in urls_arr: toot(urls, post.caption, mastodon, fetched_user) else: 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) else: - return + break + print(Fore.GREEN + "โœจ > Fetched All") + print(Style.RESET_ALL) + print(datetime.datetime.now())