Compare commits

...

50 Commits
v0.2 ... main

Author SHA1 Message Date
George f5cceda106
Merge pull request #17 from neilcar/main
Add --scheduled, --verbose, and no user.
2023-02-04 17:10:37 +03:00
Neil Carpenter 4a0de99672 Add USE_KUBERNETES 2022-12-04 22:06:02 -05:00
Neil Carpenter 16c76af0c0 Add --scheduled, --verbose, and no user. 2022-12-04 21:37:12 -05:00
George 2db531d2aa
Merge pull request #15 from neilcar/main
Add action to build container
2022-11-29 09:40:55 +00:00
neilcar a5912cee3c
Update docker-publish.yml 2022-11-29 00:59:17 -05:00
neilcar 04e2d13247
Updating cosign
Fixing cosign error related to invalid TUF.
2022-11-29 00:54:47 -05:00
neilcar 519e6beafa
Add GH action to build & push 2022-11-29 00:49:24 -05:00
George 44783e0343
Merge pull request #12 from gunchleoc/fix-typo
Fix typo
2022-09-11 17:45:41 +00:00
GunChleoc 79da06cf2e Fix typo 2022-09-04 09:48:01 +01:00
George e7b8423dc8
Update README.md 2022-09-01 09:22:05 +00:00
Horhik c388f6096a Add instagram authorization 2022-08-31 21:02:54 +03:00
Horhik ae9afdbc3e update gitignore 2022-08-31 20:37:28 +03:00
George 765e398010
Merge pull request #7 from gunchleoc/support-video
Add support for video
2022-05-31 10:39:17 +03:00
GunChleoc 2721af5aeb Add support for video 2022-05-29 13:42:15 +01:00
George 509d44f3a4
Just removed emoji 2022-05-13 06:47:30 +03:00
George 880bbd7c40
Update README.md 2022-05-12 23:27:59 +03:00
horhik 95e3f9d220 Add both flags and environment variables support 2022-04-13 23:11:54 +03:00
horhik ff058ea7d8 Update README 2022-04-13 23:10:49 +03:00
horhik a424783a71 finish docker-compose file 2022-04-13 22:42:50 +03:00
horhik 43d2ea6c01 add more environment variables 2022-04-13 21:53:05 +03:00
horhik 497d04ad2d update requipments.txt 2021-11-23 10:38:49 +03:00
horhik a8354a8b5b update requipments.txt 2021-11-23 10:37:07 +03:00
George 85a3191426
Merge pull request #3 from Horhik/v0.3
Merge v0.3 into master
2021-11-22 16:37:48 +03:00
George 684bb8c5bb
Change "vim" to "nano"
Because I'm worried what you can't exit :)
2021-11-22 15:44:55 +03:00
horhik 971e74d254 add more variabes in env.sh 2021-11-22 15:41:47 +03:00
horhik 68f475f6e5 update image 2021-11-22 15:30:59 +03:00
George 043739193f
Update README.md 2021-11-22 15:27:38 +03:00
George 918c22a021
Update Docs.md 2021-11-20 14:38:10 +03:00
Innubis a31fccae67 probably, fix issue 2 2021-11-20 05:50:00 -05:00
horhik edec38f883 some updates 2021-11-20 12:56:16 +03:00
horhik 2f35753de5 Merge branch 'v0.3' of https://github.com/Horhik/instagram2mastodon into v0.3 2021-11-20 12:55:06 +03:00
horhik 2b425b0f5a update gitignore 2021-11-20 12:53:31 +03:00
horhik 6078359bb6 try to fix issue 2 2021-11-20 12:18:58 +03:00
Innubis 5c7a8a2861 some changes 2021-11-17 11:39:53 -05:00
root 61fa41d39e some changes 2021-11-17 11:38:22 -05:00
horhik 5c8867a938 beautify Docs.md 2021-11-14 23:54:16 +03:00
horhik a39365ec1f add example to Docs 2021-11-14 23:52:13 +03:00
horhik 0cea3d42f3 update README 2021-11-14 23:49:10 +03:00
horhik 66ce481e03 add Default arguments list to Docs.md 2021-11-14 23:46:53 +03:00
horhik aa0e8cfdbf add --use-docker to run.sh 2021-11-14 23:33:28 +03:00
horhik 4847a5e870 add time in logging and remove some shit 2021-11-14 23:24:59 +03:00
horhik 535ea46f24 remove shit from ./insta2fedi 2021-11-14 23:20:48 +03:00
horhik 1c717f9ded fix errors in Docs.md 2021-11-14 23:19:53 +03:00
horhik 88e344a9f7 update Docs 2021-11-14 23:17:45 +03:00
horhik e35b400819 add Docs.md 2021-11-14 23:11:00 +03:00
horhik 4f27547390 update gitignore 2021-11-14 22:53:43 +03:00
root 11e33f07a2 add requirements.txt 2021-11-14 14:51:47 -05:00
root 6c6a87cbac add --use-docker option; add requimpments.txt 2021-11-14 14:47:34 -05:00
horhik df66d02287 add flags 2021-11-11 14:52:47 +03:00
horhik 9ddfab49d4 some updates 2021-11-11 14:51:48 +03:00
16 changed files with 534 additions and 51 deletions

