Malíček provides a REST interface for Alík.cz.
An experimental, toy middle layer that serves as an abstraction of Alík's features. Currently limited to chat.
Due to Alík's limitations, this service is inherently unsafe and using a public instance is not recommended. See below for details.
Malíček sits between the user and Alík, translating REST requests into standard Alík web page requests, munching the responses and presenting them in machine-friendly, lightweight JSON.
Since Alík does not provide authentication methods for third parties, Malíček needs to handle the user's username and password directly, passing it through and holding the session cookies. In turn, Malíček issues its own session cookies for the REST interface. The API is documented below. Both request and response bodies must be in JSON.
No actions are performed automatically on the user's behalf. Each REST request directly translates into a synchronous Alík request.
The code relies heavily on regular expressions for parsing Alík's responses. Unfortunately, this is necessary as the responses are often broken, invalid HTML. The parsing code is therefore very fragile and needs to be updated whenever Alík changes. Fortunately that doesn't happen very frequently.
Reponses are in JSON, request bodies must also be in JSON.
Returns 200
for authenticated sessions, 401
otherwise.
{
"app": "malicek",
"version": "0.1.12",
"agent": "Malicek/v0.1.12",
"state": true
}
Provides the app identification, its name, version, its User-Agent string, and whether the session is active or not.
Creates a session with the following request:
{
"user": "username",
"pass": "p4s5w0rd"
}
Redirects to /
.
Destroys the session. Redirects to /
.
Provides generic user status information:
- Whether they have any in-mail.
- How many users are currently online.
- Their points balance (kačky).
{
"mail": 3,
"people": 42,
"cash": 120031
}
Unauthenticated sessions get redirected to /
.
Provides a list of currently available rooms and their users.
[
{
"id": "alik",
"name": "Alík",
"users": [],
"allowed": "all"
},
{
"id": "tajny-stul",
"name": "Tajný stůl!",
"users": [
{
"link": "foo",
"name": "FoO",
"sex": "unisex",
"admin": [
"admin"
]
},
{
"link": "bar",
"name": "bar",
"sex": "girl",
"admin": []
}
],
"allowed": "friends"
}
]
allowed
can be either all
, friends
, girls
or boys
.
sex
can be either boy
, girl
or unisex
.
admin
is currently either an empty list or a list containing a single
item, admin
.
Unauthenticated sessions get redirected to /
.
Gets the list of curently visible messages in the selected room. Joins the room on the first request (although see the Shortcomings section).
A detailed list of users in the room is also provided.
{
"name": "Super stůl",
"topic": "Stůl o všem a pro všechny",
"creator": "FoO",
"allowed": "all",
"users": [
{
"id": null,
"name": "contyk",
"link": "contyk",
"since": "4:44",
"last": "4:44",
"admin": [],
"sex": "boy",
"age": 415
}
],
"messages": [
{
"nick": "contyk",
"color": "#424242",
"message": "Hmmm...",
"avatar": "https://o00o.cz/obrazky/DF/QL/XDZ-avatar.png",
"time": "04:56:35",
"private": [],
"type": "chat",
"event": null
},
{
"nick": null,
"color": null,
"message": "Kamarád contyk si přisedl ke stolu.",
"avatar": null,
"time": "04:44:00",
"private": [],
"type": "system",
"event": {
"type": "join",
"source": "contyk",
"target": null
}
}
]
}
User id
is null
for self, otherwise it's the numerical system ID,
usable for private messaging.
Messages are sorted by the most recent first.
Message time
can be null
if timestamps are disabled.
Message avatar
can be null
if avatars are disabled or in the case of
system messages.
Message color
can be a bogus (but valid) value if colors are disabled,
or null
for system messages.
private
contains a list of nicks the message is intended for. If
empty, the message is public.
Messages with null
as the private recipient are filtered out. These
can be used for keepalive messages.
For message of type
system
, event
may contain additional data
about the message. Currently supported types include join
, part
,
kick
, oper
, clear
, lock
and unlock
. Most set the source
,
kick
sets the target
.
Room settings can also be queried with ?query=settings
.
{
"color": "#424242",
"refresh": 5,
"highlight": true,
"colors": true,
"system": true,
"time": true,
"avatars": true
}
Own's message color is represented by color
. refresh
is the number
of seconds between Alík's own refreshes. highlight
highlights one's
name on Alík, colors
toggles whether other users' colors are shown,
system
toggles whether system messages are shown, time
toggles
whether timestamps are shown, and avatars
toggles the visibility of
user avatars in messages.
Unauthenticated sessions get redirected to /
.
Sends a message or leaves the selected room.
To post a message:
{
"action": "post",
"to": 0,
"message": "Test message",
"color": "#424242"
}
Where message
is the message to send, color
is the message color,
and to
is the recipient of the message as their numerical ID. A
special value of 0
means the message is public. Negative values send
broken messages, potentially useful for keepalive.
To leave the room:
{
"action": "leave"
}
Redirects to /rooms/<id>
with post
or /rooms
with leave
.
Unauthenticated sessions get redirected to /
.
Gets the status of a supported game. Currently only Lednička is supported.
Unauthenticated sessions get redirected to /
.
{
"active": 1,
"defrost": 0,
"total": 0,
"additions": [
{
"who": "contyk",
"when": "18. listopadu v 4:41:12",
"amount": 6,
"method": "hodem"
}
]
}
Where active
deontes whether the user may play, defrost
whether that
method is available, total
holds the turn result after POST
(see
below), and additions
is a list of up to 50 last turns from all the
users.
additions
timestamps and methods are raw and generally in Czech.
Takes an action in a supported game. Currently only Lednička is supported.
Unauthenticated sessions get redirected to /
.
Attempts to play a turn in Lednička. Expects no request body. The
method
can be specified as an optional query parameter, e.g.
?method=k
.
Methods are passed directly to Alík. Currently supported methods
include 1
, h
, m
, M
, d
, k
, c
, r
and o
.
If no method is specified, Malíček will choose a method with the highest potential yield automatically.
Redirects to /games/lednicka
.
Serves a file from the public
directory.
If malicek.tar.gz
is requested and the file doesn't exist or is older
than any of the currently used source files, Malíček creates a gzip'd
tarball of itself and serves that file.
A very simple web client, suitable for handheld devices, is bundled; see the Deployment section for how to access it.
Additionally, a proof-of-concept curses-based client can be found in
public/cli.pl
.
An IRC gateway client, mlck, is also available.
Testing can be done directly with CUrl or any similar tool. See the Testing section below for further information.
Due to Alík's chat design, it is impossible to join a room where you already are without a valid chat cookie. A simple workaround lies in joining a different room to obtain the said chat cookie, leave, and join the original room. As an automatic workaround, Malíček attempts to leave every room before joining if it doesn't have a chat cookie already.
Additionally Alík doesn't let users directly know when they've been kicked out or have idled out. Malíček could support additional workarounds to detect these situations but currently does not.
Room admin features are currently unsupported.
The provided test.sh
can be used for simple GET
tests. Login
information must be provided in ~/.malicek
via user
and pass
variables. curl
and jq
must be installed.
echo user=foo > ~/.malicek
echo pass=password >> ~/.malicek
./test.sh rooms
./test.sh games/lednicka
./test.sh logout
A Containerfile
is provided to build a local container to assist with
this if you cannot run Malíček directly. Furthermore, a a Makefile
is
provided to make the container process easier.
make build
make start
make stop
make clean
Malíček is written in Perl and has several module dependencies:
- perl 5.16 or later
- Archive::Tar
- Dancer2
- File::stat
- File::Temp
- HTML::Entities
- HTTP::Cookies
- LWP::UserAgent
Dancer2 must support JSON serialization and Simple session caches.
To run a simple endpoint, run Malíček directly and connect to port 3000:
./malicek.pl
To provide the bundled web application and TLS support (recommended),
configure a forward proxy that maps /
requests to /app/
, and /api/
requests to /
.
An example Nginx configuration snippet.
server {
...;
port_in_redirect off;
...;
location /api/ {
proxy_pass http://192.168.0.10:3000/;
proxy_redirect / $scheme://$host/api/;
proxy_set_header Host malicek;
proxy_set_header X-Forwarded-For $proxy_add_x_forwaded_for;
}
location / {
proxy_pass http://192.168.0.10:3000/app$uri$is_arg$args;
proxy_set_header Host malicek;
proxy_set_header X-Forwarded-For $proxy_add_x_forwaded_for;
}
Petr Šabata [email protected], 2019-2023
Code licensed under MIT/X. See public/LICENSE.txt
for details.
Additionally, the public/favicon.png
image is a glyph from the Noto
Emoji typeface, distributed under SIL Open Font License by
Google.