* Ian Garmaise <ia...@phorixsol.com> [2021-02-27 21:59]:
> Hi Bob,
> 
> How do you send mail to mailing lists with BBDB?  Do you use a templating
> tool?  Also, how do you start a phone call?

Phone call may be started by using Android/Replicant/LineageOS devices
and Termux tools' https://termux.com command: `termux-telephony-call`

I am adding some of functions I am using, but cannot help in polishing
the package for you. If you need some more functions from here, I will
send you.

You can then send termux command `termux-telephony-call` and initiate
the call on Android/Replicant/LineageOS devices.

For sending SMS I am heavily using KDEConnect, but you may use termux
to send SMS by loading older library (ask me), or by using ShellMS
application.

Jean

;;; termux.el --- Termux functions. -*- lexical-binding: t; -*-

;; Copyright (C) 2016-2021 by Jean Louis

;; Author: Jean Louis <bugs@gnu.support>
;; Version: 0.1
;; Package-Requires: (rcd-utilities rcd-cf rcd-db-init rcd-db time-date)
;; Keywords: 
;; URL: https://gnu.support/gnu-emacs/packages/termux-el.html

;; This file is not part of GNU Emacs.

;; This program 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.
;;
;; This program 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 program. If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; These are the Termux utilities for GNU Emacs Lisp
;;
;; RCD is acronym for Reach, Connect, Deliver, my personal
;; principle and formula for Wealth.

;;; Change Log:

;;; Code:

(require 'rcd-db-init)
(require 'rcd-db)
(require 'rcd-utilities)
(require 'subr-x)
(require 'time-date)

(defcustom termux-ip "192.168.43.1" "IP Address of Replicant system" :type 
'string :group 'Termux)
(setq termux-ip "192.168.42.129")
;;(setq termux-ip "192.168.43.1")
(defvar termux-ssh-command "ssh")
(defvar termux-ssh-port "8022")
(defvar termux-bin-directory "/data/data/com.termux/files/usr/bin/")

;;;; END OF SETUP

(defun termux/send-command (command)
  "Sends command to connected Replicant device and returns the output string"
  (let* ((command (concat termux-ssh-command " -p " termux-ssh-port " " 
termux-ip " \"" command "\""))
         (output (shell-command-to-string command))
         (output (string-trim output)))
    output))

(defun termux/error (error)
  "Messages error"
  (message error))

(defun termux/escape-double-quotes (s)
  "Escapes double quotes"
  (replace-regexp-in-string "\"" "\\\\\"" s))

(defun termux/escape-single-quotes (s)
  "Escapes single quotes"
  (replace-regexp-in-string "'" "\'" s))

(defun termux/escape-quotes (s)
  "Escapes double and single quotes"
  (let* ((s (termux/escape-double-quotes s)))
    ;;(s (termux/escape-single-quotes s)))
    s))

(defun termux/commands ()
  "Returns the list of all available termux commands and run the command if 
available."
  (interactive)
  (let* ((command (concat "cd " termux-bin-directory " && ls termux*"))
         (commands (termux/send-command command))
         (commands (split-string commands "\n"))
         (commands (append commands '("insert-location")))
         (command (completing-read "Termux commands: " commands))
         (command (intern (concat "termux/" command))))
    (if (functionp command)
        (funcall command)
      (message (concat "Command " (symbol-name command) " does not exist")))))

(defun termux/termux-location ()
  (let* ((command "termux-location")
         (result (termux/send-command command))
         (parsed (json-parse-string result)))
    (if (gethash "longitude" parsed)
        (let* ((latitude (number-to-string (gethash "latitude" parsed)))
               (longitude (number-to-string (gethash "longitude" parsed)))
               (location (concat latitude "," longitude)))
          location)
      (termux/error "Could not get location"))))

(defun termux/termux-location-insert ()
  "Inserts current location in the buffer"
  (interactive)
  (let ((location (termux/termux-location)))
    (insert location)))

(defun termux/termux-clipboard-get ()
  "Returns the Replicant clipboard value"
  (let* ((command "termux-clipboard-get")
         (result (termux/send-command command)))
    result))

(defun termux/termux-clipboard-get-insert ()
  "Inserts the Replicant clipboard value"
  (interactive)
  (let ((clipboard (termux/termux-clipboard-get)))
    (insert clipboard)))

(defun termux/termux-clipboard-set (value)
  "Sets the Replicant clipboard value"
  (interactive "MSet clipboard to: ")
  (let* ((value (termux/escape-double-quotes value))
         (command (concat "termux-clipboard-set \"" value "\"")))
    (termux/send-command command)))

(defun termux/termux-wifi-enable (boolean) ;; TODO
  "Enable or disable Wi-Fi on Replicant"
  (interactive "MEnable Wi-Fi (T for true, F or else for false): ")
  (let* ((boolean (if (string-match  "t" (downcase boolean)) "true" "false"))
         (command (concat "termux-wifi-enable \"" boolean "\"")))
    (termux/send-command command)))

(defun termux/termux-tts-speak (text)
  "Speaks by using termux"
  (interactive "MText to speak: ")
  (let* ((text (termux/escape-quotes text))
         (command (concat "termux-tts-speak \"" text "\"")))
    (termux/send-command command)))

(defun termux/termux-vibrate ()
  "Vibrate Replicant device"
  (interactive)
  (let* ((command "termux-vibrate"))
    (termux/send-command command)))

;; (defun termux-sms-send (number text)
;;   "Sends SMS message by using termux"
;;   (interactive "MNumber: \nMText: ")
;;   (let* ((text (termux/escape-double-quotes text))
;;       (command (concat "termux-sms-send -n \"" number "\" \"" text "\"")))
;;     (termux/send-command command)))

(defun termux-sms-send (number text)
  "Send SMS message by using ShellMS application"
  (let* ((text (termux/escape-quotes text))
         (command (format "am startservice --user 0 -n 
com.android.shellms/.sendSMS -e contact %s -e msg \\\"%s\\\"" number text)))
    (termux/send-command command)))

;; (termux-sms-send "+123" sms)
;; (termux-sms-send "+123" "Hello")
;; (termux-sms-send "+123" "Hello thank you, but I do not have this number 
registered, what is your name?")

(defun termux-import-sms-list ()
  (interactive)
  (let* ((command "termux-sms-list -t inbox -l 500")
         (list (termux/send-command command))
         (list (json-parse-string list))
         (remainder-file (concat (getenv "TMPDIR") "SMS-import-" 
(rcd-timestamp) ".el"))
         (_ (data-to-file list remainder-file))
         (how-many (length list)))
    (dotimes (n how-many)
      (let ((sms (elt list n)))
        (cf-sms-insert sms)))))

;; (termux-import-sms-list)

(defun cf-sms-handle (sms)
  "Returns list after reading the `sms' hash"
  (let* ((type (gethash "type" sms))
         (status (cf-sms-status type))
         (number (gethash "number" sms))
         (date-received (gethash "received" sms))
         (body (gethash "body" sms)))
    (list status number date-received body)))

(defun cf-sms-status (status)
  (cond ((string= status "inbox") 2)
        (t (error (concat "Cannot find SMS type: " status)))))

(defun cf-sms-insert (sms)
  "Inserts the SMS hash into the database"
  (let ((exists (cf-sms-imported-exists sms)))
    (if exists (message-any sms)
      (let* ((sms (cf-sms-handle sms))
             (status (elt sms 0))
             (number (elt sms 1))
             (date-received (elt sms 2))
             (date-received (cf-sms-repair-date date-received))
             (body (elt sms 3))
             (body (sql-escape-string body))
             (id (cf-contact-by-phone-1 number))
             (sql (format "INSERT INTO sms (sms_datecreated, sms_contacts, 
sms_smsstatus, sms_body, sms_phone) VALUES ('%s', %s, %s, %s, '%s') RETURNING 
sms_id" date-received id status body number)))
        (if id (rcd-sql-first sql *cf*)
          (message (format "SMS not entered: %s" (prin1-to-string sms))))))))

(defun cf-sms-imported-exists (sms)
  (let* ((sms (cf-sms-handle sms))
         (status (elt sms 0))
         (number (elt sms 1))
         (date-received (elt sms 2))
         (date-received (cf-sms-repair-date date-received))
         (body (elt sms 3))
         (body (sql-escape-string body))
         (id (cf-contact-by-phone-1 number))
         (sql (format "SELECT * FROM sms WHERE sms_datecreated = '%s' AND 
sms_contacts = %s AND sms_smsstatus = %s AND sms_body = %s AND sms_phone = 
'%s'" date-received id status body number)))
    (if id
        (let ((ids (rcd-sql-list sql *cf*)))
          (if ids ids nil))
    (message (format "SMS did not find ID: %s" (prin1-to-string sms))))))

(defun cf-sms-repair-date (date)
  "Repairs the date give by `termux-sms-list' function"
  (let* ((time (decoded-time-add (parse-time-string date) '(nil nil nil nil nil 
nil nil nil nil)))
         (year (decoded-time-year time))
         (month (decoded-time-month time))
         (day (decoded-time-day time))
         (hour (decoded-time-hour time))
         (minute (decoded-time-minute time))
         (second (decoded-time-second time))
         (date-time (format "%04.f-%02.f-%02.f %02.f:%02.f:%02.f" year month 
day hour minute second)))
    date-time))

;; (defun termux-sms-list ()
;;   (interactive)
;;   (let* ((command "termux-sms-list -t inbox -l 500")
;;       (list (termux/send-command command))
;;       (list (json-parse-string list))
;;       (how-many (length list)))
;;     (dotimes (n how-many)
;;       (let ((sms (elt list n)))
;;      (cf-sms-insert sms)))))

(defun rcd-kdeconnect-send-sms (number message)
  (let* ((status (shell-command
                  (mapconcat 'identity
                             (list "kdeconnect-cli" "-d"
                                   ;; "73fc592ea61a3a70" ;; Airtel
                                   "c47e5d958b455603" ;; MTN
                                   "--destination" number
                                   "--send-sms" (shell-quote-argument message))
                             " "))))
    status))

(defvar rcd-prefixes-uganda-airtel-mobile '(700 701 702 703 704 705 706
                                                710 711 712 713 714 715 716 717 
718 719
                                                750 751 752 753 754 755 756 757 
758 759))

(defvar rcd-prefixes-uganda-mtn-mobile '(770 771 772 773 774 775 776 777 778 779
                                             780 781 782 783 784 785 786 787 
788 789))

(defun rcd-sql-phone-national-prefix-or (country-prefix prefixes)
  (let* ((country-prefix (if (numberp country-prefix)
                             (number-to-string country-prefix)
                           country-prefix))
         (prefixes (mapcar (lambda (n) (single-quote (concat country-prefix 
(number-to-string n)))) prefixes))
         (prefixes (string-join prefixes ", ")))
    prefixes))

(defun rcd-sql-phone-recent-people-by-mobile-prefix (prefixes &optional 
how-many)
  (let* ((how-many (or how-many 200))
         (sql (format "SELECT people_id, get_full_contacts_name(people_id) FROM 
people WHERE (substring(people_officephone, 2, 6) IN (%s) OR 
substring(people_mobilephone, 2, 6) IN (%s) OR substring(people_homephone, 2, 
6) IN (%s) OR substring(people_otherphone, 2, 6) IN (%s) OR 
substring(people_fax, 2, 6) IN (%s)) ORDER BY people_id DESC LIMIT %s" prefixes 
prefixes prefixes prefixes prefixes how-many)))
    sql))

(defun cf-tabulated-people-recent-by-airtel-mobile ()
  (interactive)
  (let* ((prefixes (rcd-sql-phone-national-prefix-or 256 
rcd-prefixes-uganda-airtel-mobile))
         (sql (rcd-sql-phone-recent-people-by-mobile-prefix prefixes)))
    (rcd-db-sql-report-two "Recent people with UG Airtel numbers" sql [("ID" 8) 
("Person" 80)] "people" nil)))

(provide 'termux)
;;; termux.el ends here

Reply via email to