commit
dea7f997ea
@ -0,0 +1,69 @@
|
||||
#+TITLE: Sico: Rockie's Robot Butler
|
||||
|
||||
* Installation
|
||||
|
||||
=sico= will soon be installable via MELPA. Otherwise you can add this directory to your =load-path=
|
||||
and =(require 'sico)=.
|
||||
|
||||
* Usage
|
||||
|
||||
** Basic Usage
|
||||
|
||||
- src_emacs-lisp[:exports code]{(customize-group 'sico)}
|
||||
- At the bare minimum, customize =sico-rooms= and =sico-users=
|
||||
- You may also want to customize =matrix-homeserver-base-url=
|
||||
- =M-x sico-start= will connect Sico to the Matrix homeserver and begin polling for events
|
||||
|
||||
** Extending
|
||||
|
||||
*** Customizing the Note Taker
|
||||
|
||||
The note taker is a simple wrapper around =org-capture=, which gives you a nice simple wedge with
|
||||
which to get your way in to an org-mode document
|
||||
|
||||
Begin by defining a capture-template:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :exports code
|
||||
(add-to-list 'org-capture-templates
|
||||
'("A"
|
||||
"Automatic note taking using Sico"
|
||||
entry
|
||||
(file org-default-notes-file)
|
||||
"* %c :NOTE:NOEXPORT:"
|
||||
:immediate-finish t))
|
||||
#+END_SRC
|
||||
|
||||
The important bits here are:
|
||||
- The usage of =%c=, which will be expanded to the head of the kill-ring. Sico stores the note in
|
||||
there as an easy way to push it in to Org-Mode.
|
||||
- The usage of =:immediate-finish t=, since Sico is not helping you do interactive things over
|
||||
Matrix, you need to make sure the Capture dialog doesn't stay open.
|
||||
|
||||
Do read the [[http://orgmode.org/manual/Capture-templates.html][Org-Mode documentation on Capture Tepmlates]] (=(info "org#Capture templates")= inside of
|
||||
Emacs), as it is quite helpful.
|
||||
|
||||
*** New Listeners
|
||||
|
||||
Creating new listeners is fairly trivial. Create a function which takes a single argument, which is
|
||||
the sexp representation of a Matrix.org [[http://matrix.org/docs/spec/r0.0.0/client_server.html#room-events][room event]]. It can do whatever you want it to do with that
|
||||
event, for a minimal example, see the source for =sico-capture-note=.
|
||||
|
||||
Once you have your function defined, add a cons to the alist =sico-listeners= where the car is the
|
||||
regexp that will match the event, and the cdr is the symbol for the function you've just defined.
|
||||
|
||||
* Contributing
|
||||
|
||||
|
||||
To submit patches:
|
||||
- Clone the repo
|
||||
- Create a git branch, code in a branch.
|
||||
- When you're done, send me a =git format-patch= style patch
|
||||
- =git format-patch --to ryan@whatthefuck.computer master..HEAD > YOURBRANCHNAME.patch=
|
||||
- Mail that patch to me at [[mailto:ryan@whatthefuck.computer][ryan@whatthefuck.computer]], and I will integrate it.
|
||||
- Or send it to me on Matrix, I'm =@rrix:whatthefuck.computer=
|
||||
|
||||
Project Discussion happens in =#ft_kickass:whatthefuck.computer=
|
||||
|
||||
* License
|
||||
|
||||
See LICENSE in the root of the repository for legal information.
|
@ -0,0 +1,160 @@
|
||||
;;; sico.el --- A personal assistant for Emacs and Matrix.org
|
||||
|
||||
;; Copyright (C) 2015 Ryan Rix
|
||||
;; Author: Ryan Rix <ryan@whatthefuck.computer>
|
||||
;; Maintainer: Ryan Rix <ryan@whatthefuck.computer>
|
||||
;; Created: 20 December 2015
|
||||
;; Keywords: web
|
||||
;; Homepage: http://doc.rix.si/matrix.html
|
||||
;; Package-Version: 0.0.1
|
||||
;; Package-Requires: ((matrix-client "0.1.0"))
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; sico.el is free software: you can redistribute it and/or modify it under the
|
||||
;; terms of the GNU General Public License as published by the Free Software
|
||||
;; Foundation, either version 3 of the License, or (at your option) any later
|
||||
;; version.
|
||||
;;
|
||||
;; sico.el is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
;; A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License along with
|
||||
;; this file. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Sico is your personal robot butler. Sico acts as an interface to a running
|
||||
;; Emacs session over the Matrix.org RPC network. Run `sico-start' and it will
|
||||
;; jump in to your favorite Matrix rooms and lend a hand when you need it.
|
||||
|
||||
;; To get started, customize, at the minumum `sico-rooms' and `sico-users'. You
|
||||
;; may also have to customize `matrix-homeserver-base-url', if you are
|
||||
;; connecting to a non-standard homeserver.
|
||||
|
||||
;; Right now Sico is in early development, supporting only the ability to
|
||||
;; capture single-line notes to your Org-Mode. The docstring for
|
||||
;; `sico-capture-note-template' explains how to set this up.
|
||||
|
||||
;; Future work and plans:
|
||||
;; - Render an Agenda as HTML and send it over Matrix
|
||||
;; - Schedule timers
|
||||
;; - Evaluate functions from a whitelist
|
||||
;; - Notify you for anything using the `sauron' library
|
||||
;; - Notify you for anything using the `notifications' library
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'matrix-client)
|
||||
(require 'matrix-api)
|
||||
|
||||
(defgroup sico nil "Customization options for Sico, your
|
||||
Matrix/Emacs robot butler."
|
||||
:prefix "sico")
|
||||
|
||||
(defcustom sico-rooms nil
|
||||
"Rooms the sico should join."
|
||||
:type '(repeat string)
|
||||
:group 'sico)
|
||||
|
||||
(defcustom sico-users nil
|
||||
"List of users that are allowed to interact with the sico."
|
||||
:type '(repeat string)
|
||||
:group 'sico)
|
||||
|
||||
(defcustom sico-capture-note-regexp "^!note\\b\\(.*\\)"
|
||||
"Regular expression defining how to capture notes.
|
||||
|
||||
Handler expects (match-string 1) to include the note."
|
||||
:type 'string
|
||||
:group 'sico)
|
||||
|
||||
(defcustom sico-capture-note-template "A"
|
||||
"The org-capture-templates key that will be used to store a note.
|
||||
|
||||
Templates should assume that the body will be given as the head
|
||||
of the kill ring, accessible from inside a template as %c. A
|
||||
standard org-capture-template could look like:
|
||||
|
||||
'(\"A\" \"Automatic note taking using Sico\"
|
||||
entry
|
||||
(file org-default-notes-file)
|
||||
\"* %c :NOTE:NOEXPORT:\"
|
||||
:immediate-finish t)")
|
||||
|
||||
(defcustom sico-listeners (list (cons sico-capture-note-regexp 'sico-capture-note))
|
||||
"Alist containing a set of (`regexp' . `function') cells.
|
||||
The regexps that match will get the entire line passed to it as a
|
||||
single argument."
|
||||
:type '(alist :value-type (group function)))
|
||||
|
||||
(defcustom sico-connection-string
|
||||
"Hello, my name is Sico and I am pleased to be of service."
|
||||
"The message the bot says upon joining each room.")
|
||||
|
||||
(defvar sico-current-end-token nil)
|
||||
(defvar sico-running nil)
|
||||
|
||||
(defun sico-start ()
|
||||
"Start Assitant and connect it to the Matrix homeserver."
|
||||
(interactive)
|
||||
(unless matrix-token
|
||||
(matrix-client-login))
|
||||
(sico-join-rooms)
|
||||
(sico-start-polling)
|
||||
(setq sico-running t))
|
||||
|
||||
(defun sico-stop ()
|
||||
"Tell sico to stop polling."
|
||||
(setq sico-running nil))
|
||||
|
||||
(defun sico-start-polling ()
|
||||
"Begin polling for events with the [`sico-poll-callback'] as the handler."
|
||||
(matrix-event-poll sico-current-end-token
|
||||
10
|
||||
'sico-poll-callback))
|
||||
|
||||
(defun sico-join-rooms ()
|
||||
"Join the rooms that Sico is configured to be in."
|
||||
(dolist (room sico-rooms)
|
||||
(matrix-join-room room)
|
||||
(matrix-send-message room sico-connection-string)))
|
||||
|
||||
(defun sico-poll-callback (data)
|
||||
"The callback triggered by [`matrix-event-poll'].
|
||||
|
||||
DATA comes directly from the Matrix homeserver, massaged in to a
|
||||
sexp by [`json-parse']"
|
||||
(if (eq (car data) 'error)
|
||||
(message "%s" data)
|
||||
(let* ((chunk (matrix-get 'chunk data)))
|
||||
(setq sico-current-end-token (matrix-get 'end data))
|
||||
(mapc (lambda (item)
|
||||
(dolist (cell sico-listeners)
|
||||
(let ((content (matrix-get 'content item))
|
||||
(type (matrix-get 'type item)))
|
||||
(when (and content
|
||||
(string= type "m.room.message")
|
||||
(string-match (car cell)
|
||||
(matrix-get 'body content)))
|
||||
(funcall (cdr cell) item))))) chunk)))
|
||||
(when sico-running
|
||||
(sico-start-polling)))
|
||||
|
||||
(defun sico-capture-note (event)
|
||||
"Capture a note from EVENT."
|
||||
(let* ((content (matrix-get 'content event))
|
||||
(body (matrix-get 'body content))
|
||||
(matching (string-match sico-capture-note-regexp body))
|
||||
(match (match-string 1 body))
|
||||
(user-id (matrix-get 'sender event))
|
||||
(room-id (matrix-get 'room_id event)))
|
||||
(when (member user-id sico-users)
|
||||
(kill-new match)
|
||||
(org-capture nil sico-capture-note-template)
|
||||
(matrix-send-message room-id
|
||||
(format "I have captured: \"%s\"" match)))))
|
||||
|
||||
(provide 'sico)
|
||||
;;; sico.el ends here
|
Loading…
Reference in new issue