94
.github/workflows/docker-publish.yml vendored Normal file
View File

@ -0,0 +1,94 @@
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
push:
branches: [ "main" ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Install the cosign tool except on PR
# https://github.com/sigstore/cosign-installer
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@f3c664df7af409cb4873aa5068053ba9d61a57b6 #v2.6.0
with:
cosign-release: 'v1.13.1'
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
env:
COSIGN_EXPERIMENTAL: "true"
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign {}@${{ steps.build-and-push.outputs.digest }}

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
*~
already_posted.txt
src/__pycache__/
.venv
.venv/
.env.sh
docker-compose.yml
docker-compose.yaml

21
Dockerfile Normal file → Executable file
View File

@ -1,12 +1,25 @@
FROM python:3.9
RUN pip install instaloader
RUN pip install Mastodon.py
RUN pip install colorama
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENV USE_DOCKER=1
ENV YOUR_CONTAINER_NAME="$YOUR_CONTAINER_NAME"
ENV I2M_INSTAGRAM_USER="$I2M_INSTAGRAM_USER"
ENV I2M_INSTANCE="$I2M_INSTANCE"
ENV I2M_TOKEN="$I2M_TOKEN"
ENV I2M_CHECK_INTERVAL "$I2M_CHECK_INTERVAL"
ENV I2M_POST_INTERVAL "$I2M_POST_INTERVAL"
ENV I2M_USE_MASTODON "$I2M_USE_MASTODON"
ENV I2M_FETCH_COUNT "$I2M_FETCH_COUNT"
#ENTRYPOINT ["python", "/app/src/main.py", "--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]
#ENTRYPOINT ["echo", "--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]
ENTRYPOINT ["python", "/app/src/main.py"]

89
Docs.md Normal file
View File

@ -0,0 +1,89 @@
# Instagram2Fedi Docs 📜
## How to use
You can use Instagram2Fedi via docker or just like a python script
** Note: ** _Credentials can be complicated. Running without Instagram credentials (`user-name` and `user-password`) appears to work for a short period of time but will, eventually, fail. Providing credentials will work unless Instagram issues a challenge. Recommend leaving `user-name` blank if running as a scheduled job (`--scheduled`) and providing them otherwise._
### 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 --user-name <admin> --user-password <admin>
# 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 fetched instagram account user name.
---
`--user-name` - Your instagram user name.
---
`--user-password` - Your instagram password.
---
`--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`
---
`--scheduled` - If set, Instagram2Fedi runs once instead of sleeping for `check-interval` and running forever. This is intended for use as a `cron` job. No additional parameter is needed, just add `--scheduled`.
---
`--verbose` - If set, output all logs including secrets. No additional parameter is needed, just add `--scheduled`.
## 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

51
README.md Normal file → Executable file
View File

@ -1,22 +1,57 @@
_Guys... Instagram is sh*t. Even [bibliogram](https://www.reddit.com/r/privacy/comments/wrczxc/bibliogram_is_being_discontinued/) is being discontinued. If you're able to migrate you proile to any fediverse instance or contact to person, whose instagram you'd like to crosspost and ask him to post to fediverse to, it wil be the best desicion_
# 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 docker-compose
1. create `docker-compose.yaml` with following content
_You can use default.docker-compose.yaml from repo_
``` yaml
version: '3'
services:
bot:
build:
context: .
image: "horhik/instagram2fedi:latest"
environment:
- YOUR_CONTAINER_NAME=<whatever>
- I2M_INSTAGRAM_USER=<instgram username>
- I2M_INSTANCE=<mastodon or pixelfed instance>
- I2M_TOKEN=<your token here>
- I2M_CHECK_INTERVAL=3600 #1 hour
- I2M_POST_INTERVAL=3600 #1 hour
- I2M_USE_MASTODON=4 #max carouse - is 4, if there's no limit set to -1
- I2M_FETCH_COUNT=5 # how many instagram posts to fetch per check_interval -
- I2M_USER_NAME=admin # Your instagram login name
- I2M_USER_PASSWORD=admin # Your instagram password
```
** Note: ** _Since somewhen it's seems not possible to fetch any data from instagram anonymously (maybe i'm wrong and there's a solution, I'll be very happy to know about it). Due that you unfortunately have to had an instagram accound and provide login and password to this script_
2. And edit environment variables
3. Run `docker-compose up -d`
## Using with Dockerfile
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)

