Sending email in emacs w/ request package and mailgun - email

I am trying to write an elisp function to send email using a mailgun account.
Here is what I have tried:
(require 'request)
(defun send-mail (api-key from-url to-name to-address subject message)
"Sends an email using Mailgun account"
(request
(concat "https://api.mailgun.net/v3/" from-url "/messages")
:type "POST"
:data '(("from" . (concat "Mailgun Sandbox <postmaster#" from-url ">"))
("to" . (concat to-name " <" to-address ">"))
("subject" . subject)
("text" . message))
:headers '(("api" . api-key))
:parser 'json-read
:success (function*
(lambda (&key data &allow-other-keys)
(message "I sent: %S" (assoc-default 'form data))))))
I am getting the error "Wrong type argument: integerp, concat" from the "from" section in :data. Not sure what I am doing wrong, or if this is even the correct approach to be taking.

In this section:
:data '(("from" . (concat "Mailgun Sandbox <postmaster#" from-url ">"))
("to" . (concat to-name " <" to-address ">"))
("subject" . subject)
("text" . message))
you are quoting the entire list literally with a single quote ('), but you actually want the concat function calls to be evaluated before that.
You can use a backquote instead, and put a comma before the parts that should be evaluated:
:data `(("from" . ,(concat "Mailgun Sandbox <postmaster#" from-url ">"))
("to" . ,(concat to-name " <" to-address ">"))
("subject" . subject)
("text" . message))
The question lawlist linked to in the comments explains this in more detail.

Related

How to store string from user input using a widget in emacs?

I have created a widget with an editable-field.
(widget-create 'editable-field
:size 13
:format "name: %v \n"
"Type in your name")
How can I now store only the user input?
I tried setq before widget-create
but it stores the entire widget definition.
I was able to store the user input using the notify field in the widget creation.
(widget-create 'editable-field
:size 13
:format "name: %v \n"
;;"Type in your name"
:notify (lambda (widget &rest ignore)
(setq *name* (widget-value widget)))
)

Passing a callback through cl-function

I'm trying to use the excellent request.el library request data from a REST API:
(request
"http://httpbin.org/get"
:params '(("key" . "value") ("key2" . "value2"))
:parser 'json-read
:success (function*
(lambda (&key data &allow-other-keys)
(message "I sent: %S" (assoc-default 'args data)))))
Which works nicely. Being a lisp-newbie I don't really know what the function* does here, I just got that from the request.el-examples.
I then tried to wrap this call in a function to reduce boilerplate like so:
(defun my/do-request (callback)
(request
"http://httpbin.org/get"
:params '(("key" . "value") ("key2" . "value2"))
:parser 'json-read
:success callback))
(my/do-request (lambda (data)
(message "got data: %s" data)))
But the callback is not being called? I also tried passing the callback like this:
(defun my/do-request (callback)
(request
"http://httpbin.org/get"
:params '(("key" . "value") ("key2" . "value2"))
:parser 'json-read
:success (function*
(lambda (&key data &allow-other-keys)
(callback data)))))
with the same result. I thought I might need lexical binding here, but that also didn't help.
How can I reduce the boilerplate code here?
When calling the new function, it is generally a good practice to try it with the exact same value you originally had:
(my/do-request
(function*
(lambda (&key data &allow-other-keys)
(message "I sent: %S" (assoc-default 'args data)))))
The above prints the desired message.
First approach
You called the code as follows, and nothing was printed:
(my/do-request (lambda (data)
(message "got data: %s" data)))
It turns out there is an error, but unfortunately it does not reach the user. In case of doubt, you should enable the debugger:
(setf debug-on-error t)
You can eval the above in the *scratch* buffer or in the minibuffer after doing M-: (eval-expression).
Then, when you reevaluate the call, the following should be displayed:
Debugger entered--Lisp error: (wrong-number-of-arguments (lambda (data) (message "got data: %s" data)) 8)
(lambda (data) (message "got data: %s" data))(:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . .....) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . .....) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #0) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil])
apply((lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
apply(apply (lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
request--safe-apply((lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
request--callback(#<killed buffer> :params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #0) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil])
apply(request--callback #<killed buffer> (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil #0 #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
request--curl-callback(#<process request curl> "finished\n")
Based on the documentation, the callback function should be variadic, which means the following works:
(lambda (&rest args) (message "got data: %s" args))
But then, you will see too much data.
Arguments are passed as key/values pairs. In order to get the data associated with the :data symbol, you would have to do:
(lambda (&rest args)
(message "got data: %s" (getf args :data)))
The resulting value is an association list, from which you can access the 'args entry like done above (i.e. (assoc-default 'args data)).
But, instead of doing (getf args :data), you can also write:
(lambda (&key data) ...)
The special &key symbol is used to automatically access the value associated with :data in the implicit list of arguments. But, keyword parameters are from Common Lisp, Emacs Lisp does not know how to handle &key out of the box. That's why there is a (function* ...) macro that wraps around the lambda. You then have the choice of using the function* macro or deal with the argument list by yourself, as shown above. It depends on what you need. As suggested by the documentation, if you use &key, you should also use &allow-other-keys.
Second approach
In your second approach, having debug-on-entry set to t indicates that callback is not a known function:
Debugger entered--Lisp error: (void-function callback)
This is due to Emacs Lisp being a Lisp-2, i.e. you cannot call a function given in argument simply by putting it as the first element of a function call. Emacs Lisp understands the syntax as "call the function named callback", not "call the function object bound to variable callback". You'd need to use funcall:
(function*
(lambda (&key data &allow-other-keys)
(funcall callback data)))
But with the above, now the error is that callback is an undefined variable. And now, this is due to dynamic scoping. If you evalute the following line and reevaluate the defun, the code works as expected:
(setf lexical-binding t)
Alternatively, you can put the following as the first line of you file:
;; -*- lexical-binding: t -*-

Generating inline javascript with cl-who, parenscript and hunchentoot

I'm trying to generate inline javascript, but I have to put the parenscript code inside (:script) and (str) tags using cl-who. ps, ps*, ps-inline and ps-inline* don't seem to make much difference to the generated js.
Is the usual way to write a macro to avoid code duplication, or is there a better way?
Here's my program:
(in-package #:ps-test)
(defmacro standard-page ((&key title) &body body)
`(with-html-output-to-string (*standard-output* nil :prologue t :indent t)
(:html
:lang "en"
(:head
(:meta :http-equiv "Content-Type"
:content "text/html;charset=utf-8")
(:title ,title)
(:link :type "text/css"
:rel "stylesheet"
:href "/style.css"))
(:body
,#body))))
(defun main ()
(with-html-output (*standard-output* nil :indent t :prologue nil)
(standard-page (:title "Parenscript test")
(:div (str "Hello worldzors"))
(:script :type "text/javascript"
(str (ps (alert "Hello world as well")))))))
(define-easy-handler (docroot :uri "/") ()
(main))
(defun start-ps-test ()
(setf (html-mode) :html5)
(setf *js-string-delimiter* #\")
(start (make-instance 'hunchentoot:easy-acceptor :port 8080)))
(defun stop-ps-test ()
(stop *server*))
(defvar *server* (start-ps-test))
Macros are fine in this use case.
The trick is that macros are expanded in a specific order. Say
you define a js macro: when macroexpansion encounters
with-html-output, the inner call to your macros (js (alert "Ho Ho Ho")) looks like a function call, and is left as-is in the generated
code. If your js macro then expands into (:script ...), then the system will complain that :script is an unknown function (assuming you
didn't actually name a function like that). You should emit an
enclosing (who:htm ...) expression to interpret the code using
CL-WHO's code walker.
(defmacro js (code)
`(who:htm
(:script :type "text/javascript" (who:str (ps:ps ,code)))))
This only works in the context of an enclosing with-html-output.
For inline Javascript, you don't want to have a <script> tag around it,
and you can generally simply use ps-inline:
(who:with-html-output (*standard-output*)
(:a :href (ps:ps-inline (void 0))
"A link where the usual HREF behavior is canceled."))
;; prints:
;;
;; <a href='javascript:void(0)'>A link where the usual HREF behavior is canceled.</a>
But feel free to use a macro if you often do the same thing:
(defmacro link (&body body)
`(who:htm (:a :href #.(ps:ps-inline (void 0)) ,#body)))
(who:with-html-output (*standard-output*) (link "Link"))
;; prints:
;;
;; <a href='javascript:void(0)'>Link</a>

Org Babel Source Block Native Fontification

I'm trying to get org-babel source blocks to do native syntax highlighting. I've set org-src-fontify-natively to true, but am still not seeing anything.
Here's my related config:
(setq org-babel-default-header-args
'((:session . "none")
(:results . "replace")
(:exports . "code")
(:cache . "no")
(:noweb . "yes")
(:hlines . "yes")
(:tangle . "yes")
(:padnewline . "yes")))
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(R . t)
(js . t)
(clojure . t)
(org . t)
(sh . t)
(python . t)
(dot . t)
(prolog . t)
(lisp . t)
))
(setq org-edit-src-content-indentation 0
org-src-tab-acts-natively t
org-src-fontify-natively t
org-confirm-babel-evaluate nil
org-src-window-setup 'current-window
org-src-preserve-indentation t
org-src-strip-leading-and-trailing-blank-lines t
)
And here's the org-babel-view-src-block-info (C-c C-v I):
Lang: emacs-lisp
Properties:
:header-args nil
:header-args:emacs-lisp nil
Header Arguments:
:cache no
:exports code
:hlines yes
:lexical no
:noweb yes
:padnewline yes
:results none
:session none
:tangle yes
And my (emacs-version):
GNU Emacs 25.1.50.1 (x86_64-apple-darwin13.4.0, NS appkit-1265.21 Version 10.9.5 (Build 13F1603))
of 2016-04-03

Elisp help. Why does the following snippet fail?

I'm trying to write a little script that notifies me when someone talks to me in erc. So I need to write a funcion that recieves what I presume are two strings, nick and msg so I can hook it. But my test function fails. I have tested growl-notify and the format s-expresions and they work fine but I cant get test to work. I have no idea why is it failing. Any pointers?
(defun growl-notify (&key message)
"Use growl for notifications"
(start-process "growlnotify" "growlnotifications" "growlnotify" "-m " message))
(defun test (nick msg)
(growlnotify :message (format "%s: %s" nick msg)))
(test "Ppop" "something")
It gives the following backtrack, hope it helps.
Debugger entered--Lisp error: (void-function growlnotify)
(growlnotify :message (format "%s: %s" nick msg))
test("oi" "poi")
eval((test "oi" "poi") nil)
eval-last-sexp-1(t)
eval-last-sexp(t)
eval-print-last-sexp()
call-interactively(eval-print-last-sexp nil nil)
recursive-edit()
debug(error (wrong-number-of-arguments (lambda (&key message) "Use growl for notifications" (start-process "growlnotify" "growlnotifications" "growlnotify" "-m " message)) 3))
The error message is Debugger entered--Lisp error: (void-function growlnotify), which tells you that growlnotify isn't defined as a function.
If you look at your code, you'll see that you defined it like this: (defun growl-notify (&key message), which defines a function called growl-notify. A simple typo.