mirror of https://gitlab.com/octtspacc/staticoso
Temp. fix for Gemini URIs; Add base for Mastodon support
This commit is contained in:
parent
28dbca3ea3
commit
e0777bd143
|
@ -21,6 +21,7 @@ Feel free to experiment with all of this stuff!
|
||||||
- [html2gmi](https://github.com/LukeEmmet/html2gmi)
|
- [html2gmi](https://github.com/LukeEmmet/html2gmi)
|
||||||
|
|
||||||
## Features roadmap
|
## Features roadmap
|
||||||
|
- [ ] ActivityPub support (Feed + embedded comments)
|
||||||
- [ ] Polished Gemtext generation
|
- [ ] Polished Gemtext generation
|
||||||
- [x] Autodetection of pages and posts
|
- [x] Autodetection of pages and posts
|
||||||
- [x] Info for posts shown on their page
|
- [x] Info for posts shown on their page
|
||||||
|
|
|
@ -23,6 +23,7 @@ except ModuleNotFoundError:
|
||||||
|
|
||||||
from Libs import htmlmin
|
from Libs import htmlmin
|
||||||
from Libs.bs4 import BeautifulSoup
|
from Libs.bs4 import BeautifulSoup
|
||||||
|
from Modules.ActivityPub import *
|
||||||
from Modules.Gemini import *
|
from Modules.Gemini import *
|
||||||
from Modules.Pug import *
|
from Modules.Pug import *
|
||||||
from Modules.Utils import *
|
from Modules.Utils import *
|
||||||
|
@ -315,8 +316,9 @@ def DelTmp():
|
||||||
for Ext in Extensions['Pages']:
|
for Ext in Extensions['Pages']:
|
||||||
for File in Path('public').rglob('*.{}'.format(Ext)):
|
for File in Path('public').rglob('*.{}'.format(Ext)):
|
||||||
os.remove(File)
|
os.remove(File)
|
||||||
for File in Path('public').rglob('*.tmp'):
|
for Dir in ('public', 'public.gmi'):
|
||||||
os.remove(File)
|
for File in Path(Dir).rglob('*.tmp'):
|
||||||
|
os.remove(File)
|
||||||
|
|
||||||
def RevSort(List):
|
def RevSort(List):
|
||||||
List.sort()
|
List.sort()
|
||||||
|
@ -480,10 +482,17 @@ def Main(Args, FeedEntries):
|
||||||
|
|
||||||
if Args.GemtextOut:
|
if Args.GemtextOut:
|
||||||
GemtextCompileList(Pages)
|
GemtextCompileList(Pages)
|
||||||
#HTML2Gemtext(
|
|
||||||
# Pages=Pages,
|
"""
|
||||||
# SiteName=SiteName,
|
MastodonSession = MastodonGetSession(
|
||||||
# SiteTagline=SiteTagline)
|
Args.MastodonURL if Args.MastodonURL else '',
|
||||||
|
Args.MastodonToken if Args.MastodonToken else '')
|
||||||
|
MastodonPosts = MastodonGetPostsFromUserID(
|
||||||
|
MastodonSession,
|
||||||
|
MastodonGetMyID(MastodonSession))
|
||||||
|
for i in MastodonPosts:
|
||||||
|
print(i['uri'], i['content'])
|
||||||
|
"""
|
||||||
|
|
||||||
DelTmp()
|
DelTmp()
|
||||||
os.system("cp -R Assets/* public/")
|
os.system("cp -R Assets/* public/")
|
||||||
|
@ -503,6 +512,8 @@ if __name__ == '__main__':
|
||||||
Parser.add_argument('--ContextParts', type=str)
|
Parser.add_argument('--ContextParts', type=str)
|
||||||
Parser.add_argument('--MarkdownExts', type=str)
|
Parser.add_argument('--MarkdownExts', type=str)
|
||||||
Parser.add_argument('--ReservedPaths', type=str)
|
Parser.add_argument('--ReservedPaths', type=str)
|
||||||
|
Parser.add_argument('--MastodonURL', type=str)
|
||||||
|
Parser.add_argument('--MastodonToken', type=str)
|
||||||
Args = Parser.parse_args()
|
Args = Parser.parse_args()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
Beautiful Soup is made available under the MIT license:
|
||||||
|
|
||||||
|
Copyright (c) 2004-2022 Leonard Richardson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Beautiful Soup incorporates code from the html5lib library, which is
|
||||||
|
also made available under the MIT license. Copyright (c) 2006-2013
|
||||||
|
James Graham and other contributors
|
||||||
|
|
||||||
|
Beautiful Soup depends on the soupsieve library, which is also made
|
||||||
|
available under the MIT license. Copyright (c) 2018 Isaac Muse
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Lorenz Diener / Mastodon.py contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
||||||
|
from .Mastodon import Mastodon, AttribAccessDict, MastodonError, MastodonVersionError, MastodonIllegalArgumentError, MastodonIOError, MastodonFileNotFoundError, MastodonNetworkError, MastodonAPIError, MastodonNotFoundError, MastodonUnauthorizedError, MastodonRatelimitError, MastodonMalformedEventError, MastodonServerError, MastodonInternalServerError, MastodonBadGatewayError, MastodonServiceUnavailableError, MastodonGatewayTimeoutError
|
||||||
|
from .streaming import StreamListener, CallbackStreamListener
|
||||||
|
|
||||||
|
__all__ = ['Mastodon', 'AttribAccessDict', 'StreamListener', 'CallbackStreamListener', 'MastodonError', 'MastodonVersionError', 'MastodonIllegalArgumentError', 'MastodonIOError', 'MastodonFileNotFoundError', 'MastodonNetworkError', 'MastodonAPIError', 'MastodonNotFoundError', 'MastodonUnauthorizedError', 'MastodonRatelimitError', 'MastodonMalformedEventError',
|
||||||
|
'MastodonServerError', 'MastodonInternalServerError', 'MastodonBadGatewayError', 'MastodonServiceUnavailableError', 'MastodonGatewayTimeoutError']
|
|
@ -0,0 +1,190 @@
|
||||||
|
"""
|
||||||
|
Handlers for the Streaming API:
|
||||||
|
https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/Streaming-API.md
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import six
|
||||||
|
from . import Mastodon
|
||||||
|
from .Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout
|
||||||
|
from requests.exceptions import ChunkedEncodingError, ReadTimeout
|
||||||
|
|
||||||
|
class StreamListener(object):
|
||||||
|
"""Callbacks for the streaming API. Create a subclass, override the on_xxx
|
||||||
|
methods for the kinds of events you're interested in, then pass an instance
|
||||||
|
of your subclass to Mastodon.user_stream(), Mastodon.public_stream(), or
|
||||||
|
Mastodon.hashtag_stream()."""
|
||||||
|
|
||||||
|
def on_update(self, status):
|
||||||
|
"""A new status has appeared! 'status' is the parsed JSON dictionary
|
||||||
|
describing the status."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_notification(self, notification):
|
||||||
|
"""A new notification. 'notification' is the parsed JSON dictionary
|
||||||
|
describing the notification."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_abort(self, err):
|
||||||
|
"""There was a connection error, read timeout or other error fatal to
|
||||||
|
the streaming connection. The exception object about to be raised
|
||||||
|
is passed to this function for reference.
|
||||||
|
|
||||||
|
Note that the exception will be raised properly once you return from this
|
||||||
|
function, so if you are using this handler to reconnect, either never
|
||||||
|
return or start a thread and then catch and ignore the exception.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_delete(self, status_id):
|
||||||
|
"""A status has been deleted. status_id is the status' integer ID."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_conversation(self, conversation):
|
||||||
|
"""A direct message (in the direct stream) has been received. conversation
|
||||||
|
contains the resulting conversation dict."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_heartbeat(self):
|
||||||
|
"""The server has sent us a keep-alive message. This callback may be
|
||||||
|
useful to carry out periodic housekeeping tasks, or just to confirm
|
||||||
|
that the connection is still open."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_stream(self, response):
|
||||||
|
"""
|
||||||
|
Handles a stream of events from the Mastodon server. When each event
|
||||||
|
is received, the corresponding .on_[name]() method is called.
|
||||||
|
|
||||||
|
response; a requests response object with the open stream for reading.
|
||||||
|
"""
|
||||||
|
event = {}
|
||||||
|
line_buffer = bytearray()
|
||||||
|
try:
|
||||||
|
for chunk in response.iter_content(chunk_size = 1):
|
||||||
|
if chunk:
|
||||||
|
for chunk_part in chunk:
|
||||||
|
chunk_part = bytearray([chunk_part])
|
||||||
|
if chunk_part == b'\n':
|
||||||
|
try:
|
||||||
|
line = line_buffer.decode('utf-8')
|
||||||
|
except UnicodeDecodeError as err:
|
||||||
|
exception = MastodonMalformedEventError("Malformed UTF-8")
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
if line == '':
|
||||||
|
self._dispatch(event)
|
||||||
|
event = {}
|
||||||
|
else:
|
||||||
|
event = self._parse_line(line, event)
|
||||||
|
line_buffer = bytearray()
|
||||||
|
else:
|
||||||
|
line_buffer.extend(chunk_part)
|
||||||
|
except ChunkedEncodingError as err:
|
||||||
|
exception = MastodonNetworkError("Server ceased communication.")
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
except MastodonReadTimeout as err:
|
||||||
|
exception = MastodonReadTimeout("Timed out while reading from server."),
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
|
||||||
|
def _parse_line(self, line, event):
|
||||||
|
if line.startswith(':'):
|
||||||
|
self.handle_heartbeat()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
key, value = line.split(': ', 1)
|
||||||
|
except:
|
||||||
|
exception = MastodonMalformedEventError("Malformed event.")
|
||||||
|
self.on_abort(exception)
|
||||||
|
raise exception
|
||||||
|
# According to the MDN spec, repeating the 'data' key
|
||||||
|
# represents a newline(!)
|
||||||
|
if key in event:
|
||||||
|
event[key] += '\n' + value
|
||||||
|
else:
|
||||||
|
event[key] = value
|
||||||
|
return event
|
||||||
|
|
||||||
|
def _dispatch(self, event):
|
||||||
|
try:
|
||||||
|
name = event['event']
|
||||||
|
data = event['data']
|
||||||
|
payload = json.loads(data, object_hook = Mastodon._Mastodon__json_hooks)
|
||||||
|
except KeyError as err:
|
||||||
|
exception = MastodonMalformedEventError('Missing field', err.args[0], event)
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
except ValueError as err:
|
||||||
|
# py2: plain ValueError
|
||||||
|
# py3: json.JSONDecodeError, a subclass of ValueError
|
||||||
|
exception = MastodonMalformedEventError('Bad JSON', data)
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
|
||||||
|
handler_name = 'on_' + name
|
||||||
|
try:
|
||||||
|
handler = getattr(self, handler_name)
|
||||||
|
except AttributeError as err:
|
||||||
|
exception = MastodonMalformedEventError('Bad event type', name)
|
||||||
|
self.on_abort(exception)
|
||||||
|
six.raise_from(
|
||||||
|
exception,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
handler(payload)
|
||||||
|
|
||||||
|
class CallbackStreamListener(StreamListener):
|
||||||
|
"""
|
||||||
|
Simple callback stream handler class.
|
||||||
|
Can optionally additionally send local update events to a separate handler.
|
||||||
|
"""
|
||||||
|
def __init__(self, update_handler = None, local_update_handler = None, delete_handler = None, notification_handler = None, conversation_handler = None):
|
||||||
|
super(CallbackStreamListener, self).__init__()
|
||||||
|
self.update_handler = update_handler
|
||||||
|
self.local_update_handler = local_update_handler
|
||||||
|
self.delete_handler = delete_handler
|
||||||
|
self.notification_handler = notification_handler
|
||||||
|
self.conversation_handler = conversation_handler
|
||||||
|
|
||||||
|
def on_update(self, status):
|
||||||
|
if self.update_handler != None:
|
||||||
|
self.update_handler(status)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.local_update_handler != None and not "@" in status["account"]["acct"]:
|
||||||
|
self.local_update_handler(status)
|
||||||
|
except Exception as err:
|
||||||
|
six.raise_from(
|
||||||
|
MastodonMalformedEventError('received bad update', status),
|
||||||
|
err
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_delete(self, deleted_id):
|
||||||
|
if self.delete_handler != None:
|
||||||
|
self.delete_handler(deleted_id)
|
||||||
|
|
||||||
|
def on_notification(self, notification):
|
||||||
|
if self.notification_handler != None:
|
||||||
|
self.notification_handler(notification)
|
||||||
|
|
||||||
|
def on_conversation(self, conversation):
|
||||||
|
if self.conversation_handler != None:
|
||||||
|
self.conversation_handler(conversation)
|
|
@ -0,0 +1,27 @@
|
||||||
|
""" ================================= |
|
||||||
|
| This file is part of |
|
||||||
|
| staticoso |
|
||||||
|
| Just a simple Static Site Generator |
|
||||||
|
| |
|
||||||
|
| Licensed under the AGPLv3 license |
|
||||||
|
| Copyright (C) 2022, OctoSpacc |
|
||||||
|
| ================================= """
|
||||||
|
|
||||||
|
from Libs.mastodon import Mastodon
|
||||||
|
from Modules.Utils import *
|
||||||
|
|
||||||
|
def MastodonGetSession(MastodonURL, MastodonToken):
|
||||||
|
return Mastodon(
|
||||||
|
api_base_url=MastodonURL,
|
||||||
|
access_token=MastodonToken)
|
||||||
|
|
||||||
|
def MastodonGetMyID(Session):
|
||||||
|
return Session.me()['id']
|
||||||
|
|
||||||
|
def MastodonGetPostsFromUserID(Session, UserID):
|
||||||
|
return Session.account_statuses(
|
||||||
|
UserID,
|
||||||
|
exclude_replies=True)
|
||||||
|
|
||||||
|
def MastodonDoPost(Session):
|
||||||
|
pass # mastodon.toot('Tooting from python using #mastodonpy !')
|
|
@ -22,11 +22,13 @@ OpenTags = (
|
||||||
'img')
|
'img')
|
||||||
|
|
||||||
def GemtextCompileList(Pages):
|
def GemtextCompileList(Pages):
|
||||||
|
Cmd = ''
|
||||||
for File, Content, Titles, Meta, HTMLContent, Description, Image in Pages:
|
for File, Content, Titles, Meta, HTMLContent, Description, Image in Pages:
|
||||||
Src = 'public/{}.html.tmp'.format(StripExt(File))
|
Src = 'public.gmi/{}.html.tmp'.format(StripExt(File))
|
||||||
WriteFile(Src, HTMLContent)
|
WriteFile(Src, HTMLContent.replace('.html', '.gmi')) # TODO: Adjust links properly..
|
||||||
Dst = 'public.gmi/{}.gmi'.format(StripExt(File))
|
Dst = 'public.gmi/{}.gmi'.format(StripExt(File))
|
||||||
os.system('cat {} | html2gmi > {}'.format(Src, Dst))
|
Cmd += 'cat "{}" | html2gmi > "{}"; '.format(Src, Dst)
|
||||||
|
os.system(Cmd)
|
||||||
|
|
||||||
def FindEarliest(Str, Items):
|
def FindEarliest(Str, Items):
|
||||||
Pos, Item = 0, ''
|
Pos, Item = 0, ''
|
||||||
|
|
Loading…
Reference in New Issue