Fortune

Lukasz
7 min readMar 27, 2021

--

Photo by Elena Koycheva on Unsplash

It looks like “fortune” (the computer program described in Wikipedia as a Fortune (Unix)) has been around for something like 40 years already. It is a simple program that prints out a randomized quotation from its database, e.g.:

➜  ~ fortune
The first duty of a revolutionary is to get away with it.
-- Abbie Hoffman

“Nothing special” at the first glance, but — it is really… addictive
(and probably there is a lot of addicted users — note that 40 years!).

Thinking of “where to start” (when I decided to level up my addiction) I have focused on fortune’s databases that can be found among the debian project packages. Interesting thing here is (I definitely have to learn more about how the debian project “works”) that there is much more (fortunes) packages on packages.debian.org than exist in the debian repository — salsa.debian.org (I will have to figure out why, in the free time :))

But to the point — as a result of my interest in fortunes I have started a small project (a few in fact) — here, for instance, you can find the “fortune” web interface: https://fortune.luka.sh (for the time being the “database” contains only english fortunes from the debian’s fortunes package which “upstream” repository is fortune-mod so all fortunes files, from that package, can be also found here).

fortune.luka.sh

This project has been made for fun (as a “test use case” of React), but right now some plans appeared ;)

The “project” diagram looks like this:

Here is the list of links visible (or not) above:

  1. The GitHub repository: https://github.com/shlomif/fortune-mod
  2. There are two forks right now on salsa — and i’m not sure which one (if at all any) is used to build the deb package (the package source information suggests yet another repository).
  3. Fortune (lfortune) on Pypi: https://pypi.org/project/lfortune
  4. Fortune Api project: https://github.com/lbacik/fortune-api
  5. Fortune Api — docker image: https://hub.docker.com/r/lbacik/fortune-api
  6. Fortune Api url: https://fortune-api.luka.sh
  7. Fortune front — React project: https://github.com/lbacik/fortune-front-react
  8. https://fortune.luka.sh

In that scenario updating the fortunes databases don’t seem to be an easy task — but switching to “sid” or “experimental” debian branch or even clones and builds the fortunes package during the fortune-api image build process can allow for updating the fortunes databases more frequently than “once per few years” (when the new stable debian is released).

However — right now it is not a problem.

Below you can find a short summary of some of the project components.

Cli

Cli is a simple addition to the python-fortune library (the library to read the fortune files) — and to be honest, that cli is much less functional than the original fortune program’s cli (from the fortune-mod package). It was not a point to develop the replacement for the original fortune command — the point here was to prepare the (python) library that could be used by the API service (so for the time being the cli interface is not the most important one).

The project (fortune library and its cli) can be installed through the pip (python3 version):

$ pip install lfortune

It is available on pypi.org under the “lfortune” name as the “fortune” name has been already taken. When installing the package through the pip you have to remember also to separately install the fortune databases (files) and to appropriate configure the lfortune program (to let it know where the fortune’s files can be found — if you are interested check the README.md file for more details).

It can be also “tested” (or used) locally as a docker container (image available on docker.hub contains the fortunes database already and it is ready to be used just out-of-the-box):

➜  ~ docker run --rm lbacik/fortune
Ideas don't stay in some minds very long because they don't like
solitary confinement.

Try: docker run --rm lbacik/fortune --help (or lfortune --help ) to find all available options.

Api

Api contains two methods — (1) GET (there are two GET methods visible but I just couldn’t find (yet) the way to merge the “default” invocation with the invocation with specified path) and (2) POST. The POST method can be used to define more precisely the set of fortunes from which the user would like to randomly select one (it works very similarly to the original fortune “percentage approach”).

Let’s check it all on examples:

➜  ~ lfortune
Consider the postage stamp: its usefulness consists in the ability to stick to one thing till it gets there.
-- Josh Billings

So invoking the “fortune” program (here the lfortune cli) without any parameters is the same like using the GET /fortune method (without the path argument):

