{"id":18374526,"url":"https://github.com/massix/gleeter","last_synced_at":"2026-04-15T06:31:25.917Z","repository":{"id":253253707,"uuid":"842947404","full_name":"massix/gleeter","owner":"massix","description":"Display xkcd comics instantly in your terminal or over HTTP, with full graphics support and zero setup.","archived":false,"fork":false,"pushed_at":"2025-07-27T07:38:34.000Z","size":1665,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-27T09:09:24.072Z","etag":null,"topics":["comic-viewer","erlang","gleam-lang","gleam-language","http","kitty-terminal","terminal-based","terminal-graphics","webservice","wezterm","xkcd","xkcd-api","xkcd-client","xkcd-comics"],"latest_commit_sha":null,"homepage":"https://xkcd.massi.rocks/comics/latest","language":"Gleam","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/massix.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-08-15T13:01:58.000Z","updated_at":"2025-07-27T07:38:09.000Z","dependencies_parsed_at":"2024-12-09T10:21:46.857Z","dependency_job_id":"0273ad0b-99f7-4f9a-b70f-bf98e9be7ee9","html_url":"https://github.com/massix/gleeter","commit_stats":null,"previous_names":["massix/gleeter"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/massix/gleeter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massix%2Fgleeter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massix%2Fgleeter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massix%2Fgleeter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massix%2Fgleeter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/massix","download_url":"https://codeload.github.com/massix/gleeter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massix%2Fgleeter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31829743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T18:05:02.291Z","status":"online","status_checked_at":"2026-04-15T02:00:06.175Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["comic-viewer","erlang","gleam-lang","gleam-language","http","kitty-terminal","terminal-based","terminal-graphics","webservice","wezterm","xkcd","xkcd-api","xkcd-client","xkcd-comics"],"created_at":"2024-11-06T00:14:56.869Z","updated_at":"2026-04-15T06:31:25.908Z","avatar_url":"https://github.com/massix.png","language":"Gleam","readme":"# Gleeter\nStop 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.\n\nThis is a very simple and straightforward software to fetch and display comics from [xkcd](https://xkcd.com) right in the terminal.\n\nSupported commands are:\n*  `latest`: to show the latest comic\n*  `random`: to show a random comic\n*  `id \u003cnumber\u003e`: to show the comic with id `\u003cnumber\u003e`. Replace `\u003cnumber\u003e` with the desired comic ID.\n*  `serve \u003cport\u003e \u003cpath\u003e` to create a [web server](#serve-mode-details) which you can query with curl!\n* You can also define aliases for the above commands (e.g. `bobbytables`) and fetch very specific comics using the [configuration file](#configuration-file).\nSee the [How to use](#how-to-use) section for more information and details about the commands.\n\nFor this to work, you need a terminal which understands the [Terminal Graphics Protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/).\n\nGleeter is known to work well with the following terminals:\n\n*   [Ghostty](https://ghostty.dev/)\n*   [Kitty](https://sw.kovidgoyal.net/kitty/)\n*   [Wezterm](https://wezfurlong.org/wezterm/)\n*   ... and probably more\n\n## Test it out now\n\nYou can start using it without installing anything: simply\n`curl -H \"X-TERMINAL-ROWS: $(tput lines)\" -H \"X-TERMINAL-COLUMNS: $(tput cols)\" https://xkcd.massi.rocks/comics/latest`\n\nReplace `latest` with `random` or `id/\u003ccomic_id\u003e` to change the behavior, you can also check [this file](./docker-digitalocean/config.toml) for a list of available aliases!\n\n## Screenshots\n\n### Latest comic\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/latest.png\" width=\"400px\" /\u003e\n\u003c/div\u003e\n\n### Random Comic\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/random.png\" width=\"400px\" /\u003e\n\u003c/div\u003e\n\n### Fetch a comic by ID\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/id.png\" width=\"400px\" /\u003e\n\u003c/div\u003e\n\n### Running on Ghostty via Docker\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/ghostty.png\" width=\"400px\" /\u003e\n\u003c/div\u003e\n\n### Serve mode using Docker and Ghostty\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/docker.png\" width=\"400px\" /\u003e\n\u003c/div\u003e\n\n## How to use\n\nGleeter understands the following commands:\n\n*   `help`: Prints help information, this is also the default behavior if no arguments are provided.\n*   `version`: Prints the application version.\n*   `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.\n*   `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.\n*   `id \u003cnumber\u003e`: Fetches the comic with the specified ID and displays it in the terminal. Replace `\u003cnumber\u003e` 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.\n\n*   `serve \u003cport\u003e \u003cbase_path\u003e`: Starts a web server to serve the comics. `\u003cport\u003e` is optional and defaults to 8080. `\u003cbase_path\u003e` 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.\n*   `clearcache`: Clears the local cache of comics.\n\n## Configuration File\n\nGleeter 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.\n\n### Syntax\n\nHere's an example configuration file (based on `test_data/config.toml`):\n\n```toml\nrandom_start = 38\n\n[screen]\nmax_cols = 80\nmax_lines = 23\n\n[[alias]]\nname = \"bobbytables\"\ntype = \"id\"\nid = 987\n\n[[alias]]\nname = \"rnd\"\ntype = \"random\"\n\n[[alias]]\nname = \"l\"\ntype = \"latest\"\n\n[metrics]\nignore_base_path = true\nusername = \"your_username\"\npassword = \"your_password\"\n```\n\n### Structure and Usage\n\nThe configuration file is loaded when Gleeter starts. Gleeter checks for the configuration file in the following order:\n\n1.  `$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.\n2.  `$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.\n3.  `$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.\n4.  `./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.\n\nThe settings defined in the file are used to initialize the application and control its behavior.\n\n*   `random_start`: Specifies the starting comic number for the random comic selection. If not specified, a default value is used.\n\n*   `[screen]`: This section configures the screen dimensions for displaying comics.\n    *   `max_cols`: Specifies the maximum number of columns to use for displaying the comic.\n    *   `max_lines`: Specifies the maximum number of lines to use for displaying the comic.\n*   `[metrics]`: This section configures the [metrics](#prometheus-metrics) endpoint and it is meaningful only for the `serve` mode.\n    *   `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.\n    *   `username`: An optional username for basic authentication on the `/metrics` endpoint. If provided, a password must also be specified.\n    *   `password`: An optional password for basic authentication on the `/metrics` endpoint. If provided, a username must also be specified.\n*   `[[alias]]`: This section defines aliases for accessing comics. You can define multiple aliases.\n    *   `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.\n    *   `type`: The type of alias. Valid values are `\"id\"`, `\"random\"`, and `\"latest\"`.\n    *   `id`: (Only required for `\"id\"` aliases) The comic ID to associate with the alias.\n\nThe [config.gleam](src/gleeter/config.gleam) file defines the structure of the configuration data and how it is parsed from the TOML file.\n\nTo modify Gleeter's behavior, simply edit the configuration file and restart the application.\n\n### Serve mode details\n\nWhen Gleeter is run in `serve` mode, it starts an HTTP server that exposes the following endpoints:\n\n*   `/`: Serves the latest comic.\n*   `/metrics/`: Serves the [Prometheus metrics](#prometheus-metrics) endpoint.\n*   `/latest`: Serves the latest comic (same as `/`).\n*   `/random`: Serves a random comic.\n*   `/id/\u003cnumber\u003e`: Serves the comic with the specified ID. Replace `\u003cnumber\u003e` with the desired comic ID.\n\nYou can customize the base path for these endpoints by using the `\u003cbase_path\u003e` argument. For example, if you start the server with `gleeter serve 8080 /comics`, the endpoints will be:\n\n*   `/comics/`\n*   `/comics/metrics`\n*   `/comics/latest`\n*   `/comics/random`\n*   `/comics/id/\u003cnumber\u003e`\n\nGleeter 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.\n\n#### Serve and Configuration file\nServe 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:\n```toml\n[[alias]]\nname = \"bobbytables\"\ntype = \"id\"\nid = 987\n\n[[alias]]\nname = \"rnd\"\ntype = \"random\"\n\n[[alias]]\nname = \"l\"\ntype = \"latest\"\n```\n\nAnd you start the server with `gleeter serve 8080 /comics`, on top of the URLs mentioned above, you will also get the following:\n*   `/comics/bobbytables`, which will be an alias for `/comics/id/987`\n*   `/comics/rnd`, which will be an alias for `/comics/random`\n*   `/comics/l`, which will be an alias for `/comics/latest`\n\n#### Health Check\nIn 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:\n\n*   `cache_status`: A boolean indicating whether the cache is enabled.\n*   `cache_elements`: The number of elements currently stored in the cache.\n*   `processed_queries`: The number of queries processed by the server since it started.\n*   `current_version`: The version of Gleeter being run.\n*   `loaded_aliases`: A list of currently loaded alias names.\n\nExample response:\n\n```json\n{\n  \"cache_status\": true,\n  \"cache_elements\": 198,\n  \"processed_queries\": 4,\n  \"current_version\": \"Gleeter v1.3.1 (https://github.com/massix/gleeter)\",\n  \"loaded_aliases\": [\n    \"wikipedia\",\n    \"programmers\",\n    \"compiling\",\n    \"sudo\",\n    \"standards\",\n    \"techsupport\",\n    \"bobbytables\",\n    \"tenthousand\",\n    \"correlation\"\n  ]\n}\n```\n\nThis endpoint can be useful for monitoring the health and performance of the server.\n\nThe 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.\n\n#### Prometheus Metrics\n\nGleeter exposes Prometheus metrics that can be used to monitor the server's performance and health. These metrics are available at the `/\u003cbase_path\u003e/metrics` and **optionally** also at the `/metrics` path (the behavior is configurable via the configuration file).\n\nIt 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.\n\nThe following metrics are exposed:\n\n*   `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`.\n*   `gleeter:processed_queries_total`: A counter of the total number of processed queries, labeled by comic method (again: \"random\", \"id\" or \"latest\").\n*   `gleeter:cache_hits_total`: A counter of the total number of cache hits, labeled by comic method.\n*   `gleeter:cache_miss_total`: A counter of the total number of cache misses, labeled by comic method.\n*   `gleeter:cached_elements_total`: A counter of the total number of elements currently stored in the cache.\n*   `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\").\n*   `gleeter:http_invalid_requests_total`: A counter of the total number of invalid HTTP requests.\n*   `gleeter:jpeg_retried_total`: A counter of the total number of times the server had to retry because it was getting a JPEG image.\n\nThese metrics can be used to create dashboards and alerts to monitor the Gleeter server's performance.\n\nA [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.\n\nOnce the stack is running:\n\n1.  Access Grafana in your web browser at `http://localhost:3000`.\n2.  Log in with username `gleeter` and password `gleeter`.\n3.  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\" -\u003e \"New\" -\u003e \"Import\" and uploading the JSON file.\n\nThis 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.\n\n## How to install\n\nGleeter 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.\n\n### NixOS and nixpkgs\n\nIf you have [Nix flakes](https://nixos.wiki/wiki/flakes) enabled, you can run Gleeter directly with `nix run github:massix/gleeter -- \u003ccommand\u003e`, replacing `\u003ccommand\u003e` with `latest`, `random`, `id \u003cnumber\u003e`, 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:\n\n```nix\n{\n  inputs.nixpkgs.url = \"github:nixos/nixpkgs/nixos-unstable\";\n  inputs.gleeter.url = \"github:massix/gleeter\";\n\n  outputs = inputs@{ nixpkgs, gleeter, ...}:\n  let\n    pkgs = import nixpkgs {\n      system = \"aarch64-darwin\";\n      overlays = [ gleeter.overlays.default ];\n    };\n  in\n  {\n    # From here on, you can use pkgs.gleeter\n  };\n}\n```\n\n### Docker\n\nGleeter 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.\n\nTo run Gleeter with Docker, use the following command:\n\n```bash\ndocker run --rm massix86/gleeter:\u003cversion or latest\u003e -- \u003ccommand\u003e\n```\n\nReplace `\u003cversion or latest\u003e` with the desired version of Gleeter, or use `latest` to get the most recent version. Replace `\u003ccommand\u003e` with the Gleeter command you want to run (e.g., `latest`, `random`, `id 42`). The `--rm` flag automatically removes the container when it exits.\n\nFor example, to fetch a random comic using the latest version of Gleeter, you would run:\n\n```bash\ndocker run --rm massix86/gleeter:latest -- random\n```\n\nTo use a configuration file with the Docker image, follow these steps:\n\n1.  Create a `config.toml` file with your desired configuration settings. For example:\n\n    ```toml\n    random_start = 100\n\n    [screen]\n    max_cols = 100\n    max_lines = 30\n\n    [[alias]]\n    name = \"bobbytables\"\n    type = \"id\"\n    id = 987\n\n    [[alias]]\n    name = \"rnd\"\n    type = \"random\"\n\n    [[alias]]\n    name = \"l\"\n    type = \"latest\"\n    ```\n\n2.  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:\n\n    ```bash\n    docker run --rm -v $(pwd)/config.toml:/app/config.toml -e GLEETER_CONFIG_FILE=/app/config.toml massix86/gleeter:latest -- bobbytables\n    ```\n\n    This command does the following:\n\n    *   `--rm`: Automatically removes the container when it exits.\n    *   `-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.\n    *   `-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.\n    *   `massix86/gleeter:latest`: Specifies the Docker image to use.\n    *   `-- bobbytables`: Tells Gleeter to fetch the comic pointed by the bobbytables alias.\n\nMake sure that the `config.toml` file exists in the current directory when you run the Docker command.\n\n### Other systems\n\nTo 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.\n\n## Contributions\n\nAll contributions are welcome! Feel free to open pull requests or issues to help improve the project.\n\n## License and copyrights\n\nGleeter is licensed under the MIT License. See [LICENSE.txt](LICENSE.txt) for details.\n\nAll 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.\n\nI am in no way responsible for the content of the xkcd comics. All attributions and inquiries should be directed to Randall Munroe.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmassix%2Fgleeter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmassix%2Fgleeter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmassix%2Fgleeter/lists"}