Introduction
Introduction Statistics Contact Development Disclaimer Help
Initial commit - toot - Unnamed repository; edit this file 'description' to nam…
Log
Files
Refs
LICENSE
---
commit 40a07392274e0f15f9cdce8e6d22ac4cedb6be3a
Author: Ivan Habunek <[email protected]>
Date: Wed, 12 Apr 2017 16:42:04 +0200
Initial commit
Diffstat:
.gitignore | 9 +++++++++
Makefile | 18 ++++++++++++++++++
README.rst | 28 ++++++++++++++++++++++++++++
setup.cfg | 2 ++
setup.py | 39 +++++++++++++++++++++++++++++++
toot.py | 28 ++++++++++++++++++++++++++++
toot/__init__.py | 58 ++++++++++++++++++++++++++++++
toot/config.py | 57 +++++++++++++++++++++++++++++++
toot/console.py | 90 +++++++++++++++++++++++++++++++
9 files changed, 329 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,8 @@
+*.egg-info/
+*.pyc
+.cache/
+build/
+dist/
+tmp/
+.pypirc
+/.env
+\ No newline at end of file
diff --git a/Makefile b/Makefile
@@ -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/*
diff --git a/README.rst b/README.rst
@@ -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.
diff --git a/setup.cfg b/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal=1
diff --git a/setup.py b/setup.py
@@ -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='[email protected]',
+ 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',
+ ],
+ }
+)
diff --git a/toot.py b/toot.py
@@ -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('[email protected]', 'K2oEeDHdMEvCbAnEJjeB18sv', to_file='use…
+
+
+# # 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')
+
+
+
+
diff --git a/toot/__init__.py b/toot/__init__.py
@@ -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()
diff --git a/toot/config.py b/toot/config.py
@@ -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)
diff --git a/toot/console.py b/toot/console.py
@@ -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,…
+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()
You are viewing proxied material from vernunftzentrum.de. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.