mirror of
https://github.com/ihabunek/toot.git
synced 2024-09-22 04:25:55 -04:00
Initial commit
This commit is contained in:
commit
40a0739227
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.egg-info/
|
||||||
|
*.pyc
|
||||||
|
.cache/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
tmp/
|
||||||
|
.pypirc
|
||||||
|
/.env
|
18
Makefile
Normal file
18
Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
default : clean dist
|
||||||
|
|
||||||
|
dist :
|
||||||
|
@echo "\nMaking source"
|
||||||
|
@echo "-------------"
|
||||||
|
@python setup.py sdist
|
||||||
|
|
||||||
|
@echo "\nMaking wheel"
|
||||||
|
@echo "-------------"
|
||||||
|
@python setup.py bdist_wheel --universal
|
||||||
|
|
||||||
|
@echo "\nDone."
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -rf build dist *.egg-info MANIFEST
|
||||||
|
|
||||||
|
publish :
|
||||||
|
twine upload dist/*
|
28
README.rst
Normal file
28
README.rst
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
====
|
||||||
|
Toot
|
||||||
|
====
|
||||||
|
|
||||||
|
Post to Mastodon social networks from the command line.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Install using pip:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
pip install toot
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Currently implements only posting a new status:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
toot post "Hello world!"
|
||||||
|
|
||||||
|
On first use, will ask you to choose a Mastodon instance and log in.
|
39
setup.py
Normal file
39
setup.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
with open("README.rst") as readme:
|
||||||
|
long_description = readme.read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='toot',
|
||||||
|
version='0.1.0',
|
||||||
|
description='Interact with Mastodon social networks from the command line.',
|
||||||
|
long_description=long_description,
|
||||||
|
author='Ivan Habunek',
|
||||||
|
author_email='ivan@habunek.com',
|
||||||
|
url='https://github.com/ihabunek/toot/',
|
||||||
|
keywords='mastodon toot',
|
||||||
|
license='MIT',
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'License :: OSI Approved :: MIT License',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.3',
|
||||||
|
'Programming Language :: Python :: 3.4',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
|
],
|
||||||
|
packages=['toot'],
|
||||||
|
install_requires=[
|
||||||
|
'future'
|
||||||
|
],
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'toot=toot.console:main',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
28
toot.py
Normal file
28
toot.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from mastodon import Mastodon
|
||||||
|
|
||||||
|
# app = Mastodon.create_app('toot', to_file='app_creds.txt')
|
||||||
|
# print app
|
||||||
|
|
||||||
|
# mastodon = Mastodon(client_id='app_creds.txt')
|
||||||
|
# mastodon.log_in('ivan@habunek.com', 'K2oEeDHdMEvCbAnEJjeB18sv', to_file='user_creds.txt')
|
||||||
|
|
||||||
|
|
||||||
|
# # Create actual instance
|
||||||
|
# mastodon = Mastodon(
|
||||||
|
# client_id='app_creds.txt',
|
||||||
|
# access_token='user_creds.txt'
|
||||||
|
# )
|
||||||
|
|
||||||
|
# mastodon.toot('Testing')
|
||||||
|
|
||||||
|
|
||||||
|
# import ConfigParser
|
||||||
|
|
||||||
|
# config = ConfigParser.ConfigParser()
|
||||||
|
# config.read('auth.ini')
|
||||||
|
|
||||||
|
# print config.get('Auth', 'foo2')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
58
toot/__init__.py
Normal file
58
toot/__init__.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
App = namedtuple('App', ['base_url', 'client_id', 'client_secret'])
|
||||||
|
User = namedtuple('User', ['username', 'access_token'])
|
||||||
|
|
||||||
|
APP_NAME = 'toot'
|
||||||
|
DEFAULT_INSTANCE = 'mastodon.social'
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(base_url):
|
||||||
|
url = base_url + 'api/v1/apps'
|
||||||
|
|
||||||
|
response = requests.post(url, {
|
||||||
|
'client_name': 'toot',
|
||||||
|
'redirect_uris': 'urn:ietf:wg:oauth:2.0:oob',
|
||||||
|
'scopes': 'read write',
|
||||||
|
'website': 'https://github.com/ihabunek/toot',
|
||||||
|
})
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
client_id = data.get('client_id')
|
||||||
|
client_secret = data.get('client_secret')
|
||||||
|
|
||||||
|
return App(base_url, client_id, client_secret)
|
||||||
|
|
||||||
|
|
||||||
|
def login(app, username, password):
|
||||||
|
url = app.base_url + 'oauth/token'
|
||||||
|
|
||||||
|
response = requests.post(url, {
|
||||||
|
'grant_type': 'password',
|
||||||
|
'client_id': app.client_id,
|
||||||
|
'client_secret': app.client_secret,
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'scope': 'read write',
|
||||||
|
})
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
access_token = data.get('access_token')
|
||||||
|
|
||||||
|
return User(username, access_token)
|
||||||
|
|
||||||
|
|
||||||
|
def post_status(app, user, status):
|
||||||
|
url = app.base_url + '/api/v1/statuses'
|
||||||
|
headers = {"Authorization": "Bearer " + user.access_token}
|
||||||
|
|
||||||
|
response = requests.post(url, {'status': status}, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
return response.json()
|
57
toot/config.py
Normal file
57
toot/config.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from . import User, App
|
||||||
|
|
||||||
|
CONFIG_DIR = os.environ['HOME'] + '/.config/toot/'
|
||||||
|
CONFIG_APP_FILE = CONFIG_DIR + 'app.cfg'
|
||||||
|
CONFIG_USER_FILE = CONFIG_DIR + 'user.cfg'
|
||||||
|
|
||||||
|
|
||||||
|
def collapse(tuple):
|
||||||
|
return [v for k, v in tuple.__dict__.items()]
|
||||||
|
|
||||||
|
|
||||||
|
def _load(file, tuple_class):
|
||||||
|
if not os.path.exists(file):
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(file, 'r') as f:
|
||||||
|
lines = f.read().split()
|
||||||
|
try:
|
||||||
|
return tuple_class(*lines)
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _save(file, named_tuple):
|
||||||
|
directory = os.path.dirname(file)
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
values = [v for k, v in named_tuple.__dict__.items()]
|
||||||
|
return f.write("\n".join(values))
|
||||||
|
|
||||||
|
|
||||||
|
def load_app():
|
||||||
|
return _load(CONFIG_APP_FILE, App)
|
||||||
|
|
||||||
|
|
||||||
|
def load_user():
|
||||||
|
return _load(CONFIG_USER_FILE, User)
|
||||||
|
|
||||||
|
|
||||||
|
def save_app(app):
|
||||||
|
return _save(CONFIG_APP_FILE, app)
|
||||||
|
|
||||||
|
|
||||||
|
def save_user(user):
|
||||||
|
return _save(CONFIG_USER_FILE, user)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_app(app):
|
||||||
|
return os.unlink(CONFIG_APP_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_user(user):
|
||||||
|
return os.unlink(CONFIG_USER_FILE)
|
90
toot/console.py
Normal file
90
toot/console.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from builtins import input
|
||||||
|
from getpass import getpass
|
||||||
|
|
||||||
|
from .config import save_user, load_user, load_app, save_app, CONFIG_APP_FILE, CONFIG_USER_FILE
|
||||||
|
from . import create_app, login, post_status, DEFAULT_INSTANCE
|
||||||
|
|
||||||
|
|
||||||
|
def green(text):
|
||||||
|
return "\033[92m{}\033[0m".format(text)
|
||||||
|
|
||||||
|
|
||||||
|
def red(text):
|
||||||
|
return "\033[91m{}\033[0m".format(text)
|
||||||
|
|
||||||
|
|
||||||
|
def create_app_interactive():
|
||||||
|
instance = input("Choose an instance [{}]: ".format(DEFAULT_INSTANCE))
|
||||||
|
if not instance:
|
||||||
|
instance = DEFAULT_INSTANCE
|
||||||
|
|
||||||
|
base_url = 'https://{}'.format(instance)
|
||||||
|
|
||||||
|
print("Creating app with {}".format(base_url))
|
||||||
|
app = create_app(base_url)
|
||||||
|
|
||||||
|
print("App tokens saved to: {}".format(green(CONFIG_APP_FILE)))
|
||||||
|
save_app(app)
|
||||||
|
|
||||||
|
|
||||||
|
def login_interactive(app):
|
||||||
|
print("\nLog in to " + green(app.base_url))
|
||||||
|
email = input('Email: ')
|
||||||
|
password = getpass('Password: ')
|
||||||
|
|
||||||
|
print("Authenticating...")
|
||||||
|
user = login(app, email, password)
|
||||||
|
|
||||||
|
save_user(user)
|
||||||
|
print("User token saved to " + green(CONFIG_USER_FILE))
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def print_usage():
|
||||||
|
print("toot - interact with Mastodon from the command line")
|
||||||
|
print("")
|
||||||
|
print("Usage:")
|
||||||
|
print(" toot post \"All your base are belong to us\"")
|
||||||
|
print("")
|
||||||
|
print("https://github.com/ihabunek/toot")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_post_status(app, user):
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print red("No status text given")
|
||||||
|
return
|
||||||
|
|
||||||
|
response = post_status(app, user, sys.argv[2])
|
||||||
|
|
||||||
|
print "Toot posted: " + green(response.get('url'))
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_auth(app, user):
|
||||||
|
if app and user:
|
||||||
|
print("You are logged in")
|
||||||
|
print("Mastodon instance: " + green(app.base_url))
|
||||||
|
print("Username: " + green(user.username))
|
||||||
|
else:
|
||||||
|
print("You are not logged in")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
command = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
|
||||||
|
if os.getenv('TOOT_DEBUG'):
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
app = load_app() or create_app_interactive()
|
||||||
|
user = load_user() or login_interactive(app)
|
||||||
|
|
||||||
|
if command == 'post':
|
||||||
|
cmd_post_status(app, user)
|
||||||
|
elif command == 'auth':
|
||||||
|
cmd_auth(app, user)
|
||||||
|
else:
|
||||||
|
print_usage()
|
Loading…
Reference in New Issue
Block a user