BetterPonymotes is an emote addon to serve the pony subreddits. It currently supports Chrome (including other browsers that support Chrome extensions, like Opera, Chromium, and Vivaldi), Firefox, Firefox Mobile, and Safari.
Its data is maintained as a set of files by the addon maintainer, and compiled into compact representations for use by the addon at build time in the form of a large, executable JS file. Its code is split between maintenance tools on the backend (mostly Python) and JS that runs in the browser.
First off, there are some files and directories that aren't in the git repo but need to exist.
The PEM file is a private, unencrypted RSA key. It looks like this:
-----BEGIN PRIVATE KEY----- [lots of base64 stuff] -----END PRIVATE KEY-----
The Makefile expects it to be available in the root of the project directory, but you can edit it if you like. The only thing that matters is that you NEVER expose it and NEVER lose it. Update security depends on it.
BPM assumes a POSIX environment, with standard utilities like
sed, etc. available on your PATH. This means that developing on Windows
is not currently supported. (But feel free to try to get the build working on
Windows if you want.)
You'll also need the following tools:
Python 3. Most of the scripts used for building and maintaining BPM are written in python. You'll need python installed and on your PATH with the name
python3in order to build BPM. (Many Linux distributions already have this by default.)
Python 2. The script BPM uses to tag emotes still requires Python 2, so you'll need Python 2 installed and on your PATH with the name
python2in order to tag emotes in BPM. (Many Linux distributions already have this by default.)
zip, a command line utility for creating zip-formatted archives. Needs to be on your PATH. If you don't already have it, it can probably be installed using your system's package manager. (E.g.
sudo apt-get install zipon Ubuntu)
The Firefox Addon SDK. It comes in a zip file with a
bin/activateshell script. Source it to add its
$PATH, because you need the
uhura. It's a little perl script used to sign the Firefox XPI's. It needs to be on $PATH. I'm not sure exactly where I got it, and installing it is a pain, since it depends on some CPAN modules. I forget which.
apng2gif. A tool to convert APNG files to GIF format. Needs to be on your PATH in order to build BPM.
All of this will be explained in detail further on.
Browser Addon Structure
Most addon API's have the same basic structure- there's a background script that runs on its own, and content scripts that run in the pages. The former must obviously be written to the specific browser, but the latter is largely independent except where it has to communicate with the backend.
Firefox's background script is
addon/fx-main.js. Chrome has
addon/cr-background.html, and Safari has
addon/sf-background.html. As these
scripts share a large amount of functionality, most of that is now held in
The backend is chiefly responsible for storing and managing preferences, applying the necessary files to pages (JS and CSS), and maintaining the custom CSS cache. Custom CSS is easily the most complex thing it does.
bpm-browser.js is dedicated to abstracting over the differences between
browser API's and communication.
Most of the addon proper in the content script split up between the
addon/bpm-*.js files. Most new features can be implemented here without
touching anything else.
bpm-header.js enables a bunch of extra logging, which
may help if you're getting crashes or strange behavior. Remember to disable it
Changes to preferences require editing
addon/pref-setup.js and the options
Changes involving the data backend are too complicated to go over here.
Stylesheets, Emote Data, and Compilation
Stylesheets are stored and cached in two forms - source and minified. The reason is that Reddit, by default, serves up only the minified form, and while this is enough to extract emote data, it's very difficult to read, which is very often necessary when maintaining the emote cache. The source CSS, however, has no image URLs. Since those can change freely, the only way to reliably know when emotes change is to reparse the minified CSS.
shell.py tool is responsible for updating stylesheet files. Its chief
update: redownloads all stylesheets
extract/extractall: runs the emote extraction tool on particular subreddits
git diff | komparefor convenient visual browsing.
git commit -m [...]with a standard message.
Emote data is stored in JSON form under
emotes/, one line per emote. This
makes it quite conveniently diffable and greppable.
bpgen.py tool (run by
make automatically) reads the
file for instructions, loads all emote and tag data, and compiles it into a set
of JS data files (
build/bpm-resources.js) and CSS (
These are used by the addon directly.
After updating the emote cache, any new or changed emotes need to be tagged
./tagapp.py script starts a web server on
running a webapp for this purpose. It's not too difficult to figure out how to
use, but tag data has a specific structure that should be respected.
[Small warning: the tagapp is one of the few Python scripts in the project that
runs under Python 2 (the rest is Python 3). It uses
bplib/ code, however, and
there are some lingering
unicode/json incompatibilities and bugs as a result.
In a nutshell, running the script seems to work fine on its own, but not from a
shell that has been "activated" with the Addon SDK. It seems to change the
Generally, most tags fall under others in a loose, informal hierarchy. These can
be reviewed in
Every emote must fall under at least one
ExclusiveTags can never be
used with any other tags (except as mentioned).
HiddenTags are removed from
the data set before building the addon files.
TagAliases generate additional
records in the data files, so that searching on those tags will work properly.
TagImplications essentially makes a given tag also apply several others - this
is how the hierarchy is built. Note that this is not recursive.
Every emote has exactly one primary name with all of its tags. Other names for
the same emote must be tagged as
+v (for "variant").
bpgen.py will match
them up automatically, copy tags around, and the tag search code in the addon
uses this information to hide variants (in actuality, replacing them with their
The formatting (colors and fonts) psuedo-emotes are tagged
that don't work right are tagged
+broken, any emotes that should be removed
from the final product are tagged
+nsfw is obvious and
+questionable) is slightly less so.
./checktags.py after editing tags to guard against typos and certain
classes of mistakes. Some warnings are known oddities that the script isn't
capable of recognizing; ignore those.
APNG -> GIF Conversion
Assuming all animotes are tagged with +animote, run
download and convert all of them. This process requires
apng2gif to be on
$PATH, and it will spit out GIFs to
animotes/, which must be uploaded to the
host site. It also generates
build/gif-animotes.css, which is the override
sheet used by Chrome.
Updates and Version Numbers
After updating the CSS and tags, run
shell.py commit to record it all, and
bpgen.py manually or
make to regenerate the emote data files.
This process is heavily controlled by
data/rules.yaml, which lists:
- The subreddits currently in the addon (note that
shell.pyupdate doesn't actually respect this)
- Subreddits to disregard
PONYSCRIPT-IGNOREon (needed when they incorrectly put custom emotes in this block)
- Custom tweaks to the emote set (as
emotes/is never modified directly)
- Conflict resolution rules, by subreddit (e.g. r/aaaa > r/bbbb) and by individual emote.
- Explicit matchups for +v emotes that the code can't autodetect.
The version number is at the top of the Makefile, and is used by
automatically update the version numbers every browser addon that BPM supports.
When everything is updated, running
make is sufficient to rebuild all packages.
To release BPM:
maketo rebuild all packages.
build/chrome.zipto the Chrome webstore.
build/betterponymotes.xpito addons.mozilla.org for package signing. Download the output file to
make wwwto rebuild the website.
make syncto synchronize with
ponymotes.net, including the Firefox package.
- Make update threads, message /u/TwilightShadow1, /u/DinsFire64 and other interested third parties.