feat: use public APIs and remove mastodon.py dependency
This commit is contained in:
parent
08403d912b
commit
5a1b8eaee3
5 changed files with 83 additions and 153 deletions
|
@ -5,9 +5,7 @@
|
||||||
"title": "Thoughts",
|
"title": "Thoughts",
|
||||||
"description": "Hand-picked public posts from my social feed",
|
"description": "Hand-picked public posts from my social feed",
|
||||||
"server": "https://social.ayco.io",
|
"server": "https://social.ayco.io",
|
||||||
"user": "user@mastodon.social",
|
"user_id": "0123456789"
|
||||||
"password": "ultraelectromagneticpassword",
|
|
||||||
"secret_file": "threads-masto-client.secret"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ATTRIBUTION": {
|
"ATTRIBUTION": {
|
||||||
|
|
91
mastodon.py
91
mastodon.py
|
@ -1,91 +0,0 @@
|
||||||
from mastodon import Mastodon
|
|
||||||
from . import utils
|
|
||||||
|
|
||||||
session_id = None
|
|
||||||
account_id = None
|
|
||||||
|
|
||||||
def is_public(status):
|
|
||||||
print(status)
|
|
||||||
return status['visibility'] == 'public'
|
|
||||||
|
|
||||||
def get_account_tagged_statuses(app, tag):
|
|
||||||
global account_id
|
|
||||||
mastodon = initialize_client(app)
|
|
||||||
statuses = []
|
|
||||||
try:
|
|
||||||
statuses = mastodon.account_statuses(
|
|
||||||
id=account_id,
|
|
||||||
tagged=tag,
|
|
||||||
exclude_reblogs=True
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
message = f'>>> failed to fetch statuses for ${tag}'
|
|
||||||
raise Exception(message)
|
|
||||||
|
|
||||||
# filter out not public
|
|
||||||
filtered = filter(is_public, statuses)
|
|
||||||
|
|
||||||
return list(map(lambda x: utils.clean_status(x), filtered))
|
|
||||||
|
|
||||||
def initialize_client(app):
|
|
||||||
global session_id
|
|
||||||
global account_id
|
|
||||||
mastodon = None
|
|
||||||
secret = None
|
|
||||||
try:
|
|
||||||
secret_file = open(app['secret_file'], 'r')
|
|
||||||
secret = secret_file.read()
|
|
||||||
except OSError as e:
|
|
||||||
message = '>>> No secret found.'
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
# todo, check if access_token exist in secret_file
|
|
||||||
if secret == None:
|
|
||||||
#...if token does not exist, create app:
|
|
||||||
Mastodon.create_app(
|
|
||||||
app['site_name'],
|
|
||||||
api_base_url = app['server'],
|
|
||||||
to_file = app['secret_file']
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
mastodon = Mastodon(client_id=app['secret_file'])
|
|
||||||
print('>>> Persisted new token!')
|
|
||||||
except:
|
|
||||||
message = '>>> Failed to create masto client token'
|
|
||||||
raise Exception(message)
|
|
||||||
|
|
||||||
else:
|
|
||||||
#... otherwise, reuse
|
|
||||||
try:
|
|
||||||
mastodon = Mastodon(access_token=app['secret_file'])
|
|
||||||
print('>>> Reused persisted token!')
|
|
||||||
except:
|
|
||||||
message = '>>> Persisted token did not work'
|
|
||||||
raise Exception(message)
|
|
||||||
|
|
||||||
if session_id == None:
|
|
||||||
try:
|
|
||||||
session_id = mastodon.log_in(
|
|
||||||
app['user'],
|
|
||||||
app['password'],
|
|
||||||
to_file = app['secret_file']
|
|
||||||
)
|
|
||||||
print('>>> Logged in: ', session_id)
|
|
||||||
except:
|
|
||||||
message = '>>> Failed to get mastodon session'
|
|
||||||
raise Exception(message)
|
|
||||||
else:
|
|
||||||
print('>>> Reused session: ', session_id)
|
|
||||||
|
|
||||||
if account_id == None:
|
|
||||||
try:
|
|
||||||
account = mastodon.me()
|
|
||||||
account_id = account.id
|
|
||||||
print('>>> Set account ID: ', account_id)
|
|
||||||
except:
|
|
||||||
message = '>>> Failed to get mastodon account'
|
|
||||||
raise Exception(message)
|
|
||||||
else:
|
|
||||||
print('>>> Reused account ID:', account_id)
|
|
||||||
|
|
||||||
return mastodon
|
|
|
@ -2,6 +2,4 @@ flask[async]
|
||||||
requests
|
requests
|
||||||
markdown
|
markdown
|
||||||
Flask-Caching
|
Flask-Caching
|
||||||
aiohttp
|
aiohttp
|
||||||
|
|
||||||
mastodon-py
|
|
|
@ -6,13 +6,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{{ app.title }} / {{ tag }}</title>
|
<title>{{ app.title }} / {{ tag }}</title>
|
||||||
<meta name="theme-color" content="#3054bf">
|
<meta name="theme-color" content="#3054bf">
|
||||||
{% if threads|length == 1 %}
|
|
||||||
<meta name="description" content="{{ threads[0].summary }}" />
|
|
||||||
<meta property="og:description" content="{{ threads[0].summary }}" />
|
|
||||||
{% else %}
|
|
||||||
<meta name="description" content="{{ app.description }}" />
|
<meta name="description" content="{{ app.description }}" />
|
||||||
<meta property="og:description" content="{{ app.description }}" />
|
<meta property="og:description" content="{{ app.description }}" />
|
||||||
{% endif %}
|
|
||||||
<meta name="author" content="{{ attribution.owner }}" />
|
<meta name="author" content="{{ attribution.owner }}" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:site_name" content="{{ app.site_name }}" />
|
<meta property="og:site_name" content="{{ app.site_name }}" />
|
||||||
|
|
132
threads.py
132
threads.py
|
@ -4,7 +4,7 @@ from datetime import datetime
|
||||||
from .cache import cache
|
from .cache import cache
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from . import mastodon, utils
|
from . import utils
|
||||||
|
|
||||||
threads = Blueprint('threads', __name__, template_folder='templates', static_folder='static')
|
threads = Blueprint('threads', __name__, template_folder='templates', static_folder='static')
|
||||||
|
|
||||||
|
@ -45,12 +45,44 @@ thread_ids = [
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
|
### config
|
||||||
|
def server():
|
||||||
|
return current_app.config['APPS']['threads']['server']
|
||||||
|
|
||||||
def get_attribution():
|
def get_attribution():
|
||||||
return current_app.config['ATTRIBUTION']
|
return current_app.config['ATTRIBUTION']
|
||||||
|
|
||||||
def get_app_config():
|
def get_app_config():
|
||||||
return current_app.config['APPS']['threads']
|
return current_app.config['APPS']['threads']
|
||||||
|
|
||||||
|
def get_user_id():
|
||||||
|
return current_app.config['APPS']['threads']['user_id']
|
||||||
|
|
||||||
|
### featured tags
|
||||||
|
def get_account_tagged_statuses(tag_name):
|
||||||
|
print(tag_name)
|
||||||
|
id = get_user_id()
|
||||||
|
ser = server()
|
||||||
|
url = f'{ser}/api/v1/accounts/{id}/statuses?exclude_replies=true&tagged={tag_name}'
|
||||||
|
response = requests.get(url)
|
||||||
|
statuses = response.json()
|
||||||
|
statuses = [utils.clean_status(s) for s in statuses]
|
||||||
|
return statuses
|
||||||
|
|
||||||
|
def get_tags_url():
|
||||||
|
id = get_user_id()
|
||||||
|
ser = server()
|
||||||
|
url = f'{ser}/api/v1/accounts/{id}/featured_tags'
|
||||||
|
return url
|
||||||
|
|
||||||
|
def get_featured_tags():
|
||||||
|
url = get_tags_url()
|
||||||
|
response = requests.get(url)
|
||||||
|
tags = response.json()
|
||||||
|
return tags
|
||||||
|
|
||||||
|
|
||||||
|
### middleware
|
||||||
@threads.before_request
|
@threads.before_request
|
||||||
def middleware():
|
def middleware():
|
||||||
# check current year and put ange as attribution
|
# check current year and put ange as attribution
|
||||||
|
@ -61,53 +93,7 @@ def middleware():
|
||||||
if year != attribution['year']:
|
if year != attribution['year']:
|
||||||
attribution['current_year'] = year
|
attribution['current_year'] = year
|
||||||
|
|
||||||
@threads.route('/')
|
### statuses
|
||||||
@cache.cached(timeout=300)
|
|
||||||
async def home():
|
|
||||||
statuses = await fetch_statuses()
|
|
||||||
attribution = get_attribution()
|
|
||||||
app = get_app_config()
|
|
||||||
tags = []
|
|
||||||
|
|
||||||
masto = mastodon.initialize_client(app)
|
|
||||||
|
|
||||||
# List featured hashtags
|
|
||||||
tags = masto.featured_tags()
|
|
||||||
|
|
||||||
return render_template('_home.html', threads=statuses, tags=tags, app=app, attribution=attribution, render_date=datetime.now())
|
|
||||||
|
|
||||||
|
|
||||||
@threads.route('/tag/<path:id>')
|
|
||||||
@cache.cached(timeout=300)
|
|
||||||
async def tag(id):
|
|
||||||
attribution = get_attribution()
|
|
||||||
app = get_app_config()
|
|
||||||
statuses = mastodon.get_account_tagged_statuses(app, id)
|
|
||||||
|
|
||||||
return render_template('_tag.html', threads=statuses, tag=id, app=app, attribution=attribution, render_date=datetime.now())
|
|
||||||
|
|
||||||
|
|
||||||
@threads.route('/<path:id>')
|
|
||||||
@cache.cached(timeout=300)
|
|
||||||
def thread(id):
|
|
||||||
attribution = get_attribution()
|
|
||||||
app = get_app_config()
|
|
||||||
status = fetch_thread(id)
|
|
||||||
status['summary'] = utils.clean_html(status['content']).strip()
|
|
||||||
if len(status['summary']) > 69:
|
|
||||||
status['summary'] = status['summary'][:69] + '...'
|
|
||||||
return render_template('_home.html', threads=[status], app=app, attribution=attribution, render_date=datetime.now())
|
|
||||||
|
|
||||||
@threads.route('/api')
|
|
||||||
@cache.cached(timeout=300)
|
|
||||||
async def api():
|
|
||||||
return await fetch_statuses();
|
|
||||||
|
|
||||||
@threads.route('/api/<path:id>')
|
|
||||||
@cache.cached(timeout=300)
|
|
||||||
def api_thread(id):
|
|
||||||
return fetch_thread(id)
|
|
||||||
|
|
||||||
async def get(url, session):
|
async def get(url, session):
|
||||||
try:
|
try:
|
||||||
async with session.get(url, ssl=False) as response:
|
async with session.get(url, ssl=False) as response:
|
||||||
|
@ -120,9 +106,6 @@ async def get(url, session):
|
||||||
def get_status_url(ser, id):
|
def get_status_url(ser, id):
|
||||||
return f'{ser}/api/v1/statuses/{id}'
|
return f'{ser}/api/v1/statuses/{id}'
|
||||||
|
|
||||||
def server():
|
|
||||||
return current_app.config['APPS']['threads']['server']
|
|
||||||
|
|
||||||
async def fetch_statuses():
|
async def fetch_statuses():
|
||||||
statuses = []
|
statuses = []
|
||||||
urls = [get_status_url(server(), id) for id in thread_ids]
|
urls = [get_status_url(server(), id) for id in thread_ids]
|
||||||
|
@ -149,3 +132,50 @@ def get_descendants(server, status):
|
||||||
if reply['account']['id'] == author_id and reply['in_reply_to_account_id'] == author_id:
|
if reply['account']['id'] == author_id and reply['in_reply_to_account_id'] == author_id:
|
||||||
descendants.append(utils.clean_status(reply))
|
descendants.append(utils.clean_status(reply))
|
||||||
return descendants
|
return descendants
|
||||||
|
|
||||||
|
### routes
|
||||||
|
@threads.route('/')
|
||||||
|
@cache.cached(timeout=300)
|
||||||
|
async def home():
|
||||||
|
statuses = await fetch_statuses()
|
||||||
|
attribution = get_attribution()
|
||||||
|
app = get_app_config()
|
||||||
|
tags = []
|
||||||
|
|
||||||
|
# List featured hashtags
|
||||||
|
tags = get_featured_tags()
|
||||||
|
|
||||||
|
return render_template('_home.html', threads=statuses, tags=tags, app=app, attribution=attribution, render_date=datetime.now())
|
||||||
|
|
||||||
|
|
||||||
|
@threads.route('/tag/<path:id>')
|
||||||
|
@cache.cached(timeout=300)
|
||||||
|
async def tag(id):
|
||||||
|
attribution = get_attribution()
|
||||||
|
app = get_app_config()
|
||||||
|
statuses = get_account_tagged_statuses(id)
|
||||||
|
|
||||||
|
return render_template('_tag.html', threads=statuses, tag=id, app=app, attribution=attribution, render_date=datetime.now())
|
||||||
|
|
||||||
|
|
||||||
|
@threads.route('/<path:id>')
|
||||||
|
@cache.cached(timeout=300)
|
||||||
|
def thread(id):
|
||||||
|
attribution = get_attribution()
|
||||||
|
app = get_app_config()
|
||||||
|
status = fetch_thread(id)
|
||||||
|
status['summary'] = utils.clean_html(status['content']).strip()
|
||||||
|
if len(status['summary']) > 69:
|
||||||
|
status['summary'] = status['summary'][:69] + '...'
|
||||||
|
return render_template('_home.html', threads=[status], app=app, attribution=attribution, render_date=datetime.now())
|
||||||
|
|
||||||
|
@threads.route('/api')
|
||||||
|
@cache.cached(timeout=300)
|
||||||
|
async def api():
|
||||||
|
return await fetch_statuses();
|
||||||
|
|
||||||
|
@threads.route('/api/<path:id>')
|
||||||
|
@cache.cached(timeout=300)
|
||||||
|
def api_thread(id):
|
||||||
|
return fetch_thread(id)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue