Initial commit

This commit is contained in:
Ivan Habunek 2017-04-12 16:42:04 +02:00
commit 40a0739227
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
9 changed files with 328 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.egg-info/
*.pyc
.cache/
build/
dist/
tmp/
.pypirc
/.env

18
Makefile Normal file
View 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
View 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.

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[bdist_wheel]
universal=1

39
setup.py Normal file
View 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
View 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
View 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
View 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
View 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()