This commit is contained in:
D. Scott Boggs 2025-05-26 06:40:35 -04:00
parent e754f6fe59
commit d41aad6f53
4 changed files with 103 additions and 8 deletions

View file

@ -1,7 +1,3 @@
from blastodon.client import Client
from blastodon.interface import interface
client = Client.init_cli()
post_text = input(' enter a status to post: ')
client.send_text_post(post_text)
interface()

View file

@ -59,7 +59,7 @@ def auth_bsky() -> BskyClient:
def _mastodon_find_single_user_login() -> Path | None:
found_file = None
for file in Path.cwd().iterdir():
if file.name.startswith('login.') and file.name.endswith('.secret'):
if file.name.startswith('mastodon.') and file.name.endswith('.secret'):
print('found secret file', file.name)
if found_file is None:
found_file = file
@ -72,7 +72,7 @@ def _mastodon_find_single_user_login() -> Path | None:
def _bsky_find_single_user_login() -> Path | None:
found_file = None
for file in Path.cwd().iterdir():
if file.name.startswith('bsky-') and file.name.endswith('-jwt.secret'):
if file.name.startswith('bsky_') and file.name.endswith('_session.secret'):
if found_file is None:
found_file = file
else:

98
blastodon/interface.py Normal file
View file

@ -0,0 +1,98 @@
from functools import cached_property
from os import getenv, SEEK_SET
from pathlib import Path
from shutil import which
from subprocess import run
from tempfile import NamedTemporaryFile
from traceback import print_stack
from click import command, option, group, argument, pass_context, confirm
from blastodon.client import Client
class Context:
"""A context object which may be used accross commands"""
@cached_property
def client(self) -> Client:
return Client.init_cli()
@group
def interface():
...
@interface.group
def post():
...
@post.command('status')
@argument('content', required=False)
@option('--content-file', '-f', help="post the contents of a file")
@pass_context
def text_status(ctx, content: str | None = None, content_file: str | None = None):
ctx.ensure_object(Context)
if content_file and content:
raise SyntaxError('cannot specify content and content source file')
if content_file:
with open(content_file) as file:
content = file.read()
print('status:')
print(content)
if confirm('\n\n Post this status?', show_default=True, default=True):
result = ctx.obj.client.send_text_post(content)
print('status posted ok')
print('mastodon:', result.mastodon.url)
print('bsky: ', result.bsky.uri)
@post.command(help='open the default editor to compose a status')
@pass_context
def compose(ctx):
ctx.ensure_object(Context)
status_text = _compose_message()
if not status_text:
print('not posting empty status')
return
print('status:')
print(status_text)
if confirm('\n\tPost this status?', show_default=True, default=True):
result = ctx.obj.client.send_text_post(status_text)
print('status posted ok')
print('mastodon:', result.mastodon.url)
print('bsky: ', result.bsky.uri)
else:
print('Ok, not posting.')
def _compose_message() -> str:
editor = getenv('VISUAL') or getenv('EDITOR') or which('micro') or which('nano') or 'vi'
with NamedTemporaryFile(prefix='blastodon_', suffix='.post') as tempfile:
tempfile.write(b'# compose a status, then save and close the editor to post it.\n')
tempfile.write(b'# empty lines and lines starting with "#" will be ignored\n')
tempfile.flush()
result = run(executable=editor, args=[editor, tempfile.name])
result.check_returncode()
tempfile.seek(0, SEEK_SET)
return '\n'.join(
line
for l
in (bs.decode() for bs in tempfile.readlines())
if (line := l.strip()) and not line.startswith('#')
)
@post.command('image')
@option('--compose-message', help='Open an editor to compose a text status which this image will be attached to', is_flag=True, default=False)
@option('--message', help='A text status this image will be attached to')
@option('--alt-text', help='Describe the image', prompt=True)
@option('--mime-type', help='The mime type of the attached file. If not specified, determined from the file contents')
@argument('filepath', callback=lambda _ctx, _param, fp: Path(fp))
@pass_context
def post_image(ctx, compose_message: bool, message: str, alt_text: str, mime_type: str, filepath: Path):
ctx.ensure_object(Context)
if compose_message and message:
raise SyntaxError("Can't specify both --message and --compose-message")
if compose_message:
message = _compose_message()
result = ctx.obj.client.send_image_post(post_text=message, image_path=filepath, alt_text=alt_text)
print('status posted ok')
print('mastodon:', result.mastodon.url)
print('bsky: ', result.bsky.uri)

View file

@ -15,6 +15,7 @@ dependencies = [
"atproto",
"mastodon.py",
"python-magic",
"click",
]
dynamic = ["version"]