➜  ~ http --verify no https://fortune-api.luka.sh/fortune/
HTTP/1.1 200 OK
...
{
"file": "work",
"fortune": "If we could sell our experiences for what they cost us, we would\nall be millionaires.\n\t\t-- Abigail Van Buren\n",
"index": 177
}

(as a http client i’m using here the https://httpie.io, but you can find the examples with curl, generated by the swagger library, on the api’s page).

Now, with the path:

➜  ~ lfortune --show-fortune-data computers
FORTRAN is for pipe stress freaks and crystallography weenies.
[computers / 269]

The --show-fortune-data parameter has been used to check the fortune source (file: computers, index: 269).

➜  ~ http --verify no https://fortune-api.luka.sh/fortune/computers
HTTP/1.1 200 OK
...
{
"file": "computers",
"fortune": "Like punning, programming is a play on words.\n",
"index": 457
}

And finally — we can also use lfortune like this:

➜  ~ lfortune --show-fortune-data 20% computers 20% fortunes people
It is always the best policy to tell the truth, unless, of course,
you are an exceptionally good liar.
-- Jerome K. Jerome
[people / 531]

That means 20% chance for fortune from the computers’ db, 20% for the fortune from the fortunes’ db and 60% (because it is what left) of a chance to select the fortune from the people’s db. And to be able to use API in such a way (to indicate more than one database file with optional “chance to hit”) we can use the POST method:

➜  ~ http --verify no POST https://fortune-api.luka.sh/fortune/ sources:='[{"path": "computers", "probability": 20}, {"path": "fortunes", "probability": 20},{"path": "people", "probability": 60}]' -v
POST /fortune/ HTTP/1.1
...
{
"sources": [
{
"path": "computers",
"probability": 20
},
{
"path": "fortunes",
"probability": 20
},
{
"path": "people",
"probability": 0
}
]
}
HTTP/1.1 200 OK
...
{
"file": "fortunes",
"fortune": "Better hope the life-inspector doesn't come around while you have your\nlife in such a mess.\n",
"index": 37
}

BTW, with jq installed, you can easly access each JSON property:

➜  ~ http --verify=no https://fortune-api.luka.sh/fortune/ --body | jq -r .fortune
Practice is the best of all instructors.
-- Publilius

Front

For preparing the frontend I have used React which I definitely wanted to try. However, I’ve focused on class-based components while it is not the advised approach in the React world right now I guess (as I understand that short article titled: Classes confuse both people and machines). BTW — for all interested in React — the post linked below is a really interesting one :)

In my case, the above article made my interest in React even stronger than before. And — that is my first try which I’m definitely going to continue! (At least I must rewrite the current “version” of the project to use hooks instead of class-based components when only I find a while).

So the current React code can be found in that repository (as mentioned earlier), and the “live” version — as for the time of writing that post — can be checked here: https://fortune.luka.sh

It looks simple and in fact, it is a simple app. However, I have already managed to use Redux within it to control/manage its state.

The biggest disadvantage of that part of the project is that I have not found (yet) the proper way to manage the app configuration. What I mean by that is that as you can see on the above diagram (and how that kind of apps work) the frontend (run in a user browser) connects to the backend part (api) — the problem here is (in case of this particular app) that the backend’s url is hardcoded in the React (frontend) code. It (the backend’s url) has to be provided during the build process (as an environment variable) but when the application is already built (and deployed as “static files” in the docker container with Nginx for instance) then there is no way to change that url. It is something I will have to focus on in the nearest future — what solution to choose to be able to provide the backend url during the frontend container start? Without it providing such a frontend container (e.g. on dockerhub) doesn’t make much sense.

Summary

If it sounds interesting, please visit the Fortune web page — I also encourage you to share your thoughts about the project (any feedback is welcome!).

Note: there are still a few bugs that need to be addressed, but (1) they (the known ones) don’t look as major bugs, (2) I’m on that :)

Thanks for reading!

--

--