forked from platypush/platypush
Resolve "Mastodon integration"
This commit is contained in:
parent
6db070db1c
commit
acdc636b1f
8 changed files with 1764 additions and 2 deletions
|
@ -5,6 +5,10 @@ Given the high speed of development in the first phase, changes are being report
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added Mastodon integration.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `switchbot.status` method in case of virtual devices.
|
||||
|
|
5
docs/source/platypush/plugins/mastodon.rst
Normal file
5
docs/source/platypush/plugins/mastodon.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``mastodon``
|
||||
============
|
||||
|
||||
.. automodule:: platypush.plugins.mastodon
|
||||
:members:
|
|
@ -72,6 +72,7 @@ Plugins
|
|||
platypush/plugins/luma.oled.rst
|
||||
platypush/plugins/mail.imap.rst
|
||||
platypush/plugins/mail.smtp.rst
|
||||
platypush/plugins/mastodon.rst
|
||||
platypush/plugins/media.chromecast.rst
|
||||
platypush/plugins/media.gstreamer.rst
|
||||
platypush/plugins/media.kodi.rst
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import datetime
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, Optional
|
||||
|
||||
from flask import Blueprint, request, abort, jsonify
|
||||
|
||||
|
@ -18,7 +17,7 @@ __routes__ = [
|
|||
|
||||
|
||||
@auth.route('/auth', methods=['POST'])
|
||||
def auth_endpoint() -> Dict[str, Optional[str]]:
|
||||
def auth_endpoint():
|
||||
"""
|
||||
Authentication endpoint. It validates the user credentials provided over a JSON payload with the following
|
||||
structure:
|
||||
|
|
46
platypush/common/ngrok/__init__.py
Normal file
46
platypush/common/ngrok/__init__.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from threading import RLock
|
||||
from typing import Optional
|
||||
|
||||
from platypush.config import Config
|
||||
from platypush.context import get_backend, get_plugin
|
||||
|
||||
_app_tunnel_lock = RLock()
|
||||
_app_tunnel_url: Optional[str] = None
|
||||
|
||||
|
||||
def _get_http_port() -> int:
|
||||
http = None
|
||||
if Config.get('backend.http'):
|
||||
http = get_backend('http')
|
||||
|
||||
assert http, 'The http backend is required in order to subscribe to notifications'
|
||||
return http.port
|
||||
|
||||
|
||||
def create_ngrok_tunnel() -> str:
|
||||
"""
|
||||
This method creates an ngrok tunnel for the local web application,
|
||||
useful to register public HTTPS callback URLs on the fly from plugins
|
||||
and backends.
|
||||
"""
|
||||
global _app_tunnel_url
|
||||
with _app_tunnel_lock:
|
||||
if _app_tunnel_url:
|
||||
return _app_tunnel_url
|
||||
|
||||
local_port = _get_http_port()
|
||||
|
||||
ngrok = None
|
||||
if Config.get('ngrok'):
|
||||
ngrok = get_plugin('ngrok')
|
||||
|
||||
assert ngrok, 'The ngrok plugin is required in order to subscribe to notifications'
|
||||
tunnel_response = ngrok.create_tunnel(
|
||||
resource=local_port,
|
||||
protocol='http',
|
||||
bind_tls=True,
|
||||
).output
|
||||
|
||||
_app_tunnel_url = tunnel_response.get('url')
|
||||
assert _app_tunnel_url, 'Unable to create an ngrok tunnel'
|
||||
return _app_tunnel_url
|
1502
platypush/plugins/mastodon/__init__.py
Normal file
1502
platypush/plugins/mastodon/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
4
platypush/plugins/mastodon/manifest.yaml
Normal file
4
platypush/plugins/mastodon/manifest.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
manifest:
|
||||
events: {}
|
||||
package: platypush.plugins.mastodon
|
||||
type: plugin
|
201
platypush/schemas/mastodon.py
Normal file
201
platypush/schemas/mastodon.py
Normal file
|
@ -0,0 +1,201 @@
|
|||
from random import randint
|
||||
|
||||
from marshmallow import fields, missing
|
||||
from marshmallow.schema import Schema
|
||||
from marshmallow.validate import OneOf
|
||||
|
||||
from platypush.schemas import DateTime, Date, StrippedString
|
||||
|
||||
notification_types = ['follow', 'favourite', 'reblog', 'mention', 'poll', 'follow_request']
|
||||
list_reply_policies = ['none', 'followed', 'list']
|
||||
|
||||
|
||||
class MastodonSchema(Schema):
|
||||
pass
|
||||
|
||||
|
||||
class MastodonAccountSchema(MastodonSchema):
|
||||
id = fields.String(
|
||||
dump_only=True,
|
||||
metadata=dict(
|
||||
example=''.join([f'{randint(1, 9)}' for _ in range(18)]),
|
||||
)
|
||||
)
|
||||
|
||||
username = fields.String(
|
||||
metadata=dict(
|
||||
example='admin',
|
||||
)
|
||||
)
|
||||
|
||||
url = fields.URL()
|
||||
avatar = fields.URL()
|
||||
header = fields.URL()
|
||||
followers_count = fields.Int(dump_only=True)
|
||||
following_count = fields.Int(dump_only=True)
|
||||
note = fields.String()
|
||||
display_name = StrippedString(
|
||||
metadata=dict(
|
||||
example='Name Surname',
|
||||
)
|
||||
)
|
||||
|
||||
locked = fields.Boolean()
|
||||
bot = fields.Boolean()
|
||||
discoverable = fields.Boolean()
|
||||
group = fields.Boolean()
|
||||
created_at = DateTime(dump_only=True)
|
||||
last_status_at = DateTime(dump_only=True)
|
||||
|
||||
|
||||
class MastodonFeaturedHashtagSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
name = fields.String()
|
||||
statuses_count = fields.Int(dump_only=True)
|
||||
last_status = DateTime(dump_only=True)
|
||||
|
||||
|
||||
class MastodonHashtagHistorySchema(MastodonSchema):
|
||||
day = Date()
|
||||
uses = fields.Int()
|
||||
accounts = fields.Int()
|
||||
|
||||
|
||||
class MastodonHashtagSchema(MastodonSchema):
|
||||
name = fields.String(metadata=dict(example='hashtag'))
|
||||
url = fields.URL()
|
||||
history = fields.Nested(
|
||||
MastodonHashtagHistorySchema, many=True, default=missing
|
||||
)
|
||||
|
||||
|
||||
class MastodonMediaSchema(MastodonSchema):
|
||||
id = fields.String(dump_only=True)
|
||||
description = StrippedString()
|
||||
type = fields.String(dump_only=True, metadata={'example': 'image'})
|
||||
url = fields.URL(dump_only=True)
|
||||
preview_url = fields.URL(dump_only=True)
|
||||
remote_url = fields.URL(dump_only=True)
|
||||
preview_remote_url = fields.URL(dump_only=True)
|
||||
meta = fields.Dict()
|
||||
|
||||
|
||||
class MastodonStatusSchema(MastodonSchema):
|
||||
id = fields.String(
|
||||
dump_only=True,
|
||||
metadata=dict(
|
||||
example=''.join([f'{randint(1, 9)}' for _ in range(18)]),
|
||||
)
|
||||
)
|
||||
|
||||
in_reply_to_id = fields.String(
|
||||
dump_only=True,
|
||||
allow_none=True,
|
||||
metadata=dict(
|
||||
example=''.join([f'{randint(1, 9)}' for _ in range(18)]),
|
||||
)
|
||||
)
|
||||
|
||||
in_reply_to_account_id = fields.String(
|
||||
dump_only=True,
|
||||
allow_none=True,
|
||||
metadata=dict(
|
||||
example=''.join([f'{randint(1, 9)}' for _ in range(18)]),
|
||||
)
|
||||
)
|
||||
|
||||
url = fields.URL(dump_only=True)
|
||||
content = fields.String(allow_none=False)
|
||||
account = fields.Nested(MastodonAccountSchema, dump_only=True)
|
||||
attachments = fields.Nested(
|
||||
MastodonMediaSchema,
|
||||
many=True,
|
||||
dump_only=True,
|
||||
attribute='media_attachments',
|
||||
)
|
||||
hashtags = fields.Nested(
|
||||
MastodonHashtagSchema, many=True,
|
||||
attribute='tags', dump_only=True
|
||||
)
|
||||
|
||||
replies_count = fields.Int(dump_only=True)
|
||||
reblogs_count = fields.Int(dump_only=True)
|
||||
favourites_count = fields.Int(dump_only=True)
|
||||
|
||||
sensitive = fields.Boolean()
|
||||
favourited = fields.Boolean()
|
||||
reblogged = fields.Boolean()
|
||||
muted = fields.Boolean()
|
||||
bookmarked = fields.Boolean()
|
||||
pinned = fields.Boolean()
|
||||
|
||||
created_at = DateTime(dump_only=True)
|
||||
|
||||
|
||||
class MastodonSearchSchema(MastodonSchema):
|
||||
accounts = fields.Nested(MastodonAccountSchema, many=True)
|
||||
statuses = fields.Nested(MastodonStatusSchema, many=True)
|
||||
hashtags = fields.Nested(MastodonHashtagSchema, many=True)
|
||||
|
||||
|
||||
class MastodonAccountCreationSchema(MastodonSchema):
|
||||
access_token = fields.String(dump_only=True)
|
||||
token_type = fields.String(dump_only=True, metadata={'example': 'Bearer'})
|
||||
scope = fields.String(dump_only=True, metadata={'example': 'read write follow push'})
|
||||
created_at = DateTime(dump_only=True)
|
||||
|
||||
|
||||
class MastodonAccountListSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
title = StrippedString()
|
||||
|
||||
|
||||
class MastodonFilterSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
phrase = StrippedString()
|
||||
whole_word = fields.Boolean()
|
||||
irreversible = fields.Boolean()
|
||||
expires_at = DateTime(allow_none=True)
|
||||
context = fields.List(
|
||||
fields.String(validate=OneOf(['home', 'notifications', 'public', 'thread'])),
|
||||
metadata={
|
||||
'example': 'Which context(s) this filter applies to. '
|
||||
'Possible values: home, notifications, public, thread',
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class MastodonConversationSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
unread = fields.Boolean()
|
||||
accounts = fields.Nested(MastodonAccountSchema, many=True)
|
||||
last_status = fields.Nested(MastodonStatusSchema)
|
||||
|
||||
|
||||
class MastodonListSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
title = StrippedString()
|
||||
replies_policy = fields.String(validate=OneOf(list_reply_policies))
|
||||
|
||||
|
||||
class MastodonMentionSchema(MastodonSchema):
|
||||
id = fields.Int(dump_only=True)
|
||||
username = StrippedString(metadata=dict(example='user'))
|
||||
url = fields.URL(metadata=dict(example='https://mastodon.social/@user'))
|
||||
|
||||
|
||||
class MastodonNotificationSchema(MastodonSchema):
|
||||
id = fields.String(dump_only=True)
|
||||
type = fields.String(validate=OneOf(notification_types))
|
||||
account = fields.Nested(MastodonAccountSchema)
|
||||
status = fields.Nested(MastodonStatusSchema)
|
||||
mention = fields.Nested(MastodonMentionSchema)
|
||||
created_at = DateTime(dump_only=True)
|
||||
|
||||
|
||||
class MastodonSubscriptionNotificationTypes(MastodonSchema):
|
||||
follow = fields.Boolean()
|
||||
reblog = fields.Boolean()
|
||||
mention = fields.Boolean()
|
||||
favourite = fields.Boolean()
|
||||
poll = fields.Boolean()
|
Loading…
Reference in a new issue