feat: Add media uploads
This commit is contained in:
parent
12134e4bb8
commit
6303bb2e65
15
README.rst
15
README.rst
@ -21,7 +21,6 @@ TODO
|
|||||||
|
|
||||||
- Не учитывается длина поста. Если исходный пост не будет укладываться в лимит символов на инстансе Mastodon'а, то неизвестно что произойдёт. Решение: надо обрезать текст поста в функции ``build_post()``.
|
- Не учитывается длина поста. Если исходный пост не будет укладываться в лимит символов на инстансе Mastodon'а, то неизвестно что произойдёт. Решение: надо обрезать текст поста в функции ``build_post()``.
|
||||||
- Никак не обрабатываются вложения типов отличных от фото (`photo`) и фотоальбома (`album`).
|
- Никак не обрабатываются вложения типов отличных от фото (`photo`) и фотоальбома (`album`).
|
||||||
- Надо первые 4-е картинки добавлять как вложения в пост в мастодоне. Для остальных вложений достаточно ссылки.
|
|
||||||
|
|
||||||
Настройки и запуск
|
Настройки и запуск
|
||||||
==================
|
==================
|
||||||
@ -51,18 +50,17 @@ API VK
|
|||||||
|
|
||||||
Нужно задать все переменные окружения. Удобный способ — в файле ``.env`` и экспортировать в шэлл.
|
Нужно задать все переменные окружения. Удобный способ — в файле ``.env`` и экспортировать в шэлл.
|
||||||
|
|
||||||
========================= =============================== ========================================
|
========================= =============================== =========================================================
|
||||||
Переменная окружения Пример Описание
|
Переменная окружения Пример Описание
|
||||||
========================= =============================== ========================================
|
========================= =============================== =========================================================
|
||||||
MASTODON_API_URL https://mastodon.social/api/v1 URL адрес API Mastodon.
|
MASTODON_API_URL https://mastodon.social/api/v1 URL адрес API Mastodon.
|
||||||
MASTODON_API_ACCESS_TOKEN Ключ API Mastodon.
|
MASTODON_API_ACCESS_TOKEN Ключ API Mastodon.
|
||||||
VK_API_URL https://api.vk.com/method URL API VK.
|
VK_API_URL https://api.vk.com/method URL API VK.
|
||||||
VK_API_VERSION 5.131 Версия VK API.
|
VK_API_VERSION 5.131 Версия VK API.
|
||||||
VK_API_ACCESS_TOKEN Ключ API VK.
|
VK_API_ACCESS_TOKEN Ключ API VK.
|
||||||
VK_GROUP_DOMAIN apiclub slug адрес группы/паблика VK.
|
VK_GROUP_DOMAIN apiclub slug адрес группы/паблика VK.
|
||||||
POLLING_TIME 300 Количество секунд между получением
|
POLLING_TIME 300 Количество секунд между получением постов. Умолчание: 300.
|
||||||
постов. По умолчанию 300.
|
========================= =============================== ==========================================================
|
||||||
========================= =============================== ========================================
|
|
||||||
|
|
||||||
Запуск
|
Запуск
|
||||||
------
|
------
|
||||||
@ -85,6 +83,11 @@ Docker
|
|||||||
История изменений
|
История изменений
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
0.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Реализована загрузка вложений в Mastodon (до 4-х штук). Только изображения.
|
||||||
|
|
||||||
0.1.0
|
0.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
__version__ = '0.1.0'
|
__version__ = '0.2.0'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import shutil
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
@ -36,7 +39,7 @@ def get_vk_post_url(post_data: dict) -> str:
|
|||||||
def get_vk_post_attachments(post_data: dict) -> list:
|
def get_vk_post_attachments(post_data: dict) -> list:
|
||||||
"""Process attachments. See attachments at
|
"""Process attachments. See attachments at
|
||||||
https://dev.vk.com/method/wall.post
|
https://dev.vk.com/method/wall.post
|
||||||
Return list with following structure::
|
Return list of dicts with following structure::
|
||||||
|
|
||||||
[{'photo': 'url'}, {'album': 'url'}]
|
[{'photo': 'url'}, {'album': 'url'}]
|
||||||
"""
|
"""
|
||||||
@ -76,27 +79,68 @@ def get_vk_post_attachments(post_data: dict) -> list:
|
|||||||
pass
|
pass
|
||||||
return attachments
|
return attachments
|
||||||
|
|
||||||
def build_post(post_data: dict) -> str:
|
def download_file(url: str) -> str:
|
||||||
|
"""Save file to /tmp. Return file path"""
|
||||||
|
filename = '/tmp/' + os.path.basename(urlparse(url).path)
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
with open(filename, 'wb') as out_file:
|
||||||
|
shutil.copyfileobj(response.raw, out_file)
|
||||||
|
del response
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def post_media(file: str) -> str:
|
||||||
|
"""Upload media file to Mastodon."""
|
||||||
|
headers = {'Authorization': 'Bearer ' + MASTODON_API_ACCESS_TOKEN}
|
||||||
|
files = {'file': open(file,'rb')}
|
||||||
|
response = requests.post(MASTODON_API_URL + '/media', files=files, headers=headers)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def publish_toot(post_text: str, media_ids: list):
|
||||||
|
"""Post toot on Mastodon.
|
||||||
|
See: https://docs.joinmastodon.org/methods/statuses/
|
||||||
|
"""
|
||||||
|
headers = {'Authorization': 'Bearer ' + MASTODON_API_ACCESS_TOKEN}
|
||||||
|
params = {'status': post_text, 'media_ids[]': media_ids}
|
||||||
|
response = requests.post(MASTODON_API_URL + '/statuses', data=params, headers=headers)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def post_toot(post_data: dict) -> str:
|
||||||
|
"""Upload media files, generate status text for Mastodon post and publish.
|
||||||
|
"""
|
||||||
post_text = get_vk_post_text(post_data)
|
post_text = get_vk_post_text(post_data)
|
||||||
vk_post_url = get_vk_post_url(post_data)
|
vk_post_url = get_vk_post_url(post_data)
|
||||||
attachments = get_vk_post_attachments(post_data)
|
attachments = get_vk_post_attachments(post_data)
|
||||||
|
|
||||||
|
# Upload attachments!
|
||||||
|
# Upload only first 4 photos and get media_ids
|
||||||
|
i = 0
|
||||||
|
media_ids = []
|
||||||
|
while i < 4:
|
||||||
|
if list(attachments[i].keys())[0] == 'photo':
|
||||||
|
photo_url = attachments[i]['photo']
|
||||||
|
print('Download image:', photo_url) # Log
|
||||||
|
tmpfile = download_file(photo_url) # Download file from VK
|
||||||
|
print('Saved as:', tmpfile) # Log
|
||||||
|
print('Upload to Mastodon:', tmpfile) # Log
|
||||||
|
media_id = json.loads(post_media(tmpfile).text)['id'] # Upload file to Mastodon
|
||||||
|
print('Uploaded. Media ID:', media_id) # Log
|
||||||
|
media_ids.append(media_id) # Save uploaded media IDs
|
||||||
|
print('Remove local file:', tmpfile) # Log
|
||||||
|
os.remove(tmpfile) # Remove local file
|
||||||
|
i += 1
|
||||||
|
|
||||||
# Build attachments list
|
# Build attachments list
|
||||||
post_attachments = ''
|
post_attachments = ''
|
||||||
for attachment in attachments:
|
for attachment in attachments:
|
||||||
key = str(list(attachment.keys())[0])
|
key = str(list(attachment.keys())[0])
|
||||||
# Example resulting string: 'Album: https://vk.com/album-26788782_284176934'
|
# Example resulting string: 'Album: https://vk.com/album-26788782_284176934'
|
||||||
post_attachments = post_attachments + key.title() + ': ' + attachment[key] + '\n'
|
post_attachments = post_attachments + key.title() + ': ' + attachment[key] + '\n'
|
||||||
return post_text + '\n\n' + 'Source: ' + vk_post_url + '\n\nAttachments:\n' + post_attachments
|
|
||||||
|
|
||||||
def post_toot(post_text: str):
|
# Get status text
|
||||||
"""Post toot on Mastodon.
|
text = post_text + '\n\n' + 'Source: ' + vk_post_url + '\n\nAttachments:\n' + post_attachments
|
||||||
See: https://docs.joinmastodon.org/methods/statuses/
|
|
||||||
"""
|
# Post toot!
|
||||||
auth = {'Authorization': 'Bearer ' + MASTODON_API_ACCESS_TOKEN}
|
publish_toot(text, media_ids)
|
||||||
params = {'status': post_text}
|
|
||||||
response = requests.post(MASTODON_API_URL + '/statuses', data=params, headers=auth)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def touch_lock_file(file: str, post_id: int):
|
def touch_lock_file(file: str, post_id: int):
|
||||||
with open(file, 'w') as lock:
|
with open(file, 'w') as lock:
|
||||||
@ -107,7 +151,7 @@ def read_lock_file(file: str):
|
|||||||
return int(lock.read())
|
return int(lock.read())
|
||||||
|
|
||||||
def poll():
|
def poll():
|
||||||
lock_file = './.last_post_id.tmp'
|
lock_file = './data/last_post_id'
|
||||||
if os.path.exists(lock_file):
|
if os.path.exists(lock_file):
|
||||||
prev_post_id = read_lock_file(lock_file)
|
prev_post_id = read_lock_file(lock_file)
|
||||||
print('Read last post ID from file:', lock_file)
|
print('Read last post ID from file:', lock_file)
|
||||||
@ -121,12 +165,11 @@ def poll():
|
|||||||
|
|
||||||
# Don't post duplicates
|
# Don't post duplicates
|
||||||
if post_id == prev_post_id:
|
if post_id == prev_post_id:
|
||||||
print('Skip posting')
|
print('Skip posting') # Log
|
||||||
else:
|
else:
|
||||||
# Toot!
|
# Toot!
|
||||||
print('Toot! VK post ID:', post_id)
|
print('===> Toot! VK post ID:', post_id) # Log
|
||||||
post = build_post(post_data)
|
post_toot(post_data)
|
||||||
post_toot(post)
|
|
||||||
|
|
||||||
touch_lock_file(lock_file, post_id)
|
touch_lock_file(lock_file, post_id)
|
||||||
prev_post_id = read_lock_file(lock_file)
|
prev_post_id = read_lock_file(lock_file)
|
||||||
|
Loading…
Reference in New Issue
Block a user