View File

@ -0,0 +1,17 @@
version: '3'
services:
bot:
build:
context: .
#image: "horhik/instagram2fedi:latest"
environment:
- YOUR_CONTAINER_NAME=instagram2fedi
- I2M_INSTAGRAM_USER= #<fetched instagram user name>
- I2M_INSTANCE= #<Mastodon or pixelfed instance>
- I2M_TOKEN= # SECRET TOKEN
- I2M_CHECK_INTERVAL=3600 #1 hour
- I2M_POST_INTERVAL=3600 #1 hour
- I2M_USE_MASTODON=4 #max carouse - is 4, if there's no limit set to -1
- I2M_FETCH_COUNT=5 # how many instagram posts to fetch per check_interval -
- I2M_USER_NAME= # Your instagram login name
- I2M_USER_PASSWORD= # Your instagram password

15
env.sh
View File

@ -1,8 +1,13 @@
#!/bin/sh
#!b/in/sh
export YOUR_CONTAINER_NAME=
export I2M_INSTAGRAM_USER=
export I2M_INSTANCE=
export I2M_TOKEN=
# Required
export YOUR_CONTAINER_NAME=kek
export I2M_INSTAGRAM_USER=kek
export I2M_INSTANCE=kek
export I2M_TOKEN=kek
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 $@

14
requirements.txt Normal file
View File

@ -0,0 +1,14 @@
blurhash==1.1.4
certifi==2022.6.15
charset-normalizer==2.1.0
colorama==0.4.5
decorator==5.1.1
idna==3.3
instaloader==4.9.3
Mastodon.py==1.5.1
python-dateutil==2.8.2
python-magic==0.4.27
pytz==2022.1
requests==2.28.1
six==1.16.0
urllib3==1.26.11

2
run.sh
View File

@ -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 $I2M_INSTAGRAM_USER $I2M_INSTANCE $I2M_TOKEN
docker build -t $YOUR_CONTAINER_NAME .; docker container run -it -v $(pwd):/app

