added support for more than one media file
This commit is contained in:
parent
f6f900b4e2
commit
b099fa7fec
3 changed files with 134 additions and 100 deletions
162
app/custom.py
162
app/custom.py
|
|
@ -8,6 +8,7 @@ from json import JSONDecodeError
|
||||||
|
|
||||||
import tweepy
|
import tweepy
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from telethon.tl.functions.channels import GetMessagesRequest
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ def load_x_v2_api_oauth2_handler():
|
||||||
|
|
||||||
async def create_tweet(content, media_ids=None):
|
async def create_tweet(content, media_ids=None):
|
||||||
client = load_x_v2_api()
|
client = load_x_v2_api()
|
||||||
post = client.create_tweet(text=content, media_ids=media_ids)
|
post = client.create_tweet(text=content, media_ids=media_ids if media_ids else None)
|
||||||
if post.data:
|
if post.data:
|
||||||
open("{}/created.txt".format(os.getenv('DATA_FOLDER')), "a+").write("{}\n".format(post.data['id']))
|
open("{}/created.txt".format(os.getenv('DATA_FOLDER')), "a+").write("{}\n".format(post.data['id']))
|
||||||
return post.data
|
return post.data
|
||||||
|
|
@ -96,78 +97,107 @@ async def find_text_and_download_media(bot, message):
|
||||||
text = message.message
|
text = message.message
|
||||||
media_path, media_size = None, None
|
media_path, media_size = None, None
|
||||||
media_channel_id = message.peer_id.channel_id
|
media_channel_id = message.peer_id.channel_id
|
||||||
# everything else
|
|
||||||
if hasattr(message.media, 'document'):
|
|
||||||
try:
|
|
||||||
# check video length
|
|
||||||
if message.document.attributes[0].duration > 140:
|
|
||||||
raise Exception("Video is too long")
|
|
||||||
except (AttributeError, IndexError):
|
|
||||||
pass
|
|
||||||
mime_type = message.document.mime_type.split('/')
|
|
||||||
media_id = message.media.document.id
|
|
||||||
media_path = "{}/{}.{}".format(media_folder, media_id, mime_type[1])
|
|
||||||
media_size = message.media.document.size
|
|
||||||
# compressed pictures
|
|
||||||
elif hasattr(message.media, 'photo'):
|
|
||||||
media_id = message.media.photo.id
|
|
||||||
media_path = "{}/{}.{}".format(media_folder, media_id, 'jpg')
|
|
||||||
media_size = message.media.photo.size if hasattr(message.media.photo, 'size') else message.media.photo.sizes[-1].sizes[-1]
|
|
||||||
# check filesize
|
|
||||||
if media_size > 536870912:
|
|
||||||
raise Exception("File is too big")
|
|
||||||
# create a download progress file
|
# create a download progress file
|
||||||
os.makedirs(progress_folder := "{}/progress".format(os.getenv('DATA_FOLDER')), exist_ok=True)
|
os.makedirs(progress_folder := "{}/progress".format(os.getenv('DATA_FOLDER')), exist_ok=True)
|
||||||
media_progress_file = "{}/{}-{}.json".format(progress_folder, media_channel_id, message.id)
|
media_progress_file = "{}/{}-{}.json".format(progress_folder, media_channel_id, message.id)
|
||||||
progress_data = {"last_update": time.time(), "percent": 0, "file_path": media_path, "cancelled": False, "id": None}
|
progress_data = {"last_update": time.time(), "percent": 0, "file_path": media_path, "cancelled": False, "id": None}
|
||||||
if media_path:
|
|
||||||
# get local media size
|
|
||||||
if os.path.exists(media_path):
|
|
||||||
_media_size = os.path.getsize(media_path)
|
|
||||||
else:
|
|
||||||
_media_size = 0
|
|
||||||
# check if sizes match
|
|
||||||
if media_size > _media_size:
|
|
||||||
progress_message = await message.reply("Downloading from TG... 0%")
|
|
||||||
progress_data['id'] = progress_message.id
|
|
||||||
# deleting existing file
|
|
||||||
if os.path.exists(media_path):
|
|
||||||
os.remove(media_path)
|
|
||||||
open(media_progress_file, "w").write(json.dumps(progress_data))
|
|
||||||
|
|
||||||
# callback to update the download progress
|
|
||||||
async def progress_callback(current, total):
|
|
||||||
progress = json.loads(open(media_progress_file, "r").read())
|
|
||||||
percent = round(current / total, 2)
|
|
||||||
if progress['cancelled'] is True:
|
|
||||||
await bot.edit_message(progress_message, "Downloading from TG... cancelled!!")
|
|
||||||
raise Exception('Cancelled')
|
|
||||||
if float(progress['last_update']) + 3 < time.time():
|
|
||||||
try:
|
|
||||||
await bot.edit_message(progress_message, "Downloading from TG... {}%".format(round(percent * 100, 1)))
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(e)
|
|
||||||
else:
|
|
||||||
progress['last_update'] = time.time()
|
|
||||||
progress['percent'] = percent
|
|
||||||
open(media_progress_file, "w").write(json.dumps(progress))
|
|
||||||
|
|
||||||
# download a new copy of the media
|
|
||||||
await bot.download_media(message, media_path, progress_callback=progress_callback)
|
|
||||||
# show it's completed
|
|
||||||
await bot.edit_message(progress_message, "Downloading from TG... 100%")
|
|
||||||
|
|
||||||
open(media_progress_file, "w").write(json.dumps(progress_data))
|
open(media_progress_file, "w").write(json.dumps(progress_data))
|
||||||
return text, media_path
|
|
||||||
|
|
||||||
|
media = []
|
||||||
async def upload_media(filename):
|
# get any other messages
|
||||||
api = load_x_v1_api()
|
try:
|
||||||
media = api.media_upload(filename)
|
result = await bot(GetMessagesRequest(id=list(range(message.id, message.id + int(os.getenv('X_MAX_MEDIA')))), channel=message.chat_id))
|
||||||
if hasattr(media, 'image') or media.processing_info['state'] == 'succeeded':
|
for m in result.messages:
|
||||||
return media.media_id
|
if m.date != message.date:
|
||||||
|
break
|
||||||
|
media.append(m.media)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
else:
|
else:
|
||||||
return False
|
if len(media) == 0:
|
||||||
|
return text, media
|
||||||
|
|
||||||
|
# setup the message to notify user media is downloading
|
||||||
|
progress_message = await message.reply("Downloading from TG... 0%")
|
||||||
|
progress_data['id'] = progress_message.id
|
||||||
|
open(media_progress_file, "w").write(json.dumps(progress_data))
|
||||||
|
|
||||||
|
# download all media
|
||||||
|
media_paths = []
|
||||||
|
for m in media:
|
||||||
|
# everything else
|
||||||
|
if hasattr(m, 'document'):
|
||||||
|
try:
|
||||||
|
# check video length
|
||||||
|
if m.document.attributes[0].duration > 140:
|
||||||
|
raise Exception("Video is too long")
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
pass
|
||||||
|
mime_type = m.document.mime_type.split('/')
|
||||||
|
media_id = m.document.id
|
||||||
|
media_path = "{}/{}.{}".format(media_folder, media_id, mime_type[1])
|
||||||
|
media_size = m.document.size
|
||||||
|
# compressed pictures
|
||||||
|
elif hasattr(m, 'photo'):
|
||||||
|
media_id = m.photo.id
|
||||||
|
media_path = "{}/{}.{}".format(media_folder, media_id, 'jpg')
|
||||||
|
if hasattr(m.photo, 'size'):
|
||||||
|
media_size = m.photo.size
|
||||||
|
else:
|
||||||
|
media_size = m.photo.sizes[-1].sizes[-1]
|
||||||
|
# check filesize
|
||||||
|
if media_size > 536870912:
|
||||||
|
raise Exception("File is too big")
|
||||||
|
if media_path:
|
||||||
|
# get local media size, this deletes any incomplete downloads
|
||||||
|
if os.path.exists(media_path):
|
||||||
|
_media_size = os.path.getsize(media_path)
|
||||||
|
else:
|
||||||
|
_media_size = 0
|
||||||
|
# check if sizes match
|
||||||
|
if media_size > _media_size:
|
||||||
|
# deleting existing file
|
||||||
|
if os.path.exists(media_path):
|
||||||
|
os.remove(media_path)
|
||||||
|
|
||||||
|
# callback to update the download progress
|
||||||
|
async def progress_callback(current, total):
|
||||||
|
progress = json.loads(open(media_progress_file, "r").read())
|
||||||
|
percent = round(current / total, 2)
|
||||||
|
if progress['cancelled'] is True:
|
||||||
|
await bot.edit_message(progress_message, "Downloading from TG... cancelled!!")
|
||||||
|
raise Exception('Cancelled')
|
||||||
|
if float(progress['last_update']) + 3 < time.time():
|
||||||
|
try:
|
||||||
|
await bot.edit_message(progress_message, "Downloading from TG... {}%".format(round(percent * 100, 1)))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
else:
|
||||||
|
progress['last_update'] = time.time()
|
||||||
|
progress['percent'] = percent
|
||||||
|
open(media_progress_file, "w").write(json.dumps(progress))
|
||||||
|
|
||||||
|
# download a new copy of the media
|
||||||
|
await bot.download_media(m, media_path, progress_callback=progress_callback)
|
||||||
|
# show it's completed
|
||||||
|
await bot.edit_message(progress_message, "Downloading from TG... 100%")
|
||||||
|
open(media_progress_file, "w").write(json.dumps(progress_data))
|
||||||
|
|
||||||
|
# store path to file in array
|
||||||
|
if os.path.exists(media_path):
|
||||||
|
media_paths.append(media_path)
|
||||||
|
|
||||||
|
return text, media_paths
|
||||||
|
|
||||||
|
|
||||||
|
async def upload_media(filenames):
|
||||||
|
uploaded = []
|
||||||
|
api = load_x_v1_api()
|
||||||
|
for f in filenames:
|
||||||
|
media = api.media_upload(f)
|
||||||
|
if hasattr(media, 'image') or media.processing_info['state'] == 'succeeded':
|
||||||
|
uploaded.append(media.media_id)
|
||||||
|
return uploaded
|
||||||
|
|
||||||
|
|
||||||
def refresh_x_oauth2_token():
|
def refresh_x_oauth2_token():
|
||||||
|
|
|
||||||
69
app/main.py
69
app/main.py
|
|
@ -25,7 +25,7 @@ for package in (silence_packages := (
|
||||||
bot = TelegramClient('bot', int(os.getenv('TELEGRAM_APP_ID')), os.getenv('TELEGRAM_APP_HASH')).start(bot_token=os.getenv('TELEGRAM_BOT_TOKEN'))
|
bot = TelegramClient('bot', int(os.getenv('TELEGRAM_APP_ID')), os.getenv('TELEGRAM_APP_HASH')).start(bot_token=os.getenv('TELEGRAM_BOT_TOKEN'))
|
||||||
|
|
||||||
|
|
||||||
@bot.on(events.NewMessage)
|
@bot.on(events.NewMessage(pattern='.*'))
|
||||||
async def echo(event):
|
async def echo(event):
|
||||||
user_input = event.text
|
user_input = event.text
|
||||||
# skip if bot or no one sent a command
|
# skip if bot or no one sent a command
|
||||||
|
|
@ -116,40 +116,43 @@ async def echo(event):
|
||||||
user_name = get_name_from_user(event.message.from_user)
|
user_name = get_name_from_user(event.message.from_user)
|
||||||
record_path = "{}/posts/tweet_{}_{}.json".format(os.getenv('DATA_FOLDER'), chat_id, message_id)
|
record_path = "{}/posts/tweet_{}_{}.json".format(os.getenv('DATA_FOLDER'), chat_id, message_id)
|
||||||
|
|
||||||
# get text, switch to caption if media is attached
|
media_paths = []
|
||||||
try:
|
if (reply_to_message and reply_to_message.media) or (event.message and event.message.media):
|
||||||
text, media_path = await find_text_and_download_media(bot, reply_to_message or event.message)
|
# get text, switch to caption if media is attached
|
||||||
except Exception as e:
|
try:
|
||||||
if os.path.exists(media_progress_file):
|
text, media_paths = await find_text_and_download_media(bot, reply_to_message or event.message)
|
||||||
os.remove(media_progress_file)
|
except Exception as e:
|
||||||
if "Cancelled" in str(e):
|
if os.path.exists(media_progress_file):
|
||||||
logging.debug("Cancelled media download")
|
os.remove(media_progress_file)
|
||||||
return
|
if "Cancelled" in str(e):
|
||||||
logging.error(e)
|
logging.debug("Cancelled media download")
|
||||||
if "File is too big" in str(e):
|
return
|
||||||
return await event.reply("Error: file is too big for X")
|
logging.error(e)
|
||||||
elif "Video is too long" in str(e):
|
if "File is too big" in str(e):
|
||||||
return await event.reply("Error: video is too long for X")
|
return await event.reply("Error: file is too big for X")
|
||||||
|
elif "Video is too long" in str(e):
|
||||||
|
return await event.reply("Error: video is too long for X")
|
||||||
|
else:
|
||||||
|
return await event.reply("Error: unknown TG download error")
|
||||||
else:
|
else:
|
||||||
return await event.reply("Error: unknown TG download error")
|
logging.info("{} ({}) Downloaded file(s): {}".format(user_name, user_id, media_paths))
|
||||||
|
media_progress = json.loads(open(media_progress_file, "r").read())
|
||||||
|
if media_progress['id']:
|
||||||
|
await bot.edit_message(channel_id, media_progress['id'], "Uploading to X...")
|
||||||
|
else:
|
||||||
|
progress_message = await event.reply("Uploading to X...")
|
||||||
|
media_progress['id'] = progress_message.id
|
||||||
|
open(media_progress_file, "w").write(json.dumps(media_progress))
|
||||||
else:
|
else:
|
||||||
logging.info("{} ({}) Downloaded file: {}".format(user_name, user_id, media_path))
|
text = reply_to_message.message or event.message.message
|
||||||
media_progress = json.loads(open(media_progress_file, "r").read())
|
|
||||||
if media_progress['id']:
|
|
||||||
await bot.edit_message(channel_id, media_progress['id'], "Uploading to X...")
|
|
||||||
else:
|
|
||||||
progress_message = await event.reply("Uploading to X...")
|
|
||||||
media_progress['id'] = progress_message.id
|
|
||||||
open(media_progress_file, "w").write(json.dumps(media_progress))
|
|
||||||
|
|
||||||
text = text.replace(user_input[0], '').strip() if text else ''
|
text = text.replace(user_input[0], '').strip() if text else ''
|
||||||
|
|
||||||
# check if tweet is empty
|
# check if tweet is empty
|
||||||
if len(user_input) == 1 and not media_path and not text:
|
if len(user_input) == 1 and not media_paths and not text:
|
||||||
return await event.reply("Not enough parameters")
|
return await event.reply("Not enough parameters")
|
||||||
# upload media
|
# upload media
|
||||||
try:
|
try:
|
||||||
media_id = await upload_media(media_path) if media_path else None
|
media_ids = await upload_media(media_paths) if media_paths else None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
if "maxFileSizeExceeded" in str(e):
|
if "maxFileSizeExceeded" in str(e):
|
||||||
|
|
@ -157,19 +160,19 @@ async def echo(event):
|
||||||
else:
|
else:
|
||||||
return await event.reply("Error: unknown X upload error")
|
return await event.reply("Error: unknown X upload error")
|
||||||
else:
|
else:
|
||||||
if media_id:
|
if media_ids:
|
||||||
logging.info("{} ({}) Uploaded file: {}".format(user_name, user_id, media_path))
|
logging.info("{} ({}) Uploaded file(s): {}".format(user_name, user_id, media_paths))
|
||||||
elif media_id is False:
|
elif media_ids is False:
|
||||||
logging.info("{} ({}) Failed to uploaded file: {}".format(user_name, user_id, media_path))
|
logging.info("{} ({}) Failed to uploaded file(s): {}".format(user_name, user_id, media_paths))
|
||||||
finally:
|
finally:
|
||||||
if os.path.exists(media_progress_file):
|
if os.path.exists(media_progress_file):
|
||||||
media_progress = json.loads(open(media_progress_file, "r").read())
|
media_progress = json.loads(open(media_progress_file, "r").read())
|
||||||
await bot.delete_messages(channel_id, media_progress['id'])
|
await bot.delete_messages(channel_id, media_progress['id'])
|
||||||
os.remove(media_progress_file)
|
os.remove(media_progress_file)
|
||||||
|
|
||||||
# send tweet
|
|
||||||
try:
|
try:
|
||||||
post = await create_tweet(text, [media_id] if media_id else None)
|
# send tweet
|
||||||
|
post = await create_tweet(text, media_ids)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
if "include either text or media" in str(e):
|
if "include either text or media" in str(e):
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,5 @@ X_ACCESS_SECRET=
|
||||||
X_CONSUMER_KEY=
|
X_CONSUMER_KEY=
|
||||||
X_CONSUMER_SECRET=
|
X_CONSUMER_SECRET=
|
||||||
X_CLIENT_ID=
|
X_CLIENT_ID=
|
||||||
X_CLIENT_SECRET=
|
X_CLIENT_SECRET=
|
||||||
|
X_MAX_MEDIA=10
|
||||||
Loading…
Reference in a new issue