A feed aggregator that only polls feeds when they've been updated.

Anna “CyberTailor” 1c852fc608 Added unit tests for customFilters %!s(int64=2) %!d(string=hai) anos
gemini_antenna 68505466a6 Added formatTime() helper function %!s(int64=2) %!d(string=hai) anos
tests 1c852fc608 Added unit tests for customFilters %!s(int64=2) %!d(string=hai) anos
.gitignore 4a7e1fa784 Sorted imports %!s(int64=2) %!d(string=hai) anos
LICENSE d8e07d5227 Initial commit %!s(int64=3) %!d(string=hai) anos
MANIFEST.in 4f46e71956 Added unit tests for URLHelper %!s(int64=2) %!d(string=hai) anos
README.md ab81208d8d Sync README %!s(int64=2) %!d(string=hai) anos
pyproject.toml 4f46e71956 Added unit tests for URLHelper %!s(int64=2) %!d(string=hai) anos
setup.cfg 4f46e71956 Added unit tests for URLHelper %!s(int64=2) %!d(string=hai) anos

README.md

Antenna - Receiving Transmissions from Geminispace

   |\
   \ \ @
   /\_\/
  _|_
_|___|___
 ANTENNA

This is a feed aggregator with a twist. It doesn't have a list of feeds that it repeatedly checks. Instead it takes feed URLs as user input, and checks newly submitted feeds every few minutes. No more useless hammering of dead feed files.

  • You write a post on your gemlog.
  • Your atom/RSS/gemsub feed updates.
  • You call gemini://[domain and path to submission script]?[URL to your feed]
  • A few minutes later Antenna fetches your feed and lists your entries.

Background

Most gemlogs live only a brief life, or post seldom and irregularly. A common feed aggregator relies on the consumer to curate a list of feeds, all of which will be fetched again and again and again at some interval. Few of them (most of the time none of them) have any new entries since last check. Some disappear, causing timeouts. I find this incessant blind polling a waste of resources.

This became very apparent when Solderpunk decided that the central CAPCOM installation should no longer poll every feed it knew of, but only a random subset of 100 of them each month. At the time of this writing, CAPCOM looks like a ghost town.

I believe that a better way to aid discoverability in any community is to let publishers push their content to where people are looking for it. And the gemini community is not yet too large for a central hub of information.

Preparation

Antenna runs on python3 and mostly uses modules available in core. Two exceptions I remember are feedparser and gemcall. Please tell me if you try to run this and run into any undocumented requirements.

The current code base makes a few assumptions that may or may not be true for your system:

  • That there are two folders in the same directory named antenna and public_gemini, respectively.
  • That the user which runs antenna-submit and antenna-filter CGI scripts has both read and write access to the SQLite3 database and the folder antenna, which the database file will be in.
  • That there are files about.gmi, log, filter and submit in the public_gemini folder, because the generated page will link to them.

My setup is a useful referense. It looks like this:

~antenna/
    |
    +--- antenna/
    |       |
    |       +--- README.md
    |       +--- LICENSE
    |       +--- db.py
    |       +--- ingestfeeds-wrapper.sh
    |       +--- antenna.log
    |       +--- antenna.sqlite
    |       +--- blocklist.txt
    |
    +--- public_gemini/
            |
            +--- cgi-bin/
                    |
                    +--- filter
                    +--- submit
                    +--- log
            +--- index.gmi
            +--- about.gmi

Nothing outside of ~antenna/public_gemini/ is publicly reachable. The file about.gmi is a handwritten file about your instance. The index.gmi file is generated by Antenna. The scripts log, feed and submit change the working directory to ~antenna/antenna/ and then run tail -n 50 antenna.log, antenna-filter and antenna-submit respectively.

Installation

Packaging status

# install from this repository
pip install git+https://notabug.org/tinyrabbit/gemini-antenna.git#egg=gemini-antenna
# or use unofficial package on PyPI
pip install antenna

Create a database in the antenna directory:

cd antenna
python3
> from gemini_antenna import db
> db.AntennaDB.createDB()

Make sure that the user that executes antenna-submit and antenna-filter scripts has read and write permissions to the directory antenna as well as antenna/antenna.sqlite.

Create a cron job that runs antenna refresh via the wrapper (substitute for whichever user should run the ingest job, and which directory the ingestfeeds-wrapper.sh is in):

echo "*/10 * * * * antenna /home/antenna/antenna/ingestfeeds-wrapper.sh" | sudo tee /etc/cron.d/antenna-ingestion

If there are any specific domains you'd like to block from publishing to your Antenna, please fill them in (one on each line) in a file named blocklist.txt, one URL per line and all starting with gemini:// or other scheme and separator, depending on what you'd like to block.

Example server configuration (gmid)

Custom database location can be set using ANTENNA_DATAROOT env variable.

# old aliases
location "/antenna/log" {
	block return 31 "/antenna/cgi-bin/log"
}
location "/antenna/filter" {
	block return 31 "/antenna/cgi-bin/filter"
}
location "/antenna/submit" {
	block return 31 "/antenna/cgi-bin/submit"
}

location /antenna {
	root "/home/antenna/public_gemini"
	cgi "cgi-bin/*"
}

Contributing

  • Be kind, humble and open-minded in discussions.
  • Send pull requests or submit issues here, or contact me directly with suggestions/feedback/thoughts/bug reports: bjorn.warmedal@gmail.com or ew0k@tilde.team.