107
src/arguments.py Normal file
View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
import os
import datetime
from colorama import Fore, Back, Style
instagram_user = os.environ.get("I2M_INSTAGRAM_USER")
user_name = os.environ.get("I2M_USER_NAME")
user_password = os.environ.get("I2M_USER_PASSWORD")
instance = os.environ.get("I2M_INSTANCE")
token = os.environ.get("I2M_TOKEN")
check_interval = os.environ.get("I2M_CHECK_INTERVAL") #1 hour
post_interval = os.environ.get("I2M_POST_INTERVAL") #1 hour
use_mastodon = os.environ.get("I2M_USE_MASTODON") #max carousel is 4, if there's no limit set to -1
fetch_count = os.environ.get("I2M_FETCH_COUNT") # how many instagram posts to fetch per check_interval
if os.environ.get("I2M_SCHEDULED") == "True":
scheduled_run = True # run continuously (if False) or a single time (if True)
else:
scheduled_run = False
if os.environ.get("I2M_VERBOSE") == "True": # verbose output
verbose_output = True
else:
verbose_output = False
if verbose_output:
print('instagram', instagram_user)
print('instagram', instance)
print(token)
print(check_interval)
print(post_interval)
print(use_mastodon)
print(fetch_count)
print(user_name)
print(user_password)
print(scheduled_run)
print(verbose_output)
def flags(args, defaults):
count = 1
while (len(args) > count):
if(args[count] == "--instance"):
defaults["instance"] = args[count + 1]
elif (args[count] == "--instagram-user"):
defaults["instagram-user"] = args[count + 1]
elif (args[count] == "--token"):
defaults["token"] = args[count + 1]
elif (args[count] == "--check-interval"):
defaults["check-interval"] = int(args[count + 1])
elif (args[count] == "--post-interval"):
defaults["post-interval"] = int(args[count + 1])
elif (args[count] == "--fetch-count"):
defaults["fetch-count"] = int(args[count + 1])
elif (args[count] == "--use-mastodon"):
defaults["carousel-limit"] = int(args[count + 1])
elif (args[count] == "--use-docker"):
defaults["use-docker"] = args[count + 1]
elif (args[count] == "--user-name"):
defaults["user-name"] = args[count + 1]
elif (args[count] == "--user-password"):
defaults["user-password"] = args[count + 1]
elif (args[count] == "--scheduled"):
defaults["scheduled"] = True
count -= 1
elif (args[count] == "--verbose"):
defaults["verbose"] = True
count -= 1
else:
print(Fore.RED + '❗ -> Wrong Argument Name!...')
print(Style.RESET_ALL)
print(datetime.datetime.now())
count +=2
return defaults
def check_defaults(arg):
return arg if arg != '' and arg else None
def process_arguments(args, defaults):
defaults["instance"] = instance if instance !='' and instance else None
defaults["instagram-user"] = instagram_user if instagram_user != '' and instagram_user else None
# Users login and password
defaults["user-name"] = check_defaults(user_name)
defaults["user-password"] = check_defaults(user_password)
defaults["token"] = token if token != '' and token else None
defaults["check-interval"] = int(check_interval) if check_interval != '' and check_interval else None
defaults["post-interval"] = int(post_interval) if post_interval != '' and post_interval else None
defaults["fetch-count"] = int(fetch_count) if fetch_count != '' and fetch_count else None
defaults["carousel-limit"] = int(use_mastodon) if use_mastodon != '' and use_mastodon else None
defaults["scheduled"] = bool(scheduled_run) if scheduled_run else False
defaults["verbose"] = bool(verbose_output) if verbose_output else False
#print(Fore.RED + '❗ -> Missing Argument ')
#print(Style.RESET_ALL)
#print(datetime.datetime.now())
# Command line arguments more prioritized, if smth has been written in .env and in cmd args, then Instagram2Fedi will take values from `cmd args`
new_defaults = flags(args, defaults)
return new_defaults

View File

