`twitfetch.py` fetches the threads defined in `tweets.txt`, and then the tmux.html has an example (will likely be made a macro in the future)...main
@@ -14,6 +14,10 @@ vnu.jar_$(VNU_RELEASE).zip: | |||
vnu.jar: vnu.jar_$(VNU_RELEASE).zip | |||
unzip -p $< dist/vnu.jar > $@ || (rm "$@"; false) | |||
.PHONY: | |||
test: | |||
ls fixtures/* encthenet_plugins.py | entr python -m unittest encthenet_plugins | |||
.PHONY: | |||
validate: vnu.jar | |||
hyde gen | |||
@@ -91,6 +91,34 @@ immediate children. Add a `>` to restrict it. | |||
Hack to make a CSS only clickable be exposed in order to handle the nav bar: | |||
https://stackoverflow.com/questions/6019845/show-hide-div-on-click-with-css | |||
List of filters: `jinja2/filters.py` (`FILTERS`) and `hyde/ext/templates/jinja.py` (`self.env.filters`) | |||
Notes on Twitter | |||
================ | |||
A Python module `twauth.py` stores credentials. It should contain | |||
a memember `twit_argskw` that is a tuple of (args, kwargs). These will be used | |||
as arguments to the Twitter instance. Here is a sample: | |||
``` | |||
from twitter import OAuth | |||
tok = 'thetoken' | |||
tokkey = 'tokenkey' | |||
apikey = 'apikeyfromrunningthis' | |||
apisec = 'apisecretkey' | |||
twit_argskw = (), dict(auth=OAuth(tok, tokkey, apikey, apisec)) | |||
__all__ = [ 'twit_argskw' ] | |||
if __name__ == '__main__': | |||
from twitter import oauth_dance | |||
oauth_dance('pytwscr', tok, tokkey) | |||
``` | |||
TODO | |||
==== | |||
@@ -4,3 +4,5 @@ description: Blog posts in progress | |||
extends: drafts.j2 | |||
listable: false | |||
--- | |||
Empty |
@@ -0,0 +1,20 @@ | |||
--- | |||
title: tmux beginners guide | |||
description: Quick simple beginners guide to using tmux | |||
created: !!timestamp '2021-01-01' | |||
extends: drafts.j2 | |||
listable: false | |||
time: 12:00 PM | |||
tags: | |||
- tweets | |||
--- | |||
<p>Originally Posted: {{ site.content.resource_from_relative_path('twitter/1556349580932833280.yaml').meta.created_at }}</p> | |||
{% for twdata in get_tweet_thread(1556349580932833280, 1556353959723356160) %} | |||
{{ twdata.meta | gettweettext }} | |||
{% for media in twdata.meta.extended_entities.media %} | |||
<p> | |||
<img src="/media/twitter/{{ (media.media_url | make_path).name }}" alt="{{ media.ext_alt_text | attr_escape }}"> | |||
</p> | |||
{% endfor %} | |||
{% endfor %} |
@@ -0,0 +1,3 @@ | |||
--- | |||
{"created_at": "Sun Aug 07 18:40:30 +0000 2022", "id": 1556349580932833280, "id_str": "1556349580932833280", "full_text": "@0xKruzr First, here's the .tmux.conf I use (alt text to copy/paste).\n\nThis remaps the control key from ctrl-b (^B) to ctrl-a which is more natural for me.\n\nNext is split windows, to have two windows side by side: ^A %, to have top/bottom: ^A \". https://t.co/3jPtg5Z41z", "truncated": false, "display_text_range": [9, 245], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 (same handle at IG, Masto coming soon)", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": [], "media": [{"id": 1556348891120877569, "id_str": "1556348891120877569", "indices": [246, 269], "media_url": "http://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "media_url_https": "https://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "url": "https://t.co/3jPtg5Z41z", "display_url": "pic.twitter.com/3jPtg5Z41z", "expanded_url": "https://twitter.com/encthenet/status/1556349580932833280/photo/1", "type": "photo", "sizes": {"large": {"w": 714, "h": 712, "resize": "fit"}, "small": {"w": 680, "h": 678, "resize": "fit"}, "thumb": {"w": 150, "h": 150, "resize": "crop"}, "medium": {"w": 714, "h": 712, "resize": "fit"}}}]}, "extended_entities": {"media": [{"id": 1556348891120877569, "id_str": "1556348891120877569", "indices": [246, 269], "media_url": "http://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "media_url_https": "https://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "url": "https://t.co/3jPtg5Z41z", "display_url": "pic.twitter.com/3jPtg5Z41z", "expanded_url": "https://twitter.com/encthenet/status/1556349580932833280/photo/1", "type": "photo", "sizes": {"large": {"w": 714, "h": 712, "resize": "fit"}, "small": {"w": 680, "h": 678, "resize": "fit"}, "thumb": {"w": 150, "h": 150, "resize": "crop"}, "medium": {"w": 714, "h": 712, "resize": "fit"}}, "ext_alt_text": "unbind C-b\nset -g prefix C-a\nbind C-a send-prefix\n\nbind ^L refresh-client\nset-window-option -g mode-keys vi\n\nbind < resize-pane -L 4\nbind > resize-pane -R 4\nbind - resize-pane -D 4\nbind + resize-pane -U 4\n\nset -g mode-mouse off\nset -g mouse-resize-pane off\nset -g mouse-select-pane off\nset -g mouse-select-window off\n\nbind k select-pane -U\nbind j select-pane -D\nbind h select-pane -L\nbind l select-pane -R\n"}]}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556120685713522690, "in_reply_to_status_id_str": "1556120685713522690", "in_reply_to_user_id": 5424492, "in_reply_to_user_id_str": "5424492", "in_reply_to_screen_name": "0xKruzr", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1870, "friends_count": 1956, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231707, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130080, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 1, "favorite_count": 3, "favorited": false, "retweeted": false, "possibly_sensitive": false, "possibly_sensitive_appealable": false, "lang": "en"} | |||
--- |
@@ -0,0 +1,3 @@ | |||
--- | |||
{"created_at": "Sun Aug 07 18:42:25 +0000 2022", "id": 1556350064749998081, "id_str": "1556350064749998081", "full_text": "@0xKruzr To move between windows, ^A then one of h (up) j (left) k (right) l (ell down), aka the vi movement keys, or you can use arrow keys.\n\nTo resize the current window, ^A < or > or + or -.\n\nTo swap two windows (to move them around), ^A { or ^A }.", "truncated": false, "display_text_range": [9, 257], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 Is Thankful for the Cyrus SASL Project \ud83e\udd83", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": []}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556349580932833280, "in_reply_to_status_id_str": "1556349580932833280", "in_reply_to_user_id": 1972539134, "in_reply_to_user_id_str": "1972539134", "in_reply_to_screen_name": "encthenet", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1870, "friends_count": 1956, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231707, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130080, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 0, "favorite_count": 1, "favorited": false, "retweeted": false, "lang": "en"} | |||
--- |
@@ -0,0 +1,3 @@ | |||
--- | |||
{"created_at": "Sun Aug 07 18:44:45 +0000 2022", "id": 1556350651805798401, "id_str": "1556350651805798401", "full_text": "@0xKruzr To copy between windows, ^A [. Then move the cursor to where you want to start, hit space to start selection, move to the end, hit enter. Now that you have your selection, to paste it, ^A ].\n\nTo create another workspace in the same session ^A c. To switch workspaces, ^A num.", "truncated": false, "display_text_range": [9, 287], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 Is Thankful for the Cyrus SASL Project \ud83e\udd83", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": []}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556350064749998081, "in_reply_to_status_id_str": "1556350064749998081", "in_reply_to_user_id": 1972539134, "in_reply_to_user_id_str": "1972539134", "in_reply_to_screen_name": "encthenet", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1870, "friends_count": 1956, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231707, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130080, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 0, "favorite_count": 1, "favorited": false, "retweeted": false, "lang": "en"} | |||
--- |
@@ -0,0 +1,3 @@ | |||
--- | |||
{"created_at": "Sun Aug 07 18:45:16 +0000 2022", "id": 1556350779216130048, "id_str": "1556350779216130048", "full_text": "@0xKruzr Note that my window/workspace terminology doesn't line up w/ tmux's, but makes more sense to me.\n\nfin.", "truncated": false, "display_text_range": [9, 111], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 (same handle at IG, Masto coming soon)", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": []}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556350651805798401, "in_reply_to_status_id_str": "1556350651805798401", "in_reply_to_user_id": 1972539134, "in_reply_to_user_id_str": "1972539134", "in_reply_to_screen_name": "encthenet", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1870, "friends_count": 1956, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231707, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130080, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 0, "favorite_count": 1, "favorited": false, "retweeted": false, "lang": "en"} | |||
--- |
@@ -0,0 +1,3 @@ | |||
--- | |||
{"created_at": "Sun Aug 07 18:57:54 +0000 2022", "id": 1556353959723356160, "id_str": "1556353959723356160", "full_text": "@0xKruzr Oh, another thing. You have have multiple terminals connected to the same session (tmux attach). This is great for pair programming, so you both don't have to crowd around a terminal and swap keyboards, each can be on their own laptop, or now w/ WFH, be far away.", "truncated": false, "display_text_range": [9, 274], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 (same handle at IG, Masto coming soon)", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": []}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556350779216130048, "in_reply_to_status_id_str": "1556350779216130048", "in_reply_to_user_id": 1972539134, "in_reply_to_user_id_str": "1972539134", "in_reply_to_screen_name": "encthenet", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1870, "friends_count": 1956, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231707, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130080, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 0, "favorite_count": 1, "favorited": false, "retweeted": false, "lang": "en"} | |||
--- |
@@ -1,9 +1,134 @@ | |||
from jinja2 import contextfilter, environmentfilter | |||
from jinja2 import contextfilter, contextfunction, environmentfilter | |||
from jinja2.ext import Extension | |||
import functools | |||
import json | |||
import pathlib | |||
import re | |||
import unittest | |||
import hyde.model | |||
from io import StringIO | |||
from lxml import etree | |||
#from commando.util import getLoggerWithNullHandler | |||
#from logging import DEBUG | |||
#logger = getLoggerWithNullHandler('hyde.server') | |||
#logger.setLevel(DEBUG) | |||
def tbwrapper(f): | |||
@functools.wraps(f) | |||
def wrapper(*args, **kwargs): | |||
try: | |||
return f(*args, **kwargs) | |||
except: | |||
import traceback | |||
traceback.print_exc() | |||
raise | |||
return wrapper | |||
@contextfunction | |||
@tbwrapper | |||
def do_get_tweet_thread(context, start, stop): | |||
#print('gtt:', repr(start), repr(stop)) | |||
nodes = [] | |||
nextnode = stop | |||
while True: | |||
#print('proc:', nextnode) | |||
curnode = context['site'].content.node_from_relative_path( | |||
'twitter').get_resource('%d.yaml' % nextnode) | |||
#print('cn:', repr(curnode)) | |||
nodes.append(curnode) | |||
if nextnode == start: | |||
break | |||
nextnode = curnode.meta.in_reply_to_status_id | |||
return iter(reversed(nodes)) | |||
@contextfilter | |||
def do_attr_escape(context, value): | |||
#print('ae:', repr(value)) | |||
# jinja2 requires <, >, and new line to be escaped, which isn't | |||
# required per html spec: | |||
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 | |||
return value.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>').replace('\n', ' ') | |||
@contextfilter | |||
def make_path(context, value): | |||
#print('mp:', repr(value)) | |||
return pathlib.PurePosixPath(value) | |||
@contextfilter | |||
def do_gettweettext(context, value): | |||
if isinstance(value, str): | |||
raise TypeError('got string expected tweet object') | |||
#print('rd:', type(value), repr(value), type(value.entities)) | |||
if hasattr(value, 'extended_entities'): | |||
mediaindices = [ x.indices for x in value.extended_entities.media ] | |||
else: | |||
mediaindices = [ ] | |||
mentionsindices = [ x.indices for x in value.entities.user_mentions ] | |||
delindices = mediaindices + mentionsindices | |||
text = value.full_text | |||
for start, stop in reversed(sorted(delindices)): | |||
text = text[:start] + text[stop:] | |||
text = text.strip() | |||
return ''.join(('<p>%s</p>' % x for x in text.split('\n') if x)) | |||
class TweetExtension(Extension): | |||
""" | |||
A wrapper around the rellinktoabs filter for syntactic sugar. | |||
""" | |||
tags = { 'gettweettext' } | |||
def __init__(self, env): | |||
super(TweetExtension, self).__init__(env) | |||
env.filters['gettweettext'] = do_gettweettext | |||
env.filters['make_path'] = make_path | |||
env.filters['attr_escape'] = do_attr_escape | |||
env.globals['get_tweet_thread'] = do_get_tweet_thread | |||
def parse(self, parser): | |||
""" | |||
Parses the statements and defers to the callback | |||
for rellinktoabs processing. | |||
""" | |||
lineno = next(parser.stream).lineno | |||
body = parser.parse_statements(['name:endcleantweet'], drop_needle=True) | |||
return nodes.CallBlock( | |||
self.call_method('_render_cleantweet'), | |||
[], [], body).set_lineno(lineno) | |||
def _render_cleantweet(self, caller=None): | |||
""" | |||
Calls the cleantweet filter to transform the output. | |||
""" | |||
if not caller: | |||
return '' | |||
output = caller().strip() | |||
return do_gettweettext(self.environment, output) | |||
@contextfilter | |||
def rellinktoabs(context, value): | |||
env = context.environment | |||
@@ -82,3 +207,36 @@ class RelLinktoAbs(Extension): | |||
return '' | |||
output = caller().strip() | |||
return rellinktoabs(self.environment, output) | |||
class Tests(unittest.TestCase): | |||
_fnameparser = re.compile('funcs_(.*)_[^_]+\\.json') | |||
def test_fixtures(self): | |||
for i in sorted(pathlib.Path('fixtures').glob( | |||
'funcs_*_*.json')): | |||
with self.subTest(file=i): | |||
mat = self._fnameparser.match(i.name) | |||
self.assertIsNotNone(mat) | |||
with i.open() as fp: | |||
obj = json.load(fp) | |||
if obj['do_expando']: | |||
obj['args'][1] = hyde.model.Expando( | |||
obj['args'][1]) | |||
fun = globals()[mat.group(1)] | |||
self.assertEqual(fun(*obj['args']), obj['res']) | |||
def test_errors(self): | |||
# make sure we get a more useful error message | |||
with self.assertRaises(TypeError): | |||
do_gettweettext(None, 'foo') | |||
def test_basepath(self): | |||
v = 'https://foo/bar/baz' | |||
self.assertEqual(make_path(None, v), pathlib.PurePosixPath(v)) | |||
def test_attr_escape(self): | |||
self.assertEqual(do_attr_escape(None, 'some & < > \n\n ""'), 'some & < > ""') |
@@ -0,0 +1,5 @@ | |||
{ | |||
"do_expando": true, | |||
"args": [ null, {"created_at": "Sun Aug 07 18:40:30 +0000 2022", "id": 1556349580932833280, "id_str": "1556349580932833280", "full_text": "@0xKruzr First, here's the .tmux.conf I use (alt text to copy/paste).\n\nThis remaps the control key from ctrl-b (^B) to ctrl-a which is more natural for me.\n\nNext is split windows, to have two windows side by side: ^A %, to have top/bottom: ^A \". https://t.co/3jPtg5Z41z", "truncated": false, "display_text_range": [9, 245], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 Is Thankful for the Cyrus SASL Project \ud83e\udd83", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": [], "media": [{"id": 1556348891120877569, "id_str": "1556348891120877569", "indices": [246, 269], "media_url": "http://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "media_url_https": "https://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "url": "https://t.co/3jPtg5Z41z", "display_url": "pic.twitter.com/3jPtg5Z41z", "expanded_url": "https://twitter.com/encthenet/status/1556349580932833280/photo/1", "type": "photo", "sizes": {"large": {"w": 714, "h": 712, "resize": "fit"}, "small": {"w": 680, "h": 678, "resize": "fit"}, "thumb": {"w": 150, "h": 150, "resize": "crop"}, "medium": {"w": 714, "h": 712, "resize": "fit"}}}]}, "extended_entities": {"media": [{"id": 1556348891120877569, "id_str": "1556348891120877569", "indices": [246, 269], "media_url": "http://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "media_url_https": "https://pbs.twimg.com/media/FZlDEhHVQAEj5eA.png", "url": "https://t.co/3jPtg5Z41z", "display_url": "pic.twitter.com/3jPtg5Z41z", "expanded_url": "https://twitter.com/encthenet/status/1556349580932833280/photo/1", "type": "photo", "sizes": {"large": {"w": 714, "h": 712, "resize": "fit"}, "small": {"w": 680, "h": 678, "resize": "fit"}, "thumb": {"w": 150, "h": 150, "resize": "crop"}, "medium": {"w": 714, "h": 712, "resize": "fit"}}, "ext_alt_text": "unbind C-b\nset -g prefix C-a\nbind C-a send-prefix\n\nbind ^L refresh-client\nset-window-option -g mode-keys vi\n\nbind < resize-pane -L 4\nbind > resize-pane -R 4\nbind - resize-pane -D 4\nbind + resize-pane -U 4\n\nset -g mode-mouse off\nset -g mouse-resize-pane off\nset -g mouse-select-pane off\nset -g mouse-select-window off\n\nbind k select-pane -U\nbind j select-pane -D\nbind h select-pane -L\nbind l select-pane -R\n"}]}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556120685713522690, "in_reply_to_status_id_str": "1556120685713522690", "in_reply_to_user_id": 5424492, "in_reply_to_user_id_str": "5424492", "in_reply_to_screen_name": "0xKruzr", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1867, "friends_count": 1959, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231737, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130160, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 1, "favorite_count": 3, "favorited": false, "retweeted": false, "possibly_sensitive": false, "possibly_sensitive_appealable": false, "lang": "en"} ], | |||
"res": "<p>First, here's the .tmux.conf I use (alt text to copy/paste).</p><p>This remaps the control key from ctrl-b (^B) to ctrl-a which is more natural for me.</p><p>Next is split windows, to have two windows side by side: ^A %, to have top/bottom: ^A \".</p>" | |||
} |
@@ -0,0 +1,5 @@ | |||
{ | |||
"do_expando": true, | |||
"args": [ null, {"created_at": "Sun Aug 07 18:42:25 +0000 2022", "id": 1556350064749998081, "id_str": "1556350064749998081", "full_text": "@0xKruzr To move between windows, ^A then one of h (up) j (left) k (right) l (ell down), aka the vi movement keys, or you can use arrow keys.\n\nTo resize the current window, ^A < or > or + or -.\n\nTo swap two windows (to move them around), ^A { or ^A }.", "truncated": false, "display_text_range": [9, 257], "entities": {"hashtags": [], "symbols": [], "user_mentions": [{"screen_name": "0xKruzr", "name": "\ud83c\udd79 Is Thankful for the Cyrus SASL Project \ud83e\udd83", "id": 5424492, "id_str": "5424492", "indices": [0, 8]}], "urls": []}, "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "in_reply_to_status_id": 1556349580932833280, "in_reply_to_status_id_str": "1556349580932833280", "in_reply_to_user_id": 1972539134, "in_reply_to_user_id_str": "1972539134", "in_reply_to_screen_name": "encthenet", "user": {"id": 1972539134, "id_str": "1972539134", "name": "John-Mark Gurney \ud83d\uddd1\ufe0f\ud83d\udd25", "screen_name": "encthenet", "location": "Oakland, CA", "description": "he/they FreeBSD developer. consultant. documentation! crypto means cryptography. open dms. tech is inherently political. Do your part, encrypt the Net!", "url": null, "entities": {"description": {"urls": []}}, "protected": false, "followers_count": 1867, "friends_count": 1959, "listed_count": 136, "created_at": "Sat Oct 19 18:59:42 +0000 2013", "favourites_count": 231737, "utc_offset": null, "time_zone": null, "geo_enabled": true, "verified": false, "statuses_count": 130160, "lang": null, "contributors_enabled": false, "is_translator": false, "is_translation_enabled": false, "profile_background_color": "352726", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/622190067939827712/txSkhH63_normal.jpg", "profile_banner_url": "https://pbs.twimg.com/profile_banners/1972539134/1591081516", "profile_image_extensions_alt_text": null, "profile_banner_extensions_alt_text": null, "profile_link_color": "D02B55", "profile_sidebar_border_color": "829D5E", "profile_sidebar_fill_color": "99CC33", "profile_text_color": "3E4415", "profile_use_background_image": true, "has_extended_profile": false, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false, "translator_type": "none", "withheld_in_countries": []}, "geo": null, "coordinates": null, "place": null, "contributors": null, "is_quote_status": false, "retweet_count": 0, "favorite_count": 1, "favorited": false, "retweeted": false, "lang": "en"} ], | |||
"res": "<p>To move between windows, ^A then one of h (up) j (left) k (right) l (ell down), aka the vi movement keys, or you can use arrow keys.</p><p>To resize the current window, ^A < or > or + or -.</p><p>To swap two windows (to move them around), ^A { or ^A }.</p>" | |||
} |
@@ -19,6 +19,7 @@ | |||
<div class="clear"></div> | |||
<section> | |||
<h1 id="{{ slug(resource) }}-title"><a class="no-tufte-underline" href="{{ itemurl(resource) }}">{{ resource.meta.title }}</a></h1> | |||
{% filter markdown|typogrify|rellinktoabs -%} | |||
{% block post -%}{%- endblock %} | |||
{%- endfilter %} | |||
@@ -1,5 +1,6 @@ | |||
mode: development | |||
media_root: media # Relative path from content folder. | |||
twitter_root: twitter # Relative path from content folder. | |||
media_url: /media # URL where the media files are served from. | |||
base_url: / # The base url for autogenerated links. | |||
plugins: | |||
@@ -18,6 +19,7 @@ context: | |||
jinja2: | |||
extensions: | |||
- encthenet_plugins.RelLinktoAbs | |||
- encthenet_plugins.TweetExtension | |||
markdown: | |||
extensions: | |||
- toc | |||
@@ -0,0 +1 @@ | |||
my_thread 1556349580932833280 1556353959723356160 |
@@ -0,0 +1,96 @@ | |||
from twitter import * | |||
import pprint | |||
import json | |||
import pathlib | |||
import urllib | |||
import twauth | |||
forcerefetch = False | |||
base_dir = pathlib.Path('content') / 'twitter' | |||
imgbase_dir = pathlib.Path('content') / 'media' / 'twitter' | |||
# Info for getting alt text. | |||
# Requires a project, which requires a phone number, which it rejects my home phone | |||
# https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/media | |||
def write_media(media): | |||
mediaurl = media['media_url_https'] | |||
parts = urllib.parse.urlparse(mediaurl) | |||
fname = pathlib.PurePosixPath(parts.path).name | |||
imgpath = imgbase_dir / fname | |||
if not forcerefetch and imgpath.exists(): | |||
print('media exists', fname) | |||
return | |||
resp = urllib.request.urlopen(mediaurl) | |||
try: | |||
if resp.getcode() == 200: | |||
with open(imgpath, 'wb') as fp: | |||
d = None | |||
while d != b'': | |||
d = resp.read1() | |||
fp.write(d) | |||
finally: | |||
resp.close() | |||
def write_tweet(tweet): | |||
twpath = base_dir / (tweet['id_str'] + '.yaml') | |||
if not forcerefetch and twpath.exists(): | |||
print('exists', tweet['id_str']) | |||
return | |||
#pprint.pprint(repr(tweet)) | |||
try: | |||
# Has media to d/l | |||
for i in tweet['entities']['media']: | |||
write_media(i) | |||
except KeyError as e: | |||
pass | |||
with open(twpath, 'w') as fp: | |||
print('write:', tweet['id_str']) | |||
# wrap in --- to mark as metadata for hyde/jinja2 | |||
print('---', file=fp) | |||
json.dump(tweet, fp) | |||
# dump doesn't terminate w/ nl | |||
print('', file=fp) | |||
print('---', file=fp) | |||
def act_my_thread(tw, start_tw_id, stop_tw_id): | |||
tw_id = int(stop_tw_id) | |||
while True: | |||
# https://alexwlchan.net/2022/11/tweet-alt-text/ | |||
base_tw = tw.statuses.show(_id=tw_id, tweet_mode='extended', include_ext_alt_text=True) | |||
write_tweet(base_tw) | |||
if start_tw_id == tw_id: | |||
break | |||
tw_id = base_tw['in_reply_to_status_id'] | |||
if __name__ == '__main__': | |||
args, kwargs = twauth.twit_argskw | |||
tw = Twitter(*args, **kwargs) | |||
# https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/get-statuses-show-id | |||
#print(repr(tw.statuses.show(_id=1556349580932833280))) | |||
with open('tweets.txt') as fp: | |||
for i in fp: | |||
action, tw_id, *extra = i.split() | |||
tw_id = int(tw_id) | |||
locals()['act_%s' % action](tw, tw_id, *extra) | |||
print('done') |