init
This commit is contained in:
commit
a93a8d736c
14
.env.example
Normal file
14
.env.example
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
OSHI_USERNAME=
|
||||||
|
|
||||||
|
INSTAGRAM_USER=
|
||||||
|
INSTAGRAM_PASS=
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
REDIS_DB=0
|
||||||
|
|
||||||
|
BOT_TOKEN=
|
||||||
|
CHAT_ID=
|
||||||
|
|
||||||
|
TELEGRAM_URL=https://api.telegram.org/bot
|
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
*.json
|
||||||
|
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
stories/
|
||||||
|
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
users\.txt
|
||||||
|
|
||||||
|
\.idea/
|
||||||
|
|
||||||
|
\.vscode/\.ropeproject/
|
||||||
|
|
||||||
|
.env
|
||||||
|
Env/
|
||||||
|
venv/
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM python:3-alpine
|
||||||
|
|
||||||
|
|
||||||
|
RUN apk update && apk upgrade && pip install -U pip
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
RUN pip --no-cache-dir install -r requirements.txt
|
||||||
|
|
||||||
|
RUN crontab /app/crontab.txt
|
||||||
|
RUN touch /var/log/cron.log
|
||||||
|
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/Asia/Jakarta /etc/localtime && echo "Asia/Jakarta" > /etc/timezone
|
||||||
|
|
||||||
|
CMD crond && tail -f /var/log/cron.log
|
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# OshiStories
|
||||||
|
Telegram bot to download your idol's latest media stories and posts
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
* [Python 3](https://www.python.org/)
|
||||||
|
* [Redis](https://redis.io)
|
||||||
|
* [Docker](https://docker.com)
|
||||||
|
|
||||||
|
### Settings before run
|
||||||
|
1. rename .env.example to .env
|
||||||
|
2. Add instagram username to OSHI_USERNAME with comma separated
|
||||||
|
|
||||||
|
### How To Create & Get BOT Token
|
||||||
|
[BotFather](https://www.siteguarding.com/en/how-to-get-telegram-bot-api-token)
|
||||||
|
|
||||||
|
### How To Get CHAT ID
|
||||||
|
1. Add bot as your friend & start chat
|
||||||
|
2. Open
|
||||||
|
```
|
||||||
|
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
|
||||||
|
```
|
||||||
|

|
||||||
|
|
||||||
|
### Build Docker Images
|
||||||
|
```bash
|
||||||
|
$ docker build -t oshi-stories .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Docker Container
|
||||||
|
```bash
|
||||||
|
$ docker run -it --rm --name oshi-stories-container -d oshi-stories
|
||||||
|
```
|
1
crontab.txt
Normal file
1
crontab.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
* * * * * python3 /app/run.py story feed > /dev/null
|
10
requirements.txt
Normal file
10
requirements.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
certifi==2019.6.16
|
||||||
|
chardet==3.0.4
|
||||||
|
idna==2.8
|
||||||
|
instagram-private-api==1.6.0
|
||||||
|
python-dateutil==2.8.0
|
||||||
|
python-dotenv==0.10.3
|
||||||
|
redis==3.3.8
|
||||||
|
requests==2.22.0
|
||||||
|
six==1.12.0
|
||||||
|
urllib3==1.25.3
|
268
run.py
Normal file
268
run.py
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
import argparse
|
||||||
|
import codecs
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from xml.dom.minidom import parseString
|
||||||
|
import urllib as urllib
|
||||||
|
import send_telegram
|
||||||
|
import time
|
||||||
|
# try:
|
||||||
|
# import urllib.request as urllib
|
||||||
|
# except ImportError:
|
||||||
|
|
||||||
|
try:
|
||||||
|
from instagram_private_api import (
|
||||||
|
Client, ClientError, ClientLoginError,
|
||||||
|
ClientCookieExpiredError, ClientLoginRequiredError,
|
||||||
|
__version__ as client_version)
|
||||||
|
except ImportError:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
from instagram_private_api import (
|
||||||
|
Client, ClientError, ClientLoginError,
|
||||||
|
ClientCookieExpiredError, ClientLoginRequiredError,
|
||||||
|
__version__ as client_version)
|
||||||
|
|
||||||
|
from instagram_private_api import ClientError
|
||||||
|
from instagram_private_api import Client
|
||||||
|
import redis
|
||||||
|
|
||||||
|
|
||||||
|
def to_json(python_object):
|
||||||
|
if isinstance(python_object, bytes):
|
||||||
|
return {'__class__': 'bytes',
|
||||||
|
'__value__': codecs.encode(python_object, 'base64').decode()}
|
||||||
|
raise TypeError(repr(python_object) + ' is not JSON serializable')
|
||||||
|
|
||||||
|
|
||||||
|
def from_json(json_object):
|
||||||
|
if '__class__' in json_object and json_object.get('__class__') == 'bytes':
|
||||||
|
return codecs.decode(json_object.get('__value__').encode(), 'base64')
|
||||||
|
return json_object
|
||||||
|
|
||||||
|
|
||||||
|
def onlogin_callback(api, setting_name):
|
||||||
|
cache_settings = api.settings
|
||||||
|
redis_client.setex(setting_name, 7776000000, json.dumps(cache_settings, default=to_json))
|
||||||
|
print('[+] New auth cookie file was made: {0!s}'.format(setting_name))
|
||||||
|
|
||||||
|
def login(username, password):
|
||||||
|
device_id = None
|
||||||
|
try:
|
||||||
|
settings_file = "credentials.json"
|
||||||
|
credential = redis_client.get('credentials')
|
||||||
|
if credential is None:
|
||||||
|
api = Client(
|
||||||
|
username, password,
|
||||||
|
on_login=lambda x: onlogin_callback(x, 'credentials'))
|
||||||
|
else:
|
||||||
|
cached_settings = json.loads(credential, object_hook=from_json)
|
||||||
|
device_id = cached_settings.get('device_id')
|
||||||
|
# reuse auth settings
|
||||||
|
api = Client(
|
||||||
|
username, password,
|
||||||
|
settings=cached_settings)
|
||||||
|
print('[+] Using cached login cookie for "' + api.authenticated_user_name + '".')
|
||||||
|
except (ClientCookieExpiredError, ClientLoginRequiredError) as e:
|
||||||
|
print('ClientCookieExpiredError/ClientLoginRequiredError: {0!s}'.format(e))
|
||||||
|
|
||||||
|
# Login expired
|
||||||
|
# Do relogin but use default ua, keys and such
|
||||||
|
api = Client(
|
||||||
|
username, password,
|
||||||
|
device_id=device_id,
|
||||||
|
on_login=lambda x: onlogin_callback(x, settings_file))
|
||||||
|
|
||||||
|
except ClientLoginError as e:
|
||||||
|
print('[!] Could not login: {:s}.\n[!] {:s}\n\n{:s}'.format(
|
||||||
|
json.loads(e.error_response).get("error_title", "Error title not available."),
|
||||||
|
json.loads(e.error_response).get("message", "Not available"), e.error_response))
|
||||||
|
print('-' * 70)
|
||||||
|
sys.exit(9)
|
||||||
|
except ClientError as e:
|
||||||
|
print('[!] Client Error: {:s}'.format(e.error_response))
|
||||||
|
print('-' * 70)
|
||||||
|
sys.exit(9)
|
||||||
|
except Exception as e:
|
||||||
|
if str(e).startswith("unsupported pickle protocol"):
|
||||||
|
print("[W] This cookie file is not compatible with Python {}.".format(sys.version.split(' ')[0][0]))
|
||||||
|
print("[W] Please delete your cookie file 'credentials.json' and try again.")
|
||||||
|
else:
|
||||||
|
print('[!] Unexpected Exception: {0!s}'.format(e))
|
||||||
|
print('-' * 70)
|
||||||
|
sys.exit(99)
|
||||||
|
|
||||||
|
print('[+] Login to "' + api.authenticated_user_name + '" OK!')
|
||||||
|
cookie_expiry = api.cookie_jar.auth_expires
|
||||||
|
print('[+] Login cookie expiry date: {0!s}'.format(
|
||||||
|
datetime.datetime.fromtimestamp(cookie_expiry).strftime('%Y-%m-%d at %I:%M:%S %p')))
|
||||||
|
|
||||||
|
return api
|
||||||
|
|
||||||
|
def like_post(post_id):
|
||||||
|
like = ig_client.post_like(post_id)
|
||||||
|
return like
|
||||||
|
|
||||||
|
def latest_post(username, user_id):
|
||||||
|
today = datetime.date.today()
|
||||||
|
# today = "2019-09-13"
|
||||||
|
t = time.mktime(datetime.datetime.strptime(str(today), "%Y-%m-%d").timetuple())
|
||||||
|
t = int(t)
|
||||||
|
feed = ig_client.user_feed(user_id, min_timestamp=str(t))
|
||||||
|
f = open("feeds.json", "w")
|
||||||
|
f.write(json.dumps(feed))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
for post in feed['items']:
|
||||||
|
list_media = []
|
||||||
|
# print()
|
||||||
|
post_id = post['id']
|
||||||
|
pk = post['code']
|
||||||
|
k = '{}:{}:{}'.format("post", username, pk)
|
||||||
|
exist = redis_client.exists(k)
|
||||||
|
if exist == 1:
|
||||||
|
print(pk + " Exist")
|
||||||
|
continue
|
||||||
|
media_type = "photo" if post['media_type'] == 1 else "video"
|
||||||
|
if "carousel_media" in post:
|
||||||
|
carousel = True
|
||||||
|
for i, cs in enumerate(post['carousel_media'], start=1):
|
||||||
|
carousel_media_type = "photo" if cs['media_type'] == 1 else "video"
|
||||||
|
if carousel_media_type == 'photo':
|
||||||
|
list_media.append({
|
||||||
|
"caption":"Photo #{}".format(i),
|
||||||
|
"type":"photo",
|
||||||
|
"media":cs['image_versions2']['candidates'][0]['url']
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
list_media.append({
|
||||||
|
"caption":"Video #{}".format(i),
|
||||||
|
"type":"video",
|
||||||
|
"media":cs['video_versions'][0]['url']
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
carousel = False
|
||||||
|
if media_type == 'photo':
|
||||||
|
url_media = post['image_versions2']['candidates'][0]['url']
|
||||||
|
else:
|
||||||
|
url_media = post['video_versions'][0]['url']
|
||||||
|
post_url = "https://instagram.com/p/{}".format(pk)
|
||||||
|
caption = "[POST] from {}\n\n{}\n\nLink: {}".format(username, post['caption']['text'], post_url)
|
||||||
|
if len(caption) > 1000:
|
||||||
|
caption = caption[:1000] + '...'
|
||||||
|
|
||||||
|
like = like_post(post_id)
|
||||||
|
|
||||||
|
print(pk + " Sending...")
|
||||||
|
if carousel is True:
|
||||||
|
send_telegram.send_media_group(caption=caption, media=list_media)
|
||||||
|
else:
|
||||||
|
send_telegram.telegram_bot_send_media(fileType=media_type, url=url_media, caption=caption)
|
||||||
|
print(pk + " OK")
|
||||||
|
redis_client.setex(k, 86400, "True")
|
||||||
|
return
|
||||||
|
|
||||||
|
def latest_stories(username, user_id):
|
||||||
|
feed = ig_client.user_story_feed(user_id)
|
||||||
|
if feed['reel'] is None:
|
||||||
|
print('No Update')
|
||||||
|
# exit(9)
|
||||||
|
return False
|
||||||
|
taken_at = True
|
||||||
|
feed_json = feed['reel']['items']
|
||||||
|
|
||||||
|
list_video = []
|
||||||
|
list_image = []
|
||||||
|
|
||||||
|
for media in feed_json:
|
||||||
|
if not taken_at:
|
||||||
|
taken_ts = None
|
||||||
|
else:
|
||||||
|
if media.get('imported_taken_at'):
|
||||||
|
taken_ts = datetime.datetime.utcfromtimestamp(media.get('imported_taken_at', "")).strftime(
|
||||||
|
'%Y-%m-%d %H:%M:%S')
|
||||||
|
else:
|
||||||
|
taken_ts = datetime.datetime.utcfromtimestamp(media.get('taken_at', "")).strftime(
|
||||||
|
'%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
is_video = 'video_versions' in media and 'image_versions2' in media
|
||||||
|
pk = media['code']
|
||||||
|
k = '{}:{}:{}:{}'.format("story", username, "video" if is_video else "photo", pk)
|
||||||
|
exist = redis_client.exists(k)
|
||||||
|
|
||||||
|
if exist == 0:
|
||||||
|
print('{} Sending...'.format(pk))
|
||||||
|
caption = "[STORY] from {}"
|
||||||
|
if is_video:
|
||||||
|
data_media = {
|
||||||
|
'url': media['video_versions'][0]['url'],
|
||||||
|
'taken': taken_ts
|
||||||
|
}
|
||||||
|
# caption = "[VIDEO] from {}"
|
||||||
|
list_video.append(data_media)
|
||||||
|
send_telegram.telegram_bot_send_media(fileType='video', url=data_media['url'], caption=caption.format(username))
|
||||||
|
else:
|
||||||
|
data_media = {
|
||||||
|
'url': media['image_versions2']['candidates'][0]['url'],
|
||||||
|
'taken': taken_ts
|
||||||
|
}
|
||||||
|
# caption = "[PHOTO] from {}"
|
||||||
|
list_image.append(data_media)
|
||||||
|
send_telegram.telegram_bot_send_media(fileType='photo', url=data_media['url'], caption=caption.format(username))
|
||||||
|
print('{} OK'.format(pk))
|
||||||
|
redis_client.setex(k, 86400, json.dumps(data_media))
|
||||||
|
else:
|
||||||
|
print(pk + ' Exists')
|
||||||
|
return feed
|
||||||
|
|
||||||
|
def download_user(user, attempt=0):
|
||||||
|
try:
|
||||||
|
if not user.isdigit():
|
||||||
|
user_res = ig_client.username_info(user)
|
||||||
|
user_id = user_res['user']['pk']
|
||||||
|
else:
|
||||||
|
user_id = user
|
||||||
|
user_info = ig_client.user_info(user_id)
|
||||||
|
if not user_info.get("user", None):
|
||||||
|
raise Exception("No user is associated with the given user id.")
|
||||||
|
else:
|
||||||
|
user = user_info.get("user").get("username")
|
||||||
|
if "feed" in sys.argv:
|
||||||
|
print("[=] Fetch POST [=]")
|
||||||
|
latest_post(user, user_id)
|
||||||
|
if "story" in sys.argv:
|
||||||
|
print("[=] Fetch STORY [=]")
|
||||||
|
latest_stories(user, user_id)
|
||||||
|
|
||||||
|
# print(user)
|
||||||
|
return user
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def start():
|
||||||
|
oshi = os.getenv('OSHI_USERNAME')
|
||||||
|
oshi = oshi.split(',')
|
||||||
|
print('------------------------------')
|
||||||
|
for o in oshi:
|
||||||
|
print("Oshi: "+ o)
|
||||||
|
download_user(o)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
load_dotenv()
|
||||||
|
try:
|
||||||
|
redis_client = redis.Redis(host=os.getenv('REDIS_HOST'), port=os.getenv('REDIS_PORT'), db=os.getenv('REDIS_DB'), password=os.getenv('REDIS_PASSWORD'))
|
||||||
|
print("Connection to redis has been established...")
|
||||||
|
except Exception as e:
|
||||||
|
print("Cannot connect to redis...")
|
||||||
|
print(e)
|
||||||
|
username = os.getenv('INSTAGRAM_USER')
|
||||||
|
password = os.getenv('INSTAGRAM_PASS')
|
||||||
|
ig_client = login(username, password)
|
||||||
|
start()
|
BIN
screenshot-get-chat_id.png
Normal file
BIN
screenshot-get-chat_id.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
77
send_telegram.py
Normal file
77
send_telegram.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import json
|
||||||
|
|
||||||
|
def telegram_bot_sendtext(bot_message):
|
||||||
|
send_text = TELEGRAM_URL + BOT_TOKEN + '/sendMessage?chat_id={}&text={}'.format(CHAT_ID,bot_message)
|
||||||
|
response = requests.get(send_text)
|
||||||
|
response_json = response.json()
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def send_media_group(**kwargs):
|
||||||
|
caption = kwargs.get('caption')
|
||||||
|
media = kwargs.get('media')
|
||||||
|
carousel = kwargs.get('carousel', None)
|
||||||
|
|
||||||
|
send_text = telegram_bot_sendtext(caption);
|
||||||
|
if send_text['ok'] is not True:
|
||||||
|
print('Stop')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
message_id = send_text.get('result')['message_id']
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'chat_id' : CHAT_ID,
|
||||||
|
"reply_to_message_id":message_id,
|
||||||
|
"media": json.dumps(media)
|
||||||
|
}
|
||||||
|
send_url = TELEGRAM_URL + '{}{}'.format(BOT_TOKEN, '/sendMediaGroup')
|
||||||
|
response = requests.post(send_url, data=data)
|
||||||
|
response_json = response.json()
|
||||||
|
if response_json.get('ok') is True:
|
||||||
|
print('DONE')
|
||||||
|
return response_json
|
||||||
|
|
||||||
|
def telegram_bot_send_media(**kwargs):
|
||||||
|
fileType = kwargs.get('fileType')
|
||||||
|
url = kwargs.get('url', None)
|
||||||
|
caption = kwargs.get('caption')
|
||||||
|
reply_first = kwargs.get('reply_first', False)
|
||||||
|
reply_id = kwargs.get('reply_id', None)
|
||||||
|
data = {
|
||||||
|
'chat_id' : CHAT_ID,
|
||||||
|
"caption": caption,
|
||||||
|
}
|
||||||
|
if reply_first is True:
|
||||||
|
send_text = telegram_bot_sendtext(caption);
|
||||||
|
if send_text['ok'] is not True:
|
||||||
|
print('Stop')
|
||||||
|
return
|
||||||
|
data.update({"reply_to_message_id": send_text.get('result')['message_id']})
|
||||||
|
if reply_id is not None: data.update({"reply_to_message_id": reply_id})
|
||||||
|
if fileType == 'video':
|
||||||
|
data.update({
|
||||||
|
'video': url
|
||||||
|
})
|
||||||
|
endpoint = "/sendVideo"
|
||||||
|
elif fileType == 'photo':
|
||||||
|
data.update({
|
||||||
|
'photo': url
|
||||||
|
})
|
||||||
|
endpoint = "/sendPhoto"
|
||||||
|
else:
|
||||||
|
print("Failed")
|
||||||
|
return False
|
||||||
|
|
||||||
|
send_url = TELEGRAM_URL + '{}{}'.format(BOT_TOKEN, endpoint)
|
||||||
|
response = requests.post(send_url,data=data)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == 'send_telegram':
|
||||||
|
load_dotenv()
|
||||||
|
BOT_TOKEN = os.getenv('BOT_TOKEN')
|
||||||
|
CHAT_ID = os.getenv('CHAT_ID')
|
||||||
|
TELEGRAM_URL = os.getenv('TELEGRAM_URL')
|
Loading…
x
Reference in New Issue
Block a user