@ -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
@ -10,13 +12,41 @@ def split_array(arr, size):
def try_to_get_carousel(array, post):
try:
urls = list(map(lambda arr: arr['node']['display_url'], vars(post)['_node']['edge_sidecar_to_children']['edges']))
return urls
print(Fore.GREEN + "🎠 > Found carousel!")
print(Style.RESET_ALL)
except Exception as e:
print(Fore.RED + "🎠💥 > No carousel :( \n", e)
print(Style.RESET_ALL)
return array
node = vars(post)['_node']
if 'edge_sidecar_to_children' in node:
try:
urls = list(map(lambda arr: arr['node']['display_url'], node['edge_sidecar_to_children']['edges']))
print(Fore.GREEN + "🎠 > Found carousel!")
print(Style.RESET_ALL)
print(datetime.datetime.now())
return urls
except Exception as e:
print(Fore.RED + "🎠💥 > No carousel :( \n", e)
print(Style.RESET_ALL)
print(datetime.datetime.now())
return array
else:
print(Fore.YELLOW + "🎠💥 > No carousel\n")
# We can also have video in a separate key
if 'is_video' in node and node ['is_video']:
try:
urls = [node['video_url']]
print(Fore.GREEN + "🎞 > Found video!")
print(Style.RESET_ALL)
print(datetime.datetime.now())
return urls
except Exception as e:
print(Fore.RED + "🎞💥 > No video :( \n", e)
print(Style.RESET_ALL)
print(datetime.datetime.now())
return array
else:
print(Fore.YELLOW + "🎠💥 > No video\n")
except Exception as e:
print(Fore.RED + "😱💥 > No node :( \n", e)
print(Style.RESET_ALL)
print(datetime.datetime.now())
return array
return array

View File

@ -1,41 +1,81 @@
# -*- coding: utf-8 -*-
import os
import sys
import time
import datetime
import json
from mastodon import Mastodon
from colorama import Fore, Back, Style
from instaloader import Profile, Instaloader, LatestStamps
from arguments import process_arguments
from network import get_new_posts
id_filename = "/app/already_posted.txt"
default_settings = {
"instance": None,
"instagram-user": None,
"user-name": "",
"user-password": None,
"token": None,
"check-interval": 3600,
"post-interval": 60,
"fetch-count" : 10,
"carousel-limit": 4,
"scheduled": False,
"verbose": False
}
settings = process_arguments(sys.argv, default_settings)
verbose = settings["verbose"]
if verbose:
print("ARGUMENTS")
print(sys.argv)
print('SETTINGS' , settings)
agree = [1, True, "true", "True", "yes", "Yes"]
if (os.environ.get("USE_DOCKER")):
id_filename = "/app/already_posted.txt"
elif (os.environ.get("USE_KUBERNETES")):
id_filename = "/data/already_posted.txt"
else:
id_filename = "./already_posted.txt"
with open(id_filename, "a") as f:
f.write("\n")
fetched_user = sys.argv[1]
mastodon_instance = sys.argv[2]
mastodon_token = sys.argv[3]
fetched_user = settings["instagram-user"]
mastodon_instance = settings["instance"]
mastodon_token = settings["token"]
post_limit = 1
time_interval_sec = 86400
post_interval = 10
post_limit = settings["fetch-count"]
time_interval_sec = settings["check-interval"] #1d
post_interval = settings["post-interval"]#1m
using_mastodon = True;
mastodon_carousel_size = 4
using_mastodon = settings["carousel-limit"] > 0;
mastodon_carousel_size = settings["carousel-limit"]
scheduled = settings["scheduled"]
print(Fore.GREEN + '🚀 > Connecting to Instagram...')
print(Style.RESET_ALL)
L = Instaloader()
profile = Profile.from_username(L.context, fetched_user)
user = {
"name": settings["user-name"],
"password": settings["user-password"]
}
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, user)
if scheduled:
break
time.sleep(time_interval_sec)

View File

@ -1,78 +1,110 @@
# -*- 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, fetched_user):
L = Instaloader()
print(Fore.GREEN + 'TEST 🚀 > Connecting to Instagram...')
print(Style.RESET_ALL)
print(datetime.datetime.now())
if user["name"] != None:
print("USER USER USER!!!!!!!!!!!!!", user["name"])
L.login(user["name"], user["password"])
return Profile.from_username(L.context, fetched_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))
post_text = str(title) + "\n" + "crosposted from https://instagram.com/"+fetched_user # creating post text
post_text = str(title) + "\n" # creating post text
post_text = post_text[0:1000]
print(ids)
mastodon.status_post(post_text, media_ids = ids)
if(ids):
print(ids)
mastodon.status_post(post_text, media_ids = ids)
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, user):
# fetching user profile to get new posts
profile = get_instagram_user(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())