whatsxmpp/db.lisp
eta a02c0fdc79 Handle chats being modified; allow the user to refresh all groups
- We now handle the :chat-modified event from whatscl, and re-fetch group
  metadata when we receive it.
- Of course, this doesn't actually help for resources which are in the MUC at
  update time; they'll need to reconnect until we have code to handle message
  stubs properly.
- Chat metadata reception now generates a message to the user, both for ease of
  debugging, and also to let them know that something happened to a groupchat
  they're in which they would otherwise not be aware of at all. (A handy link is
  also provided, if you missed the invite and want to join a MUC you're not in.)
- The new `refresh-chats` admin command forces a refresh of all group metadata
  in one big go. (Also, the help text now displays the current version.)
2020-08-02 12:59:59 +01:00

136 lines
5.8 KiB
Common Lisp

(in-package :whatsxmpp)
(defun get-user-id (jid)
"Get the user ID of JID, or NIL if none exists."
(with-prepared-statement
(get-user "SELECT id FROM users WHERE jid = ?")
(let ((stripped (strip-resource jid)))
(bind-parameters get-user stripped)
(when (sqlite:step-statement get-user)
(first (column-values get-user))))))
(defun get-user-contact-localparts (uid)
"Returns a list of all contact localparts for UID."
(with-prepared-statements
((get-stmt "SELECT wa_jid FROM user_contacts WHERE user_id = ?"))
(bind-parameters get-stmt uid)
(loop
while (sqlite:step-statement get-stmt)
collect (sqlite:statement-column-value get-stmt 0))))
(defun get-user-chat-id (uid localpart)
"Get the user chat ID of LOCALPART for UID, or NIL if none exists."
(with-prepared-statements
((get-stmt "SELECT id FROM user_chats WHERE user_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt uid localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (id) get-stmt
id))))
(defun get-user-chat-subject (uid localpart)
"Get the user chat subject of LOCALPART for UID, or NIL if none exists."
(with-prepared-statements
((get-stmt "SELECT subject FROM user_chats WHERE user_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt uid localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (subject) get-stmt
subject))))
(defun get-user-chat-resource (uid localpart)
"Get the user chat resource of LOCALPART for UID, or NIL if none exists."
(with-prepared-statements
((get-stmt "SELECT user_resource FROM user_chats WHERE user_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt uid localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (resource) get-stmt
(when (and resource (> (length resource) 0))
resource)))))
(defun get-participant-resource (chat-id localpart)
"Get the participant resource for LOCALPART in CHAT-ID, or NIL if none exists."
(with-prepared-statements
((get-stmt "SELECT resource FROM user_chat_members WHERE chat_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt chat-id localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (resource) get-stmt
(when (and resource (> (length resource) 0))
resource)))))
(defun get-user-chat-joined (uid localpart)
"Get the user chat resource of LOCALPART for UID, or NIL if none exists."
(with-prepared-statements
((get-stmt "SELECT ucj.jid FROM user_chats AS uc, user_chat_joined AS ucj WHERE uc.user_id = ? AND uc.wa_jid = ? AND uc.id = ucj.chat_id"))
(bind-parameters get-stmt uid localpart)
(loop
while (sqlite:step-statement get-stmt)
append (column-values get-stmt))))
(defun get-contact-name (uid localpart)
"Get a name for LOCALPART, a possible contact for the user with ID UID."
(with-prepared-statements
((get-stmt "SELECT name, notify FROM user_contacts WHERE user_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt uid localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (name notify) get-stmt
(or name notify (substitute #\+ #\u localpart))))))
(defun get-contact-status (uid localpart)
"Get the contact status text for LOCALPART, a possible contact for the user with ID UID."
(declare (type integer uid) (type string localpart))
(with-prepared-statements
((get-stmt "SELECT status FROM user_contacts WHERE user_id = ? AND wa_jid = ?"))
(bind-parameters get-stmt uid localpart)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (status) get-stmt
status))))
(defun insert-user-message (uid xmpp-id wa-id)
"Inserts a mapping between the message IDs XMPP-ID and WA-ID for the user UID."
(with-prepared-statements
((insert-stmt "INSERT INTO user_messages (user_id, xmpp_id, wa_id) VALUES (?, ?, ?)"))
(bind-parameters insert-stmt uid xmpp-id wa-id)
(sqlite:step-statement insert-stmt)))
(defun insert-user-chat (uid wa-id)
"Inserts a user chat with localpart WA-ID into the database for the user with UID."
(with-prepared-statements
((insert-stmt "INSERT INTO user_chats (user_id, wa_jid) VALUES (?, ?) ON CONFLICT DO NOTHING"))
(bind-parameters insert-stmt uid wa-id)
(sqlite:step-statement insert-stmt)))
(defun lookup-wa-msgid (uid wa-msgid)
"Look up the XMPP ID for the WhatsApp message ID WA-MSGID, when received for the user UID."
(with-prepared-statements
((get-stmt "SELECT xmpp_id FROM user_messages WHERE user_id = ? AND wa_id = ?"))
(bind-parameters get-stmt uid wa-msgid)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (xid) get-stmt
xid))))
(defun lookup-xmpp-msgid (uid xmpp-msgid)
"Look up the WhatsApp message ID for the XMPP message ID XMPP-MSGID, when received for the user UID."
(with-prepared-statements
((get-stmt "SELECT wa_id FROM user_messages WHERE user_id = ? AND xmpp_id = ?"))
(bind-parameters get-stmt uid xmpp-msgid)
(when (sqlite:step-statement get-stmt)
(with-bound-columns (wid) get-stmt
wid))))
(defun get-contact-localparts (uid)
"Get a list of contact localparts for the user with ID UID."
(with-prepared-statements
((get-stmt "SELECT wa_jid FROM user_contacts WHERE user_id = ?"))
(bind-parameters get-stmt uid)
(loop
while (sqlite:step-statement get-stmt)
collect (with-bound-columns (localpart) get-stmt localpart))))
(defun get-user-groupchats (uid)
"Get a list of groupchat info (cons pairs of LOCALPART . SUBJECT) for the user with ID UID."
(with-prepared-statements
((get-stmt "SELECT wa_jid, subject FROM user_chats WHERE user_id = ?"))
(bind-parameters get-stmt uid)
(loop
while (sqlite:step-statement get-stmt)
collect (with-bound-columns (localpart subject) get-stmt (cons localpart subject)))))