CL-RemiMatrix
CL-RemiMatrix is a library for Common Lisp that lets you interact with the
Matrix Protocol.
CL-RemiMatrix is entirely written and maintained by one person, Remilia Scarlet! If you want to support her and CL-RemiMatrix, you can buy her a coffee on Ko-Fi, or support her through Liberapay. Support is greatly appreciated for this volunteer effort ^_^
Releases
Releases can be found on the wiki.
How do I get set up?
Most dependencies can be installed via Quicklisp. Check the .asd file to see what you'll need. The CL-SDM and CL-RemiMarshal dependencies cannot, however, be installed from Quicklisp at the time of writing. This can instead be obtained from:
Once you have CL-SDM and CL-RemiMarshal, put it (and this library) somewhere where ASDF can find them, then run this in a REPL:
(asdf:load-system :cl-remimatrix)
Usage
There are three APIs, each with their own ASDF system and package:
- The Core API (system
cl-remimatrix-core, package:matrix-core) - The High Level API (system
cl-remimatrix, package:matrix) - The Bot API (system
cl-remimatrix-bot, package:matrix-bot)
The Core API is a low-level API that is a mapping between CLOS objects/Common Lisp functions and the Matrix Protocol. It's not exactly a 1:1 mapping, but it's very close. It uses CL-RemiMarshal in the background for all the serialization and deserialization.
;; Create a login request, get a response, then get the access token and
;; user ID.
(let* ((req (make-instance 'matrix-core:login-request/password
:identifier (make-instance 'matrix-core:identifier/matrix-id
:user "@user:example.com")
:password "some password"))
(resp (matrix-core:login req hostname)))
(format t "Access token: ~a~%" (matrix-core:login-response-access-token resp))
(format t "User ID: ~a~%" (matrix-core:login-response-user-id resp)))
And here is another example that sends an m.text message to a room. Note how
the MATRIX-CORE:SEND-EVENT function takes a MATRIX-CORE:ROOM-MESSAGE/M.TEXT
instance.
(matrix-core:send-event "some access token"
:m.room.message
"#room:example.com"
(make-instance 'matrix-core:room-message/m.text
:body "Hello, world!")
"example.com")
The High Level API builds on the Core API to provide something that's reduces
the amount of code you need to write, and also makes it a bit easier to use from
a REPL when needed. It also provides the MATRIX-CLIENT class, which abstracts
away a connection to a homeserver.
;; Sends a m.text message to the given room. Assumes you've created a
;; MATRIX:MATRIX-CLIENT and stored it in *MY-CLIENT*.
(matrix:send-message my-client "#room:example.com" "Hello, world!")
The Bot API provides a framework for creating bots, and builds on both the Core
and High Level APIs. The BOT class inherits from MATRIX:MATRIX-CLIENT, and
acts as a parent class for all bots. Once started, a bot will listen for events
in a separate thread, then dispatch the MATRIX-CORE:EVENT instances to various
methods depending on the event type. So, when implementing a bot, you basically
just subclass BOT, then specialize on the various event handler generic
functions. This package uses
lparallel for its parallel
programming tasks.
Here is an example of the Bot API, where a bot listens for messages that say
!ping and responds with Pong!:
(defparameter *bot* nil)
(defclass my-bot (remimatrix/bot:bot)
())
;; This will be called each time a :M.ROOM.MESSAGE event is encountered.
(defmethod remimatrix/bot:bot-event/message ((bot my-bot) (event matrix-core:event))
(when (equalp (matrix-core:room-message-body (matrix-core:event-content event))
"!ping")
(matrix:send-message bot event "Pong!")))
;; Sets *BOT* to a new MY-BOT instance, connects it to the server, and starts
;; listening for events.
(defun start-bot (username password hostname)
(setf *bot* (make-instance 'bot
:sync-time 5000
:name "Ms. Test Bot"
:hostname hostname))
(remimatrix/bot:bot-start *bot* username password))
;; Stops *BOT*.
(defun stop-bot ()
(remimatrix/bot:bot-stop *bot* t))
;; Start the bot, which will start listening for events in a new thread. The
;; default sync filter will handle m.room.message, m.room.member, and
;; m.room.power-levels events automatically.
(start-bot "foo" "bar" "baz")
Development
Style info
I use a slightly unorthodox style for my code. Aside from these differences, please use normal Lisp formatting.
- Keep lines 118 characters or shorter. Obviously sometimes you can't, but please try. Use 115 characters for Markdown files, though.
- I mark types using the form
T/..... For example,T/SOME-NEAT-TYPE. For predicates on these, useSOME-NEAT-TYPE-P. - No tabs. Only spaces.
How do I contribute?
- Go to https://nanako.mooo.com/fossil/cl-remimatrix/ and clone the Fossil repository.
- Create a new branch for your feature.
- Push locally to the new branch.
- Create a bundle with Fossil that contains your changes.
- Get in contact with Remilia via email, Fediverse, or open a ticket.
Contributors
- Remilia Scarlet - creator and maintainer
- Homepage: https://remilia.sdf.org/
- Fediverse: @remilia@social.cyberia9.org
- Email: zremiliaz@postzeoz.jpz My real address does not contain Z's
Links and Licenses
CL-RemiMatrix is under the GNU Affero General Public License version 3.
