alpha2, re-added replies and reactions
@@ -8,10 +8,12 @@ import subprocess
import imghdr import arrow -from pprint import pprint - -from requests_oauthlib import OAuth1Session, oauth1_session, OAuth2Session, oauth2_session +from requests_oauthlib import OAuth1Session +from requests_oauthlib import oauth1_session +from requests_oauthlib import OAuth2Session +from requests_oauthlib import oauth2_session from oauthlib.oauth2 import BackendApplicationClient + import db import shared@@ -47,7 +49,19 @@ self.params = {
'method': 'flickr.favorites.getList', 'api_key': shared.config.get('api_flickr', 'api_key'), 'user_id': self.uid, - 'extras': 'description,geo,tags,owner_name,date_upload,url_o,url_k,url_h,url_b,url_c,url_z', + 'extras': ','.join([ + 'description', + 'geo', + 'tags', + 'owner_name', + 'date_upload', + 'url_o', + 'url_k', + 'url_h', + 'url_b', + 'url_c', + 'url_z', + ]), 'per_page': 500, # maximim 'format': 'json', 'nojsoncallback': '1',@@ -837,14 +851,18 @@
if __name__ == '__main__': logging.basicConfig(level=20) - flickr = FlickrFavs() - flickr.run() + if shared.config.has_section('api_flickr'): + flickr = FlickrFavs() + flickr.run() - fivehpx = FivehpxFavs() - fivehpx.run() + if shared.config.has_section('api_500px'): + fivehpx = FivehpxFavs() + fivehpx.run() - tumblr = TumblrFavs() - tumblr.run() + if shared.config.has_section('api_tumblr'): + tumblr = TumblrFavs() + tumblr.run() - da = DAFavs() - da.run() + if shared.config.has_section('api_deviantart'): + da = DAFavs() + da.run()
@@ -9,6 +9,7 @@ import glob
import argparse import shutil from urllib.parse import urlparse +#from urllib.parse import urljoin import asyncio from math import ceil import csv@@ -18,6 +19,10 @@ import frontmatter
import arrow import langdetect import wand.image + +#import requests +#from bs4 import BeautifulSoup +from emoji import UNICODE_EMOJI import shared import db@@ -348,6 +353,47 @@
return self._images @property + def comments(self): + comments = NoDupeContainer() + cfiles = [] + lookin = [*self.redirects, self.fname] + for d in lookin: + maybe = glob.glob( + os.path.join( + shared.config.get('dirs', 'comment'), + d, + '*.md' + ) + ) + cfiles = [*cfiles, *maybe] + for cpath in cfiles: + c = Comment(cpath) + comments.append(c.mtime, c) + return comments + + @property + def replies(self): + r = {} + for mtime, c in self.comments: + if 'webmention' == c.type: + r.update({mtime:c.tmplvars}) + return sorted(r.items()) + + @property + def reactions(self): + r = {} + for mtime, c in self.comments: + if 'webmention' == c.type: + continue + if c.type not in r: + r[c.type] = {} + r[c.type].update({mtime:c.tmplvars}) + + for icon, comments in r.items(): + r[icon] = sorted(comments.items()) + return r + + @property def exif(self): if not self.photo: return {}@@ -509,7 +555,9 @@ 'licence': self.licence,
#'sourceurl': self.sourceurl, 'is_reply': self.is_reply, 'age': int(self.published.format('YYYY')) - int(arrow.utcnow().format('YYYY')), - 'summary': self.summary + 'summary': self.summary, + 'replies': self.replies, + 'reactions': self.reactions, } return self._tmplvars@@ -894,6 +942,116 @@ def __str__(self):
tmplfile = "%s.html" % (__class__.__name__) return shared.j2.get_template(tmplfile).render({'photo': self.tmplvars}) + +class Comment(object): + def __init__(self, fpath): + logging.debug("initiating comment object from %s", fpath) + self.fpath = fpath + self.mtime = os.path.getmtime(self.fpath) + with open(self.fpath, mode='rt') as f: + self.fm = frontmatter.parse(f.read()) + self.meta, self.content = self.fm + + @property + def dt(self): + return arrow.get(self.meta.get('date')) + + @property + def html(self): + html = "%s" % (self.content) + return shared.Pandoc().convert(html) + + @property + def target(self): + t = urlparse(self.meta.get('target')) + return t.path.rstrip('/').strip('/').split('/')[-1] + + @property + def source(self): + return self.meta.get('source') + + @property + def author(self): + url = self.meta.get('author').get('url', self.source) + name = self.meta.get('author').get('name', urlparse(url).hostname) + + return { + 'name': name, + 'url': url + } + + @property + def type(self): + # caching, because calling Pandoc is expensive + if not hasattr(self, '_type'): + self._type = 'webmention' + t = self.meta.get('type', 'webmention') + if 'webmention' != t: + self._type = '★' + + if len(self.content): + maybe = shared.Pandoc('plain').convert(self.content) + if maybe in UNICODE_EMOJI: + self._type = maybe + return self._type + + @property + def tmplvars(self): + if not hasattr(self, '_tmplvars'): + self._tmplvars = { + 'author': self.author, + 'source': self.source, + 'pubtime': self.dt.format(shared.ARROWFORMAT['iso']), + 'pubdate': self.dt.format(shared.ARROWFORMAT['display']), + 'html': self.html, + 'type': self.type + } + return self._tmplvars + + def __repr__(self): + return "Comment from %s for %s" % ( + self.source, self.target + ) + + def __str__(self): + tmplfile = "%s.html" % (__class__.__name__) + return shared.j2.get_template(tmplfile).render({'comment': self.tmplvars}) + + +#class SendWebmention(object): + ## TODO def __init__(self, source, target): + ## check in gone.tsv? + ## discover endpoint + ## send webmention + ## add to DB on return + + #def run(self): + #return + + +#class ReceiveWebmention(object): + ## TODO def __init__(self, source, target): + ## pull remote + ## validate if page links to X anywhere + ## find h-entry or use root as SOURCE + ## find author in SOURCE + ## find content in SOURCE + ## save under comments/[target slug]/mtime-[from-slufigied-url].md + ## + + ## add to DB on return + #def run(self): + #return + +#def parse_received_queue(): + # iterate over DB received + +#def parse_send_queue(): + # iterate over DB needs sending + +#def webmentions(target_slug): + # find all webmentions in the relevant directory + # return mtime => Webmention hash def setup(): """ parse input parameters and add them as params section to config """
@@ -1,8 +0,0 @@
-arrow==0.10.0 -Jinja2==2.9.6 -langdetect==1.0.7 -requests==2.12.4 -requests-oauthlib==0.8.0 -sanic==0.6.0 -unicode-slugify==0.1.3 -Wand==0.4.4
@@ -9,6 +9,7 @@ import db
import shared import validators import urllib.parse +import requests if __name__ == '__main__': logging_format = "[%(asctime)s] %(process)d-%(levelname)s "@@ -79,6 +80,24 @@ # otherwise it'll become read-only for reasons I'm yet to grasp
# the actual parsing will be done at site generation time wdb = db.WebmentionQueue() wdb.queue(source,target) + + # telegram notification, if set + if shared.config.has_section('api_telegram'): + url = "https://api.telegram.org/bot%s/sendMessage" % ( + shared.config.get('api_telegram', 'api_token') + ) + data = { + 'chat_id': shared.config.get('api_telegram', 'chat_id'), + 'text': 'incoming webmention from %s to %s' % ( + source, + target + ) + } + # fire and forget + try: + requests.post(url, data=data) + except: + pass response = sanic.response.text("Accepted", status=202) return response
@@ -0,0 +1,16 @@
+from setuptools import setup, find_packages +from . import __version__ + +setup( + version=__version__, + name="nasg", + author="Peter Molnar", + author_email="hello@petermolnar.eu", + description="Not Another Static Generator - a static generator", + long_description=open('README.md').read(), + packages=['nasg'], + install_requires=['arrow', 'Jinja2', 'langdetect', 'requests', 'requests-oauthlib', 'sanic', 'unicode-slugify', 'Wand', 'emoji', 'html5lib', 'BeautifulSoup'], + url='https://github.com/petermolnar/nasg', + license=open('LICENCE').read(), + include_package_data=True, +)
@@ -0,0 +1,26 @@
+<li class="h-entry p-comment"> + <span> + <time class="dt-published" datetime="{{ comment.pubtime }}"> + {{ comment.pubdate }} + </time> + </span> + <span class="p-author h-card"> + {% if comment.author.url %} + <a class="url u-url" href="{{ comment.author.url }}"> + <span class="p-name fn">{{ comment.author.name }}</span> + </a> + {% else %} + <span class="p-name fn">{{ comment.author.name }}</span> + {% endif %} + </span> +{% if 'webmention' == comment.type %} + <span class="source"> + <svg class="icon"><use xlink:href="#icon-link"></use></svg> + <a class="u-url" href="{{ comment.source }}">{{ comment.source }}</a> + </span> +{% else %} + <span class="reaction"> + <a class="u-url" href="{{ comment.source }}">{{ comment.type }} </a> + </span> +{% endif %} +</li>
@@ -59,6 +59,32 @@ Licensed under <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International</a>. You are free to share if you link back here for non commercial use, but you can't publish any altered versions of it. For commercial use please contact the author.
{% endif %} </p> </footer> + + <section class="replies"> + <h6><a name="replies"></a>Replies</h6> + <ol> + {% for mtime, comment in post.replies %} + {% include 'Comment.html' %} + {% endfor %} + </ol> + </section> + + <section class="reactions"> + <h6><a name="reactions"></a>Reactions</h6> + <dl> + {% for character, comments in post.reactions.items() %} + <dt>{{ character }}</dt> + <dd> + <ul> + {% for mtime, comment in comments %} + {% include 'Comment.html' %} + {% endfor %} + </ul> + </dd> + {% endfor %} + </dl> + </section> + </article> </section>
@@ -535,6 +535,42 @@ .search-section li {
margin: 1rem 0.6rem; } +.replies ol { + margin: 0 0 0 1rem; +} + +.replies li { + margin: 0 0 1rem 0; +} + +.replies li .p-author:before { + content: '\00B7'; + margin: 0 0.3rem; +} + +.replies li time { + font-size: 0.8rem; +} + +.replies li .source { + display: block; +} + +.reactions dl dt, +.reactions dl ul time { + display:none; + visibility: hidden; +} + +.reactions dl ul, +.reactions dl ul li { + list-style-type: none; + display: inline-block; + padding: 0; + margin: 0 1rem 0 0; +} + + /* above is mobile first; this is the desktop */ @media all and (min-width: 50rem) {