mirror of
https://github.com/ihabunek/toot.git
synced 2024-11-03 04:17:21 -05: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