https://github.com/massix/gleeter
Display xkcd comics instantly in your terminal or over HTTP, with full graphics support and zero setup.
https://github.com/massix/gleeter
comic-viewer erlang gleam-lang gleam-language http kitty-terminal terminal-based terminal-graphics webservice wezterm xkcd xkcd-api xkcd-client xkcd-comics
Last synced: 13 days ago
JSON representation
Display xkcd comics instantly in your terminal or over HTTP, with full graphics support and zero setup.
- Host: GitHub
- URL: https://github.com/massix/gleeter
- Owner: massix
- License: mit
- Created: 2024-08-15T13:01:58.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-27T07:38:34.000Z (9 months ago)
- Last Synced: 2025-07-27T09:09:24.072Z (9 months ago)
- Topics: comic-viewer, erlang, gleam-lang, gleam-language, http, kitty-terminal, terminal-based, terminal-graphics, webservice, wezterm, xkcd, xkcd-api, xkcd-client, xkcd-comics
- Language: Gleam
- Homepage: https://xkcd.massi.rocks/comics/latest
- Size: 1.59 MB
- Stars: 11
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Gleeter
Stop interrupting your workflow for comic relief! Gleeter delivers the **exact** XKCD strip you need, *right in your terminal*. Need to gently nudge a colleague about [off-by-one errors](https://xkcd.com/3062/)? Or perhaps illustrate the superpowers of [regular expressions](https://xkcd.com/208/)? Maybe teach a colleague why it is important to [sanitize your database inputs](https://xkcd.com/327/)? Gleeter's got you covered. Install now, and weaponize your command line with the power of XKCD.
This is a very simple and straightforward software to fetch and display comics from [xkcd](https://xkcd.com) right in the terminal.
Supported commands are:
* `latest`: to show the latest comic
* `random`: to show a random comic
* `id `: to show the comic with id ``. Replace `` with the desired comic ID.
* `serve ` to create a [web server](#serve-mode-details) which you can query with curl!
* You can also define aliases for the above commands (e.g. `bobbytables`) and fetch very specific comics using the [configuration file](#configuration-file).
See the [How to use](#how-to-use) section for more information and details about the commands.
For this to work, you need a terminal which understands the [Terminal Graphics Protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/).
Gleeter is known to work well with the following terminals:
* [Ghostty](https://ghostty.dev/)
* [Kitty](https://sw.kovidgoyal.net/kitty/)
* [Wezterm](https://wezfurlong.org/wezterm/)
* ... and probably more
## Test it out now
You can start using it without installing anything: simply
`curl -H "X-TERMINAL-ROWS: $(tput lines)" -H "X-TERMINAL-COLUMNS: $(tput cols)" https://xkcd.massi.rocks/comics/latest`
Replace `latest` with `random` or `id/` to change the behavior, you can also check [this file](./docker-digitalocean/config.toml) for a list of available aliases!
## Screenshots
### Latest comic
### Random Comic
### Fetch a comic by ID
### Running on Ghostty via Docker
### Serve mode using Docker and Ghostty
## How to use
Gleeter understands the following commands:
* `help`: Prints help information, this is also the default behavior if no arguments are provided.
* `version`: Prints the application version.
* `latest`: Fetches the latest comic from XKCD and displays it in the terminal. Use the `--no-cache` or `-n` flag to bypass the cache and fetch the comic directly from XKCD.
* `random`: Fetches a random comic from XKCD and displays it in the terminal. Gleeter will automatically skip the comics using an unsupported format (very old comics were using the JPG extension), so you should always get a valid comic. This is a technical limitation of the Terminal Graphics Protocol. Use the `--no-cache` or `-n` flag to bypass the cache and fetch the comic directly from XKCD. **Warning**: the `-n` (or `--no-cache` flag) **must** come before the command for it to work (e.g.: `-n random` will work, while `random -n` will not work); this behavior will be fixed in a future version.
* `id `: Fetches the comic with the specified ID and displays it in the terminal. Replace `` with the desired comic ID. Use the `--no-cache` or `-n` flag to bypass the cache and fetch the comic directly from XKCD. **Warning**: the `-n` (or `--no-cache` flag) **must** come before the command for it to work (e.g.: `-n id 998` will work, while `id 998 -n` will not work); this behavior will be fixed in a future version.
* `serve `: Starts a web server to serve the comics. `` is optional and defaults to 8080. `` is also optional and defaults to "". For example, `gleeter serve 3000 /comics` will start the server on port 3000 and serve the comics under the `/comics` path.
* `clearcache`: Clears the local cache of comics.
## Configuration File
Gleeter uses a TOML configuration file to customize its behavior. The configuration file allows you to configure the starting comic for random mode, set screen dimensions, and define aliases for comic IDs.
### Syntax
Here's an example configuration file (based on `test_data/config.toml`):
```toml
random_start = 38
[screen]
max_cols = 80
max_lines = 23
[[alias]]
name = "bobbytables"
type = "id"
id = 987
[[alias]]
name = "rnd"
type = "random"
[[alias]]
name = "l"
type = "latest"
[metrics]
ignore_base_path = true
username = "your_username"
password = "your_password"
```
### Structure and Usage
The configuration file is loaded when Gleeter starts. Gleeter checks for the configuration file in the following order:
1. `$GLEETER_CONFIG_FILE`: If this environment variable is set, Gleeter will attempt to load the configuration file from the path specified. This variable *must* point to a valid TOML file.
2. `$XDG_CONFIG_HOME/gleeter/config.toml`: If `$GLEETER_CONFIG_FILE` is not set, Gleeter will check for a configuration file in the `$XDG_CONFIG_HOME` directory, appending `/gleeter/config.toml` to the value of the variable.
3. `$HOME/.config/gleeter/config.toml`: If `$XDG_CONFIG_HOME` is not set, Gleeter will check for a configuration file in the `$HOME` directory, appending `/.config/gleeter/config.toml` to the value of the variable.
4. `./gleeter/config.toml`: If none of the above environment variables are set, Gleeter will attempt to load the configuration file from the `./gleeter/config.toml` path.
The settings defined in the file are used to initialize the application and control its behavior.
* `random_start`: Specifies the starting comic number for the random comic selection. If not specified, a default value is used.
* `[screen]`: This section configures the screen dimensions for displaying comics.
* `max_cols`: Specifies the maximum number of columns to use for displaying the comic.
* `max_lines`: Specifies the maximum number of lines to use for displaying the comic.
* `[metrics]`: This section configures the [metrics](#prometheus-metrics) endpoint and it is meaningful only for the `serve` mode.
* `ignore_base_path`: If set to `true`, the `/metrics` endpoint will be accessible even when a base path is used. This allows accessing metrics at `/metrics` regardless of the configured base path.
* `username`: An optional username for basic authentication on the `/metrics` endpoint. If provided, a password must also be specified.
* `password`: An optional password for basic authentication on the `/metrics` endpoint. If provided, a username must also be specified.
* `[[alias]]`: This section defines aliases for accessing comics. You can define multiple aliases.
* `name`: The name of the alias. Alias names **must not** be empty and **must not** contain any of the following characters: `!`, `;`, `$`, `:`, `\`, `"`, `'`, `(`, `)`, space, tab, newline, or carriage return. Leading and trailing whitespaces will be automatically removed.
* `type`: The type of alias. Valid values are `"id"`, `"random"`, and `"latest"`.
* `id`: (Only required for `"id"` aliases) The comic ID to associate with the alias.
The [config.gleam](src/gleeter/config.gleam) file defines the structure of the configuration data and how it is parsed from the TOML file.
To modify Gleeter's behavior, simply edit the configuration file and restart the application.
### Serve mode details
When Gleeter is run in `serve` mode, it starts an HTTP server that exposes the following endpoints:
* `/`: Serves the latest comic.
* `/metrics/`: Serves the [Prometheus metrics](#prometheus-metrics) endpoint.
* `/latest`: Serves the latest comic (same as `/`).
* `/random`: Serves a random comic.
* `/id/`: Serves the comic with the specified ID. Replace `` with the desired comic ID.
You can customize the base path for these endpoints by using the `` argument. For example, if you start the server with `gleeter serve 8080 /comics`, the endpoints will be:
* `/comics/`
* `/comics/metrics`
* `/comics/latest`
* `/comics/random`
* `/comics/id/`
Gleeter also supports receiving the terminal size via HTTP headers. You can send the `X-TERMINAL-COLUMNS` and `X-TERMINAL-ROWS` headers with your request to specify the terminal size. This allows Gleeter to properly format the comic for your terminal. If these headers are not provided, Gleeter will use a default terminal size. Please be aware that for the resizing to work, you need to send **both** headers.
#### Serve and Configuration file
Serve mode will also honour all the aliases defined in the [configuration file](#configuration-file), and expose them all at the root of the `base_path`. As a quick example, imagine you have the following defined in the `config.toml` file:
```toml
[[alias]]
name = "bobbytables"
type = "id"
id = 987
[[alias]]
name = "rnd"
type = "random"
[[alias]]
name = "l"
type = "latest"
```
And you start the server with `gleeter serve 8080 /comics`, on top of the URLs mentioned above, you will also get the following:
* `/comics/bobbytables`, which will be an alias for `/comics/id/987`
* `/comics/rnd`, which will be an alias for `/comics/random`
* `/comics/l`, which will be an alias for `/comics/latest`
#### Health Check
In addition to serving comics, Gleeter provides a `/health` endpoint. This endpoint returns a JSON structure with information about the server's status and performance. The structure includes the following fields:
* `cache_status`: A boolean indicating whether the cache is enabled.
* `cache_elements`: The number of elements currently stored in the cache.
* `processed_queries`: The number of queries processed by the server since it started.
* `current_version`: The version of Gleeter being run.
* `loaded_aliases`: A list of currently loaded alias names.
Example response:
```json
{
"cache_status": true,
"cache_elements": 198,
"processed_queries": 4,
"current_version": "Gleeter v1.3.1 (https://github.com/massix/gleeter)",
"loaded_aliases": [
"wikipedia",
"programmers",
"compiling",
"sudo",
"standards",
"techsupport",
"bobbytables",
"tenthousand",
"correlation"
]
}
```
This endpoint can be useful for monitoring the health and performance of the server.
The automatic skipping of unsupported images in random mode also applies when using the `/random` endpoint in serve mode, ensuring that only valid comics are served.
#### Prometheus Metrics
Gleeter exposes Prometheus metrics that can be used to monitor the server's performance and health. These metrics are available at the `//metrics` and **optionally** also at the `/metrics` path (the behavior is configurable via the configuration file).
It is also possible to protect the endpoint with basic authentication by setting a username and password in the configuration file. If authentication is enabled, you will need to provide the correct credentials to access the metrics endpoint. Only [Basic authorization](https://en.wikipedia.org/wiki/Basic_access_authentication) is supported right now.
The following metrics are exposed:
* `gleeter:http_request_duration_seconds`: A histogram of the duration of HTTP requests in seconds. Labels include the method used (i.e.: "random", "id" or "latest"). Buckets are: `0.1`, `0.25`, `0.5`, `0.75`, `1.0`, `1.5`.
* `gleeter:processed_queries_total`: A counter of the total number of processed queries, labeled by comic method (again: "random", "id" or "latest").
* `gleeter:cache_hits_total`: A counter of the total number of cache hits, labeled by comic method.
* `gleeter:cache_miss_total`: A counter of the total number of cache misses, labeled by comic method.
* `gleeter:cached_elements_total`: A counter of the total number of elements currently stored in the cache.
* `gleeter:memory_used_bytes`: A gauge of the memory used by the Erlang VM in bytes, with labels for different memory slices (e.g., "system", "processes", "ets", "code", "binary", "atom").
* `gleeter:http_invalid_requests_total`: A counter of the total number of invalid HTTP requests.
* `gleeter:jpeg_retried_total`: A counter of the total number of times the server had to retry because it was getting a JPEG image.
These metrics can be used to create dashboards and alerts to monitor the Gleeter server's performance.
A [docker-compose.yaml](./docker-test/docker-compose.yaml) file is provided in the [docker-test](./docker-test/) directory to facilitate local testing with Prometheus and Grafana. To deploy the stack locally, simply run `make docker-test`. The stack will also automatically configure Grafana to use the Prometheus datasource, which is in turn pre-configured to scrape the metrics from the gleeter service.
Once the stack is running:
1. Access Grafana in your web browser at `http://localhost:3000`.
2. Log in with username `gleeter` and password `gleeter`.
3. Import the [Gleeter-dashboard.json](./docker-test/Gleeter-dashboard.json) dashboard file into Grafana to visualize the Gleeter metrics. You can do this by navigating to "Dashboards" -> "New" -> "Import" and uploading the JSON file.
This setup allows you to easily monitor Gleeter's performance and health locally using Prometheus and Grafana. The provided dashboard includes panels for displaying processed queries, cache statistics, memory usage, and request duration.
## How to install
Gleeter should be compatible with any operating system that supports [Gleam](https://gleam.run) and [Erlang](https://www.erlang.org/). It has been tested on NixOS and MacOS Darwin. Installation instructions may vary depending on your specific system.
### NixOS and nixpkgs
If you have [Nix flakes](https://nixos.wiki/wiki/flakes) enabled, you can run Gleeter directly with `nix run github:massix/gleeter -- `, replacing `` with `latest`, `random`, `id `, etc. To install Gleeter locally, you can use the provided overlay in your `pkgs` import. Here is a very basic example on how to use it:
```nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
inputs.gleeter.url = "github:massix/gleeter";
outputs = inputs@{ nixpkgs, gleeter, ...}:
let
pkgs = import nixpkgs {
system = "aarch64-darwin";
overlays = [ gleeter.overlays.default ];
};
in
{
# From here on, you can use pkgs.gleeter
};
}
```
### Docker
Gleeter is also available as a Docker image on [Docker Hub](https://hub.docker.com/r/massix86/gleeter). This is a convenient way to run Gleeter without needing to install Gleam or Erlang.
To run Gleeter with Docker, use the following command:
```bash
docker run --rm massix86/gleeter: --
```
Replace `` with the desired version of Gleeter, or use `latest` to get the most recent version. Replace `` with the Gleeter command you want to run (e.g., `latest`, `random`, `id 42`). The `--rm` flag automatically removes the container when it exits.
For example, to fetch a random comic using the latest version of Gleeter, you would run:
```bash
docker run --rm massix86/gleeter:latest -- random
```
To use a configuration file with the Docker image, follow these steps:
1. Create a `config.toml` file with your desired configuration settings. For example:
```toml
random_start = 100
[screen]
max_cols = 100
max_lines = 30
[[alias]]
name = "bobbytables"
type = "id"
id = 987
[[alias]]
name = "rnd"
type = "random"
[[alias]]
name = "l"
type = "latest"
```
2. Run the Docker image with a bind mount to map the `config.toml` file from your host machine into the container, and set the `GLEETER_CONFIG_FILE` environment variable to point to the mounted file:
```bash
docker run --rm -v $(pwd)/config.toml:/app/config.toml -e GLEETER_CONFIG_FILE=/app/config.toml massix86/gleeter:latest -- bobbytables
```
This command does the following:
* `--rm`: Automatically removes the container when it exits.
* `-v $(pwd)/config.toml:/app/config.toml`: Creates a bind mount, mapping the `config.toml` file in the current directory (`$(pwd)`) on your host machine to the `/app/config.toml` path inside the container.
* `-e GLEETER_CONFIG_FILE=/app/config.toml`: Sets the `GLEETER_CONFIG_FILE` environment variable inside the container to `/app/config.toml`. This tells Gleeter where to find the configuration file.
* `massix86/gleeter:latest`: Specifies the Docker image to use.
* `-- bobbytables`: Tells Gleeter to fetch the comic pointed by the bobbytables alias.
Make sure that the `config.toml` file exists in the current directory when you run the Docker command.
### Other systems
To install on other systems, ensure you have Gleam and Erlang installed. Then, you can build and run the project using Gleam's build tools. Refer to the Gleam documentation for specific instructions.
## Contributions
All contributions are welcome! Feel free to open pull requests or issues to help improve the project.
## License and copyrights
Gleeter is licensed under the MIT License. See [LICENSE.txt](LICENSE.txt) for details.
All xkcd comics displayed by Gleeter are licensed under a Creative Commons license. The intellectual property of xkcd.com belongs to Randall Munroe. Contact details can be found on the xkcd.com website.
I am in no way responsible for the content of the xkcd comics. All attributions and inquiries should be directed to Randall Munroe.