Hacker News new | past | comments | ask | show | jobs | submit login

Ooooh... sounds like a fun challenge :D I've typed this out straight into the HN input box so it almost certainly doesn't work and will be full of typos and escaping bugs, but for illustrative purposes: here's what a ~8 line Matrix client for basic bi-directional chat in a given room could look like (complete with login) in almost-bash, with deps only on curl and jq:

    USERNAME='@whoever:matrix.org'; SERVER='https://matrix.org'; ROOM='#test:matrix.org'
    read -s -p "Password for $USERNAME:" PASSWORD
    TOKEN=`curl -X POST $SERVER/_matrix/client/r0/login --data "{ 'type': 'm.login.password', 'login': '$USERNAME', 'password': '$PASSWORD' }" | jq .access_token`
    ROOM_ID=`curl $SERVER/_matrix/client/r0/directory/room/$ROOM | jq .room_id`; curl "$SERVER/_matrix/client/r0/join/$ROOM_ID?access_token=$TOKEN"
    (while true; do SYNC=`curl -s $SERVER/_matrix/client/r0/sync?access_token=$TOKEN&timeout=30000&since=$SINCE`
     echo $SYNC | jq ".rooms.join.$ROOM_ID.timeline"
     SINCE=`echo $SYNC | jq .next_batch`; done) &
    while true; do read -p "<$USERNAME> " INPUT; `curl -s -X POST $SERVER/_matrix/client/r0/rooms/$ROOM_ID/m.room.message?access_token=$TOKEN --data "{ 'body': '$INPUT', 'msgtype': 'm.text'}"`; done
Spread out a bit more with comments:

    # set your matrix ID & server URL, and the room you want to chat in:

    # prompt for a password; log in and grab an access_token
    read -s -p "Password for $USERNAME:" PASSWORD
    TOKEN=`curl -X POST $SERVER/_matrix/client/r0/login --data "{ 'type': 'm.login.password', 'login': '$USERNAME', 'password': '$PASSWORD' }" | jq .access_token`

    # resolve the room alias (#test:matrix.org) to a room ID (!vfFxDRtZSSdspfTSEr:matrix.org)
    ROOM_ID=`curl $SERVER/_matrix/client/r0/directory/room/$ROOM | jq .room_id`

    # check that you're joined to the room (redundant if you know you're already there)
    curl "$SERVER/_matrix/client/r0/join/$ROOM_ID?access_token=$TOKEN"

    # set a background loop running to receive messages, and use jq to filter out the
    # messages for the room you care about from the sync response.  For now we print them
    # as JSON pretty-printed by jq, but that's not too bad.
    (while true;
        do SYNC=`curl -s $SERVER/_matrix/client/r0/sync?access_token=$TOKEN&timeout=30000&since=$SINCE`
        echo $SYNC | jq ".rooms.join.$ROOM_ID.timeline"
        SINCE=`echo $SYNC | jq .next_batch`
    done) &

    # set a foreground loop running to prompt for your own messages and send them
    # into the room as plaintext.
    while true;
        do read -p "<$USERNAME> " INPUT;
        `curl -s -X POST $SERVER/_matrix/client/r0/rooms/$ROOM_ID/m.room.message?access_token=$TOKEN --data "{ 'body': '$INPUT', 'msgtype': 'm.text'}"`
If I have time I'll actually try running & debugging this to be usable and edit the post (but got to run into a meeting now :( ).

Needless to say, this'd be much prettier on Python - and you'd prolly want to use a nice SDK like https://github.com/poljar/matrix-nio (see https://matrix.org/blog/2019/07/03/usage-of-matrix-nio/) so you get things like E2E Encryption for free. But doing it in plain bash hopefully gives more of an idea.

Too late to edit the original post, but thanks to anoa for fixing up my crappy bash there's a version that actually works now at https://github.com/ara4n/random/blob/master/bashtrix.sh. The uglinesses are mainly bash's fault rather than matrix's ;P

comedy punchline; turns out that people are actually using this monstrosity in the wild:

> I used bashtrix for about 4 hours this morning. Why, you ask? I didn't have the bandwidth, latency or reliability to use Riot. I ended up using bashtrix on one of my servers through mosh...

> Could have done with scroll back (the screen fills up with 'null' and '[]' – the latter, I started to recognise as marking read receipts and/or perhaps presence) but shrank the font size down and made the most of it :p. Worked Rather well considering

Wow, definitely wasn’t expecting this! Very cool

I think it’s important to not be “using a library” since the test case is hopefully that the protocol is easy enough to get something barebones with even super baseline tools. Your bash script obviously matches this requirement

My reference here is the excellent Haskell Wiki “Build tour own IRC bot”. Really captures the simplicity of the protocol


Nice! I tried this though and it asks me for a password (unsure if for creating an account or logging in to existing one, code suggests for login) and I tried both blank and random one, but get `Unrecognized request` as a response for the first request.

This seems to indicate it's missing a register step, and then it fails to be as simple as IRC where you just set nickname and connect to a server. Sometimes the server asks you to identify yourself, but that's up to the server, not the protocol.

yeah, sorry, i put a login stage in there but not a registration step (which is a bit more fiddly as registration typically requires solving a captcha to prevent spam). so you'd need to register via riot.im or some other client first.

That said, Matrix also supports guest accounts (which are quite locked down to prevent abuse, but would work for this example) - so the /login request could be replaced by calling /register with type=guest which would then do what you want.

edit: it would look something like this (7 lines now!):

    SERVER='https://matrix.org'; ROOM='#test:matrix.org'
    TOKEN=`curl -X POST $SERVER/_matrix/client/r0/register --data "{ 'kind': 'guest' }" | jq .access_token`
    ROOM_ID=`curl $SERVER/_matrix/client/r0/directory/room/$ROOM | jq .room_id`; curl "$SERVER/_matrix/client/r0/join/$ROOM_ID?access_token=$TOKEN"
    (while true; do SYNC=`curl -s $SERVER/_matrix/client/r0/sync?access_token=$TOKEN&timeout=30000&since=$SINCE`
     echo $SYNC | jq ".rooms.join.$ROOM_ID.timeline"
     SINCE=`echo $SYNC | jq .next_batch`; done) &
    while true; do read -p "> " INPUT; `curl -s -X POST $SERVER/_matrix/client/r0/rooms/$ROOM_ID/m.room.message?access_token=$TOKEN --data "{ 'body': '$INPUT', 'msgtype': 'm.text'}"`; done

Nice work! I've actually been working on writing something like this for a while now.

Do take a look at https://gitlab.com/darnir/matrix-shell-suite

It's a basic matrix client written in pure POSIX sh.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact