An interface to the twister microblogging application from Emacs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.2 KiB

;;; twister.el --- A client for the twister distribute microblogging system
;; Copyright (C) 2014 Marcel van der Boom <marcel@hsdev.com>
;;; Commentary:
;; The original idea for this client was to minimally implement:
;; - make it possible to post to twister directly from Emacs;
;; - have autocompletion for 'known users' when posting a message
;; (this probably means defining a 'mode')
;; During the implementation of the above the following nice-to-have
;; came to mind:
;; - get a timeline in a buffer in emacs
;; - allow for replies
;; - allow for direct messages
;; - manage follow lists
;; - have avatars obviously. (including the checkmark, which is brilliant)
;;
;; Requirements
;; - json-rpc : https://github.com/mrvdb/elisp-json-rpc
;; This repository is included as submodule
;; Installation
;; (add-to-list 'load-path "/path/to/twister.el")
;; (require 'twister)
;; (setq twister-user "yournick")
;;
;;; Credits
;; - The elisp-json-rpc library of Christopher Wellons <wellons@nullprogrma.com>
;; does the real work
;;; Code:
(require 'json-rpc)
;; Configuration variables
(defgroup twister nil
"Using the twister microblogging system"
:tag "Microblogging"
:group 'applications
)
(defcustom twister-user "twister_user"
"The nickname you use on your twister instance. When posting
messages, this will be the name used"
:type 'string
:group 'twister)
(defcustom twister-rpcuser "user"
"The RPC username configured in the twister.conf file"
:type 'string
:group 'twister)
(defcustom twister-rpcpassword "pwd"
"The RPC password for the `twister-rpcuser configured in the
twister.conf file"
:type 'string
:group 'twitter)
(defcustom twister-host "localhost"
"Host where the twister daemon runs"
:type 'string
:group 'twister)
(defcustom twister-port 28332
"Port on which twister daemon runs and serves RPC commands"
:type 'integer
:group 'twister)
(defcustom twister-max-msgsize 140
"Maximum size of messages to post. Default of the server is 140.
If you want larger messages, you will need to enable automatic splitting
in the twister configuration."
:type 'integer
:group 'twister)
(defcustom twister-post-buffername "New Twister Message"
"Name of the buffer in which Twister Messages can be composed."
:type 'string
:group 'twister)
(defvar twister-post-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-c" 'twister-post-buffer)
10 years ago
(define-key map "\C-c\C-k" 'twister-close-post)
map) "Keymap for `twister-post-mode'.")
(define-derived-mode twister-post-mode text-mode "twister-post"
"Twister major mode for posting new messages."
;; Reason I want this:
;; - define autompletion on @ sign
;; - define specific key map for posting messages
)
;; Preliminary convention
;; tw-* methods -> sorta private for now, don't use directly
;; twister-* methods -> public API
(defun twister-rpc (method &rest params)
"Wrapper for the json-rpc method for twister use.
The connection is closed afer each use. This is not necessarily
the most effective. METHOD is the RPC method we are calling
while PARAMS contain the rest of the parameters."
(let* ((twisterd (json-rpc-connect
twister-host twister-port
twister-rpcuser twister-rpcpassword))
(result (apply 'json-rpc twisterd method params)))
(json-rpc-close twisterd)
result))
(defun tw-get-last-post(user)
"Get the last post of a user"
(let (obj (json-new-object))
(twister-rpc "getposts" 1
(vector (json-add-to-object obj "username" user)))))
(defun tw-get-next-k(user)
"Get the next 'k' for a user; this is a post sequence"
;; Data structure visible here: http://twister.net.co/?page_id=21
(+ 1 (plist-get
(plist-get
(elt (tw-get-last-post user) 0)
:userpost) :k)))
(defun twister-post(msg)
"Post msg to the configured twister daemon"
(interactive)
(twister-rpc "newpostmsg"
twister-user
(tw-get-next-k twister-user) msg))
(defun twister-post-region (begin end)
"Post the current region to twister"
(interactive "r")
(let ((selection (buffer-substring-no-properties begin end)))
(when (or (<= (length selection) twister-max-msgsize)
(y-or-n-p (format (concat "The message is %i characters long. "
"Do you still want to post? ")
(length selection))))
(message "Posting message...")
(twister-post selection))))
(defun twister-post-buffer()
"Post the current buffer to twister"
(interactive)
(twister-post-region (point-min) ( point-max)))
(defun twister-create-post ()
(interactive)
"Create a new buffer for writing a note"
(with-current-buffer (get-buffer-create twister-post-buffername)
(twister-post-mode)
(switch-to-buffer-other-window (current-buffer))
(fit-window-to-buffer (selected-window) 10 10)))
(defun twister-close-post ()
"Hide and kill the posting buffer if it is the special posting buffer."
(interactive)
(when (get-buffer twister-post-buffername)
(with-current-buffer twister-post-buffername
;; if the window is the sole window in its frame, delete-window will error
(if (window-parent) (delete-window))
(kill-buffer twister-post-buffername)
)))
(provide 'twister)
;;; twister.el ends here