aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bot/sx-bot.el54
-rwxr-xr-xbot/sx-bot.sh8
-rw-r--r--sx-compose.el29
-rw-r--r--sx-filter.el35
-rw-r--r--sx-tag.el78
5 files changed, 203 insertions, 1 deletions
diff --git a/bot/sx-bot.el b/bot/sx-bot.el
new file mode 100644
index 0000000..f7e0557
--- /dev/null
+++ b/bot/sx-bot.el
@@ -0,0 +1,54 @@
+;;; sx-bot.el --- Functions for viewing different tabs. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014 Artur Malabarba
+
+;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; 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:
+
+;;
+
+
+;;; Code:
+
+(require 'sx-site)
+(require 'sx-tag)
+
+(defcustom sx-bot-out-dir "./data/tags/"
+ "Directory where output tag files are saved."
+ :type 'directory
+ :group 'sx)
+
+
+;;; Printing
+(defun sx-bot-write-to-file (data)
+ "Write (cdr DATA) to file named (car DATA).
+File is savedd in `sx-bot-out-dir'."
+ (with-temp-file (expand-file-name (car data) sx-bot-out-dir)
+ (let (print-length)
+ (prin1 (cdr data) (current-buffer)))))
+
+
+(defun sx-bot-fetch-and-write-tags ()
+ "Get a list of all tags of all sites and save to disk."
+ (make-directory sx-bot-out-dir t)
+ (mapc #'sx-bot-write-to-file
+ ;; @TODO: Not yet implemented!
+ (mapcar #'sx-tag--get-all (sx-site-get-api-tokens))))
+
+;;; Newest
+(provide 'sx-bot)
+;;; sx-bot.el ends here
diff --git a/bot/sx-bot.sh b/bot/sx-bot.sh
new file mode 100755
index 0000000..176e454
--- /dev/null
+++ b/bot/sx-bot.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/bash
+
+git branch gh-pages &&
+ git pull &&
+ emacs -Q --batch -L "./" -l sx-bot -f sx-bot-fetch-and-write-tags &&
+ git commit . &&
+ git push &&
+ echo SUCCESS
diff --git a/sx-compose.el b/sx-compose.el
index ab4a58d..d27d2f3 100644
--- a/sx-compose.el
+++ b/sx-compose.el
@@ -82,6 +82,10 @@ Is invoked between `sx-compose-before-send-hook' and
"Headers inserted when composing a new question.
Used by `sx-compose-create'.")
+(defvar sx-compose--site nil
+ "Site which the curent compose buffer belongs to.")
+(make-variable-buffer-local 'sx-compose--site)
+
;;; Major-mode
(define-derived-mode sx-compose-mode markdown-mode "Compose"
@@ -116,6 +120,8 @@ contents to the API, then calls `sx-compose-after-send-functions'."
(run-hook-with-args 'sx-compose-after-send-functions
(current-buffer) result)))))
+
+;;; Functions for use in hooks
(defun sx-compose-quit (buffer _)
"Close BUFFER's window and kill it."
(interactive (list (current-buffer) nil))
@@ -131,6 +137,26 @@ contents to the API, then calls `sx-compose-after-send-functions'."
(with-current-buffer buffer
(kill-new (buffer-string)))))
+(defun sx-compose--check-tags ()
+ "Check if tags in current compose buffer are valid."
+ (save-excursion
+ (goto-char (point-min))
+ (unless (search-forward-regexp
+ "^Tags : *\\([^[:space:]].*\\) *$"
+ (next-single-property-change (point-min) 'sx-compose-separator)
+ 'noerror)
+ (error "No Tags header found"))
+ (let ((invalid-tags
+ (sx-tag--invalid-name-p
+ (split-string (match-string 1) "[[:space:],;]"
+ 'omit-nulls "[[:space:]]")
+ sx-compose--site)))
+ (if invalid-tags
+ ;; If the user doesn't want to create the tags, we return
+ ;; nil and sending is aborted.
+ (y-or-n-p "Following tags don't exist. Create them? %s " invalid-tags)
+ t))))
+
;;; Functions to help preparing buffers
(defun sx-compose-create (site parent &optional before-functions after-functions)
@@ -153,6 +179,7 @@ respectively added locally to `sx-compose-before-send-hook' and
(cdr (assoc 'title parent))))))
(with-current-buffer (sx-compose--get-buffer-create site parent)
(sx-compose-mode)
+ (setq sx-compose--site site)
(setq sx-compose--send-function
(if (consp parent)
(sx-assoc-let parent
@@ -180,6 +207,8 @@ respectively added locally to `sx-compose-before-send-hook' and
(add-hook 'sx-compose-before-send-hook it nil t))
(dolist (it (reverse after-functions))
(add-hook 'sx-compose-after-send-functions it nil t))
+ (when is-question
+ (add-hook 'sx-compose-before-send-hook #'sx-compose--check-tags nil t))
;; If the buffer is empty, the draft didn't exist. So prepare the
;; question.
(when (or (string= (buffer-string) "")
diff --git a/sx-filter.el b/sx-filter.el
index 8c00c12..ad37e67 100644
--- a/sx-filter.el
+++ b/sx-filter.el
@@ -41,7 +41,40 @@ Structure:
...)")
-;;; Compilation
+;;; Creation
+
+(defmacro sx-filter-from-nil (included)
+ "Creates a filter data structure with INCLUDED fields.
+All wrapper fields are included by default."
+ ;; @OTODO: it would be neat to have syntax like
+ ;;
+ ;; (field-a
+ ;; field-b
+ ;; (object-a subfield)
+ ;; field-c
+ ;; (object-b subfield-a subfield-b))
+ ;;
+ ;; expand into
+ ;;
+ ;; (field-a
+ ;; field-b
+ ;; object-a.subfield
+ ;; field-c
+ ;; object-b.subfield-a object-b.subfield-b)
+ `(quote ((,@included
+ .backoff
+ .error_id
+ .error_message
+ .error_name
+ .has_more
+ .items
+ .page
+ .page_size
+ .quota_max
+ .quota_remaining
+ .total
+ .type)
+ nil none)))
;;; @TODO allow BASE to be a precompiled filter name
(defun sx-filter-compile (&optional include exclude base)
diff --git a/sx-tag.el b/sx-tag.el
new file mode 100644
index 0000000..0aec814
--- /dev/null
+++ b/sx-tag.el
@@ -0,0 +1,78 @@
+;;; sx-tag.el --- Retrieving list of tags and handling tags. -*- lexical-binding: t; -*-
+
+;; 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:
+
+
+
+;;; Code:
+(eval-when-compile
+ '(require 'cl-lib))
+
+(require 'sx)
+(require 'sx-method)
+
+
+;;; Getting the list from a site
+(defvar sx-tag-filter
+ (sx-filter-from-nil
+ (tag.name))
+ "Filter used when querying tags.")
+
+(defun sx-tag--get-all (site)
+ "Retrieve all tags for SITE."
+ (sx-method-call 'tags
+ :get-all t
+ :filter sx-tag-filter
+ :site "emacs"))
+
+(defun sx-tag--get-some-tags-containing (site string)
+ "Return at most 100 tags for SITE containing STRING.
+Returns an array."
+ (sx-method-call 'tags
+ :auth nil
+ :filter sx-tag-filter
+ :site site
+ :keywords `((page . 1) (pagesize . 100) (inname . ,string))))
+
+(defun sx-tag--get-some-tag-names-containing (site string)
+ "Return at most 100 tag names for SITE containing STRING.
+Returns a list."
+ (mapcar (lambda (x) (cdr (assoc 'name x)))
+ (sx-tag--get-some-tags-containing site string)))
+
+
+;;; Check tag validity
+(defun sx-tag--invalid-name-p (site tags)
+ "Nil if TAGS exist in SITE.
+TAGS can be a string (the tag name) or a list of strings.
+Fails if TAGS is a list with more than 100 items.
+Return the list of invalid tags in TAGS."
+ (and (listp tags) (> (length tags) 100)
+ (error "Invalid argument. TAG has more than 100 items"))
+ (let ((result
+ (mapcar
+ (lambda (x) (cdr (assoc 'name x)))
+ (sx-method-call 'tags
+ :id (sx--thing-as-string tags)
+ :submethod 'info
+ :auth nil
+ :filter sx-tag-filter
+ :site site
+ :keywords '((page . 1) (pagesize . 100))))))
+ (cl-remove-if (lambda (x) (member x result)) tags)))
+
+(provide 'sx-tag)
+;;; sx-tag.el ends here