gemini.git

going-flying.com gemini git repository

summary

tree

log

refs

c9132f89279f0f9d39d23647c636a067f89e5bff - Matthew Ernisse - 1612278035

going to have my own capcom I guess.

view tree

view raw

diff --git a/capcom/capcom.py b/capcom/capcom.py
new file mode 100644
index 0000000..56672bf
--- /dev/null
+++ b/capcom/capcom.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+import argparse
+import datetime
+import os.path
+import time
+import urllib.parse
+
+import feedparser
+import gusmobile
+
+def load_feed_urls(filename="feeds.txt"):
+    feeds = []
+    with open(filename, "r") as fp:
+        for line in fp:
+            line = line.strip()
+            if not line or line.startswith("#"):
+                continue
+            feeds.append(line)
+    return feeds
+
+def add_port_to_url(url):
+    # A temporary patch for a bug in gusmobile
+    url = urllib.parse.urlsplit(url)
+    if ":" not in url.netloc:
+        url = url._replace(netloc=url.netloc+":1965")
+    return urllib.parse.urlunsplit(url)
+
+def items_from_feed_string(feed_str):
+    items = []
+    feed_obj = feedparser.parse(feed_str)
+    feed = feed_obj.feed
+    for entry in feed_obj.entries:
+        # Only add gemini:// URLs
+        if not entry.link.startswith("gemini://"):
+            for link in entry.links:
+                if link.rel == "alternate" and link.href.startswith("gemini://"):
+                    entry.link = link.href
+                    break
+            else:
+                continue
+        # Use published timestamp instead of updated timestamp, if it exists.
+        # Skip entries with no timestamp at all.
+        timestamp = entry.get("updated_parsed", None)
+        timestamp = entry.get("published_parsed", timestamp)
+        if timestamp:
+            items.append((timestamp, entry.link, entry.title, feed.title))
+    return items
+
+def format_aggregated(items, filename, n_feeds):
+    with open(filename, "w") as fp:
+        # Add header
+        if os.path.exists("header.gmi"):
+            with open("header.gmi", "r") as fp2:
+                fp.write(fp2.read())
+        else:
+            fp.write("# CAPCOM Gemini feed aggregator\n\n")
+        # Feed count
+        fp.write("Aggregating {} Atom feeds from Geminispace.\n".format(n_feeds))
+        # List feed entries
+        current_day = (0,0)
+        for updated, link, entry_title, feed_title in items:
+            item_day = (updated.tm_year, updated.tm_yday)
+            if item_day != current_day:
+                current_day = item_day
+                fp.write("\n## " + datetime.datetime.fromtimestamp(time.mktime(updated)).strftime("%Y-%m-%d") + "\n\n")
+            fp.write("=> {} {} - {}\n".format(link, feed_title, entry_title))
+        fp.write("\n")
+        # Add footer
+        if os.path.exists("footer.gmi"):
+            with open("footer.gmi", "r") as fp2:
+                fp.write(fp2.read())
+
+def aggregate(feed_file="feeds.txt", output_file="index.gmi"):
+    # Load feed URLs to query
+    feed_urls = load_feed_urls(feed_file)
+    N = len(feed_urls)
+
+    # Prepare to extract feed items
+    last_accessed = {}
+    skips = 0
+    items = []
+    while feed_urls:
+        # Get a feed URL to fetch
+        feed_url = feed_urls.pop()
+
+        # Don't hammer servers
+        netloc = urllib.parse.urlsplit(feed_url).netloc
+        last = last_accessed.get(netloc, 0)
+        now = time.time()
+        interval = int(now - last)
+        if interval < 5:
+            print("Declining to hit {} again after only {} seconds".format(netloc, interval))
+            feed_urls.insert(0, feed_url)
+            skips += 1
+            if skips == len(feed_urls):
+                # We've hammered every server in the queue!  Sleep a bit...
+                print("Sleeping to give all servers a rest!")
+                time.sleep(5)
+            continue
+        skips = 0
+
+        # Good to go
+        print("Fetching ", feed_url)
+        feed_url = add_port_to_url(feed_url)
+        try:
+            resp = gusmobile.fetch(feed_url)
+            if resp.status == "20":
+                last_accessed[netloc] = time.time()
+                items.extend(items_from_feed_string(resp.content))
+        except:
+            print("Error on {}, skipping...".format(feed_url))
+            continue
+
+    # Find 64 most recent items
+    items.sort(reverse=True)
+    items = items[0:64]
+
+    # Format output
+    format_aggregated(items, output_file, N)
+
+def main():
+    # Parse arguments
+    parser = argparse.ArgumentParser(description='Aggregate Atom feeds to a Gemini doc.')
+    parser.add_argument('-f', '--feeds', dest='feed_file', type=str,
+            default="feeds.txt", help="file to read feed URLs from")
+    parser.add_argument('-o', '--output', dest='output', type=str,
+            default="index.gmi", help='output filename')
+    args = parser.parse_args()
+
+    # Aggregate feeds
+    aggregate(args.feed_file, args.output)
+
+if __name__ == "__main__":
+    main()
diff --git a/capcom/feeds.txt b/capcom/feeds.txt
new file mode 100644
index 0000000..89ff900
--- /dev/null
+++ b/capcom/feeds.txt
@@ -0,0 +1,100 @@
+gemini://carcosa.net/send-the-nukes/atom.xml
+gemini://carcosa.net/journal/atom.xml
+gemini://gemini.circumlunar.space/users/solderpunk/gemlog/atom.xml
+gemini://gemini.circumlunar.space/users/solderpunk/pikkulog/atom.xml
+gemini://typed-hole.org/journal/atom.xml
+gemini://mozz.us/journal/atom.xml
+gemini://80h.dev/glog/atom.xml
+gemini://gemini.circumlunar.space/users/supurb/atom.xml
+gemini://samsai.eu/gemlog/atom.xml
+gemini://gemini.circumlunar.space/users/shufei/phlog/atom.xml
+gemini://gem.1.21jiggawatts.net/posts/atom.xml
+gemini://hannuhartikainen.fi/twinlog/atom.xml
+gemini://thelambdalab.xyz/atom.xml
+gemini://gemini.circumlunar.space/users/acdw/atom.xml
+gemini://republic.circumlunar.space/users/itsdave/atom.xml
+gemini://gemini.circumlunar.space/users/druxx/atom.xml
+gemini://acidic.website/musings/atom.xml
+gemini://makeworld.gq/gemlog/atom.xml
+gemini://demifiend.org/atom.xml
+gemini://starbreaker.org/atom.xml
+gemini://tanelorn.city/~vidak/atom.xml
+gemini://otrn.org/atom.xml
+gemini://qd.discordian.de/entries/atom.xml
+gemini://pon.ix.tc/~krixano/gemlog/atom.xml
+gemini://gemini.circumlunar.space/users/alchemist/gemlog/atom.xml
+gemini://makeworld.gq/users/~atyrfingerprints/gemlog/atom.xml
+gemini://gem.pwarren.id.au/gemlog/atom.xml
+gemini://gemini.circumlunar.space/users/parker/gemlog/atom.xml
+gemini://9til.de/users/~julienxx/atom.xml
+gemini://alexschroeder.ch:1965/do/atom
+gemini://gemini.marmaladefoo.com/cgi-bin/atom-feed.cgi?lukee
+gemini://dctrud.randomroad.net/gemlog/atom.xml
+gemini://envs.net/~vee/pikkulog/atom.xml
+gemini://envs.net/~vee/gemlog/atom.xml
+gemini://mrnd.xyz/log/atom.xml
+gemini://gemini.conman.org/boston.atom
+gemini://upyum.com/journal/feed.atom
+gemini://cetacean.club/journal/atom.xml
+gemini://apintandaparma.club/~ajc/log/atom.xml
+gemini://going-flying.com/~mernisse/atom.xml
+gemini://gem.rmgr.dev/blog/atom.xml
+gemini://kvothe.one/gemlog/atom.xml
+gemini://tilde.team/~easeout/glog/atom.xml
+gemini://ebc.li/atom.xml
+gemini://sunshinegardens.org/~xj9/feed.atom
+gemini://tanelorn.city/~bouncepaw/gemlog/feed.rss
+gemini://gemini.go350.com/atom.xml
+gemini://inconsistentuniverse.space/atom.xml
+gemini://avalos.me/gemlog/atom.xml
+gemini://misterbanal.net/~reedwade/atom.xml
+gemini://pulham.info/atom.xml
+gemini://envs.net/~lrb/gemlog/atom.xml
+gemini://perplexing.space/atom.xml
+gemini://idiomdrottning.org/atom.xml
+gemini://l-3.space/atom.xml
+gemini://idf.looting.uk/capslog/atom.xml
+gemini://tilde.team/~sumpygump/gemlog/atom.xml
+gemini://tilde.team/~ivanruvalcaba/glog/atom.xml
+gemini://republic.circumlunar.space/users/flexibeast/gemlog/atom.xml
+gemini://muscar.eu/feeds/feed.atom.xml
+gemini://1436.ninja/gemlog/gemlog.atom
+gemini://nytpu.com/gemlog/atom.xml
+gemini://tilde.club/~lewiscowper/gemlog/atom.xml
+gemini://gemini.susa.net/atom.xml
+gemini://gemini.circumlunar.space/~adiabatic/atom.xml
+gemini://apintandaparma.club/~ajft/phlog/atom.xml
+gemini://posixcafe.org/blogs/feed.atom
+gemini://drewdevault.com/feed.xml
+gemini://celehner.com/feed.xml
+gemini://rawtext.club/~mieum/dallok/atom.xml
+gemini://republic.circumlunar.space/~luminar/atom_feed.xml
+gemini://xhrpb.com/feed.xml
+gemini://tanso.net/atom.xml
+gemini://rosenzweig.io/gemlog/atom.xml
+gemini://rwv.io/atom.xml
+gemini://amami.city/~xdefrag/atom.xml
+gemini://thesudorm.com/atom.xml
+gemini://jfh.me/atom.xml
+gemini://tilde.team/~ew0k/atom.xml
+gemini://shit.cx/atom.xml
+gemini://rootkey.co.uk/posts/atom.xml
+gemini://gemini.omarpolo.com/rss.xml
+gemini://gemini.swinslow.net/gemlog/atom.xml
+gemini://gemini.femmeandroid.com/blog/atom.xml
+gemini://talon.computer/log/atom.xml
+gemini://rawtext.club/~exastencil/atom.xml
+gemini://www.demorrow.net/atom.xml
+gemini://republic.circumlunar.space/users/joneworlds/atom.xml
+gemini://overeducated-redneck.net/glog/atom.xml
+gemini://palm93.com/atom.xml
+gemini://tilde.team/~emilis/feed.xml
+gemini://blekksprut.net/%E6%97%A5%E5%B8%B8%E9%91%91%E8%B3%9E/atom.xml
+gemini://skyjake.fi/gemlog/atom.xml
+gemini://low-key.me/gemlog/atom.xml
+gemini://breadpunk.club/~bagel/atom.xml
+gemini://calcuode.com/gemlog/atom.xml
+gemini://gem.acdw.net/do/rss
+gemini://caolan.uk/atom.xml
+gemini://gemini.sensorstation.co/atom.xml
+gemini://gemini.ctrl-c.club/~nehrman/gemlog/atom.xml