diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..548f51a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.secret +*pycache* diff --git a/README.md b/README.md index 68aec44..de77342 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,32 @@ # mastodon.api.prototype -a mastodon api prototyping area \ No newline at end of file +A mastodon api prototyping area, a Flask interface exploration to test toots in other environments. + +## These things are dependencies + +* Mastodon.py (```$ pip3 install Mastodon.py```) +* Flask (```$ pip3 install flask```) + +## At the first time the script is running (and the mastodon api is used ...) + +Change the ```name``` and ```instance``` in the file ```api_register_app.py```. + +And run: ```$ python3 api_register_app.py``` + +## To run it + +Change the ```api_base_url``` in ```interface.py``` to the instance that you want to use. + +And then ... + +```$ python3 interface.py``` + +Browse to localhost:5000 ! :) + +## More ... + +There are a couple of API tests in the ```mastodon_api_tests.py``` file. + +## Preview + +![](Screenshot\ from\ 2018-12-05\ 00-32-36.png) \ No newline at end of file diff --git a/Screenshot from 2018-12-05 00-32-36.png b/Screenshot from 2018-12-05 00-32-36.png new file mode 100644 index 0000000..0328160 Binary files /dev/null and b/Screenshot from 2018-12-05 00-32-36.png differ diff --git a/api_register_app.py b/api_register_app.py new file mode 100644 index 0000000..80ffd57 --- /dev/null +++ b/api_register_app.py @@ -0,0 +1,13 @@ +from mastodon import Mastodon + +# Register app - only once! + +name = 'reader' +instance = 'https://post.lurk.org' + +Mastodon.create_app( + name, + api_base_url = instance, + to_file = 'api_clientcred.secret', + scopes = 'read' +) \ No newline at end of file diff --git a/interface.py b/interface.py new file mode 100644 index 0000000..85c8ac9 --- /dev/null +++ b/interface.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import flask +from mastodon import Mastodon + +""" A little test, prototype for an experimental Mastodon interface, using the Mastodon API through the Mastodon.py library. """ + +def get_timeline(): + mastodon = Mastodon( + client_id = 'api_clientcred.secret', + api_base_url = 'https://post.lurk.org' + ) + timeline = mastodon.timeline_local(max_id=None, since_id=None, limit=40) + # timeline = mastodon.timeline_public(max_id=None, since_id=None, limit=40, only_media=False) + instance = mastodon.instance() + return timeline, instance + +# Create the Flask application. +APP = flask.Flask(__name__) + +@APP.route('/', methods=['GET']) +def index(): + """ Displays the index page accessible at '/' """ + content, instance = get_timeline() + html = flask.render_template('index.html', content=content, instance=instance) + return html + +if __name__ == '__main__': + APP.debug=True + APP.run(port=5000) diff --git a/mastodon_api_tests.py b/mastodon_api_tests.py new file mode 100755 index 0000000..f5859a0 --- /dev/null +++ b/mastodon_api_tests.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 + +from mastodon import Mastodon, StreamListener +from pprint import pprint + +# This is a test environment of Mastodon.py :) +# https://mastodonpy.readthedocs.io/en/stable/ +# https://github.com/halcy/Mastodon.py + +# Install Mastodon.py through pip: +# $ pip3 install Mastodon.py + +# --- + +# Register app - only once! +# Mastodon.create_app( +# 'reader', +# api_base_url = 'https://post.lurk.org', +# to_file = 'api_clientcred.secret', +# scopes = 'read' +# ) + +# --- only api_clientcred.secret is needed --- + +# Log in - either every time, or use persisted (how does persisted work ... ?) +mastodon = Mastodon( + client_id = 'api_clientcred.secret', + api_base_url = 'https://post.lurk.org' +) + +# Bits of information about my instance. +instance = mastodon.instance() +# pprint(instance) + +instance_peers = mastodon.instance_peers() +# pprint(instance_peers) +# print(len(instance_peers)) + +instance_activity = mastodon.instance_activity() +# pprint(instance_activity) + +timeline_local = mastodon.timeline_local(max_id=None, since_id=0, limit=100) +# All endpoints that are paginated have three parameters: since_id, max_id and limit. since_id allows you to specify the smallest id you want in the returned data. max_id, similarly, allows you to specify the largest. By specifying either one (generally, only one, not both) of them you can go through pages forwards and backwards. +# https://mastodonpy.readthedocs.io/en/stable/#a-note-about-pagination +# pprint(timeline_local) +# print('\nLength of the returned timeline:', len(timeline_local)) +# 40 seems to be the max for post.lurk.org + +# for toot in timeline_local: + # print(toot.content) + +timeline_public = mastodon.timeline_public(max_id=None, since_id=None, limit=100, only_media=False) +# pprint(timeline_public) +# print('\nLength of the returned timeline:', len(timeline_public)) +# 40 seems to be the max for post.lurk.org + +next_page = mastodon.fetch_next(timeline_public) +# Fetches the next page of results of a paginated request. +# pprint(next_page) +# print('\nLength of the returned timeline:', len(next_page)) + + +# --- api_usercred.secret is needed --- + +# This is only needed once, to create the api_usercred.secret +# mastodon.log_in( +# 'mail@manettaberends.nl', +# 'password', +# to_file = 'api_usercred.secret' +# ) + +# Log in with api_usercred.secret +mastodon = Mastodon( + access_token = 'api_usercred.secret', + api_base_url = 'https://post.lurk.org' +) + +# Toot from python +# mastodon.toot('Tooting from python using #mastodonpy !') + +# Account details +account = mastodon.account_verify_credentials() +# pprint(account) + +search = mastodon.search("Tooting") +# You can only search when you're logged in +# What is the range of this search? +# Is it limited to all the instances that crossed the local instance? +# It does not really pick up statutes ... Also not recent ones. +# pprint(search) + +# --- streaming.api --- + +class listener(StreamListener): + def on_update(self, status): + print(status.content) + +# mastodon.stream_public(listener(), run_async=False, timeout=300, reconnect_async=False, reconnect_async_wait_sec=5) diff --git a/static/css/stylesheet.css b/static/css/stylesheet.css new file mode 100644 index 0000000..8db1385 --- /dev/null +++ b/static/css/stylesheet.css @@ -0,0 +1,26 @@ +body{ + background:linear-gradient(180deg, rgba(250,50,0,0.5), rgba(0,50,200,0.8)); + position: relative; + margin:1em auto; + max-width: 750px; + +} +div.status{ + padding:1em 0; + border-bottom:1px dotted black; +} + div.username{ + font-style: italic; + font-weight: bold; + float: left; + padding-right: 0.5em; + margin-top: 0.95em; + } + div.toot{ + } + div.media img{ + width: calc(50% - 4em); + margin:1em; + display: inline; + vertical-align:top; + } \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..447302d --- /dev/null +++ b/templates/base.html @@ -0,0 +1,12 @@ + + + + + {% block title %}{% endblock %} + + + +{% block content %} +{% endblock %} + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..10bc720 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block content %} +
+

Timeline explorations @ {{ instance.uri }}

+
{{ instance.stats }}
+ {% for status in content %} +
+
{{ status.account.username }}:
+
{{ status.content | safe }}
+
+ {% for media in status.media_attachments %} + {% if media.type == 'image' %} + + {% endif %} + {% endfor %} +
+
+ {% endfor %} +
+ +{% endblock %} \ No newline at end of file