1 ;;; gnus-edit.el --- Gnus SCORE file editing.
2 ;; Copyright (C) 1995 Free Software Foundation, Inc.
4 ;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
5 ;; Keywords: news, help
10 ;; Type `M-x gnus-score-customize RET' to invoke.
16 (autoload 'gnus-score-load "gnus-score")
18 (defconst gnus-score-custom-data
20 (doc . "Customization of Gnus SCORE files.
22 SCORE files allow you to assign a score to each article when you enter
23 a group, and automatically mark the articles as read or delete them
24 based on the score. In the summary buffer you can use the score to
25 sort the articles by score (`C-c C-s C-s') or to jump to the unread
26 article with the highest score (`,').")
30 (doc . "Name of SCORE file to customize.
32 Enter the name in the `File' field, then push the [Load] button to
33 load it. When done editing, push the [Save] button to save the file.
35 Several score files may apply to each group, and several groups may
36 use the same score file. This is controlled implicitly by the name of
37 the score file and the value of the global variable
38 `gnus-score-find-score-files-function', and explicitly by the the
39 `Files' and `Exclude Files' entries.")
44 (query . gnus-score-custom-load))
47 (query . gnus-score-custom-save))
50 (directory . "~/News/")
51 (default-file . "SCORE")
56 List of score files to load when the the current score file is loaded.
57 You can use this to share score entries between multiple score files.
59 Push the `[INS]' button add a score file to the list, or `[DEL]' to
60 delete a score file from the list.")
62 (data ((type . repeat)
65 (directory . "~/News/")))))
66 ((name . exclude-files)
67 (tag . "Exclude Files")
69 List of score files to exclude when the the current score file is loaded.
70 You can use this if you have a score file you want to share between a
71 number of newsgroups, except for the newsgroup this score file
72 matches. [ Did anyone get that? ]
74 Push the `[INS]' button add a score file to the list, or `[DEL]' to
75 delete a score file from the list.")
77 (data ((type . repeat)
80 (directory . "~/News/")))))
84 Articles below this score will be automatically marked as read.
86 This means that when you enter the summary buffer, the articles will
87 be shown but will already be marked as read. You can then press `x'
88 to get rid of them entirely.
90 By default articles with a negative score will be marked as read. To
91 change this, push the `Mark' button, and choose `Integer'. You can
92 then enter a value in the `Mark' field.")
93 (type . gnus-score-custom-maybe-type))
97 Articles below this score will not be shown in the summary buffer.")
98 (type . gnus-score-custom-maybe-type))
99 ((name . mark-and-expunge)
100 (tag . "Mark and Expunge")
102 Articles below this score will be marked as read, but not shown.
104 Someone should explain me the difference between this and `expunge'
105 alone or combined with `mark'.")
106 (type . gnus-score-custom-maybe-type))
107 ; ;; Sexp type isn't implemented yet.
110 ; (doc . "Evaluate this expression when the entering sumamry buffer.")
112 ;; Toggle type isn't implemented yet.
115 (doc . "Read-only score files will not be updated or saved.
116 Except from this buffer, of course!")
121 Each news header has an associated list of score entries.
122 You can use the [INS] buttons to add new score entries anywhere in the
123 list, or the [DEL] buttons to delete specific score entries.
125 Each score entry should specify a string that should be matched with
126 the content actual header in order to determine whether the entry
127 applies to that header. Enter that string in the `Match' field.
129 If the score entry matches, the articles score will be adjusted with
130 some amount. Enter that amount in the in the `Score' field. You
131 should specify a positive amount for score entries that matches
132 articles you find interesting, and a negative amount for score entries
133 matching articles you would rather avoid. The final score for the
134 article will be the sum of the score of all score entries that match
137 The score entry can be either permanent or expirable. To make the
138 entry permanent, push the `Date' button and choose the `Permanent'
139 entry. To make the entry expirable, choose instead the `Integer'
140 entry. After choosing the you can enter the date the score entry was
141 last matched in the `Date' field. The date will be automatically
142 updated each time the score entry matches an article. When the date
143 become too old, the the score entry will be removed.
145 For your convenience, the date is specified as the number of days
146 elapsed since the (imaginary) Gregorian date Sunday, December 31, 1
149 Finally, you can choose what kind of match you want to perform by
150 pushing the `Type' button. For most entries you can choose between
151 `Exact' which mean the header content must be exactly identical to the
152 match string, or `Substring' meaning the match string should be
153 somewhere in the header content, or even `Regexp' to use Emacs regular
154 expression matching. The last choice is `Fuzzy' which is like `Exact'
155 except that whitespace derivations, a beginning `Re:' or a terminating
156 parenthetical remark are all ignored. Each of the four types have a
157 variant which will ignore case in the comparison. That variant is
158 indicated with a `(fold)' after its name."))
161 (doc . "Scoring based on the authors email address.")
162 (type . gnus-score-custom-string-type))
165 (doc . "Scoring based on the articles subject.")
166 (type . gnus-score-custom-string-type))
169 (doc . "Scoring based on who the article is a followup to.
171 If you want to see all followups to your own articles, add an entry
172 with a positive score matching your email address here. You can also
173 put an entry with a negative score matching someone who is so annoying
174 that you don't even want to see him quoted in followups.")
175 (type . gnus-score-custom-string-type))
178 (doc . "Scoring based on article crossposting.
180 If you want to score based on which newsgroups an article is posted
181 to, this is the header to use. The syntax is a little different from
182 the `Newsgroups' header, but scoring in `Xref' is much faster. As an
183 example, to match all crossposted articles match on `:.*:' using the
185 (type . gnus-score-custom-string-type))
188 (doc . "Scoring based on article references.
190 The `References' header gives you an alternative way to score on
191 followups. If you for example want to see follow all discussions
192 where people from `iesd.auc.dk' school participate, you can add a
193 substring match on `iesd.auc.dk>' on this header.")
194 (type . gnus-score-custom-string-type))
197 (doc . "Scoring based on the articles message-id.
199 This isn't very useful, but Lars like completeness. You can use it to
200 match all messaged generated by recent Gnus version with a `Substring'
202 (type . gnus-score-custom-string-type))
206 WARNING: Scoring on the following three pseudo headers is very slow!
207 Scoring on any of the real headers use a technique that avoids
208 scanning the entire article, only the actual headers you score on are
209 scanned, and this scanning has been heavily optimized. Using just a
210 single entry for one the three pseudo-headers `Head', `Body', and
211 `All' will require GNUS to retrieve and scan the entire article, which
212 can be very slow on large groups. However, if you add one entry for
213 any of these headers, you can just as well add several. Each
214 subsequent entry cost relatively little extra time."))
217 (doc . "Scoring based on the article header.
219 Instead of matching the content of a single header, the entire header
220 section of the article is matched. You can use this to match on
221 arbitrary headers, foe example to single out TIN lusers, use a substring
222 match on `Newsreader: TIN'. That should get 'em!")
223 (type . gnus-score-custom-string-type))
226 (doc . "Scoring based on the article body.
228 If you think any article that mentions `Kibo' is inherently
229 interesting, do a substring match on His name. You Are Allowed.")
230 (type . gnus-score-custom-string-type))
233 (doc . "Scoring based on the whole article.")
234 (type . gnus-score-custom-string-type))
237 (doc . "Scoring based on article date.
239 You can change the score of articles that have been posted before,
240 after, or at a specific date. You should add the date in the `Match'
241 field, and then select `before', `after', or `at' by pushing the
242 `Type' button. Imagine you want to lower the score of very old
243 articles, or want to raise the score of articles from the future (such
244 things happen!). Then you can't use date scoring for that. In fact,
245 I can't imagine anything you would want to use this for.
247 For your convenience, the date is specified in Usenet date format.")
248 (type . gnus-score-custom-date-type))
252 The Lines and Chars headers use integer based scoring.
254 This means that you should write an integer in the `Match' field, and
255 the push the `Type' field to if the `Chars' or `Lines' header should
256 be larger, equal, or smaller than the number you wrote in the match
260 (doc . "Scoring based on the number of characters in the article.")
261 (type . gnus-score-custom-integer-type))
264 (doc . "Scoring based on the number of lines in the article.")
265 (type . gnus-score-custom-integer-type))
268 (doc . "Score to add to articles with no parents.")
269 (type . gnus-score-custom-maybe-type)))))
270 ;; This is to complex for me to figure out right now.
272 ; This entry controls the adaptive scoring. If it is `t', the
273 ; default adaptive scoring rules will be used. If it is `ignore', no
274 ; adaptive scoring will be performed on this group. If it is a
275 ; list, this list will be used as the adaptive scoring rules. If it
276 ; isn't present, or is something other than `t' or `ignore', the
277 ; default adaptive scoring rules will be used. If you want to use
278 ; adaptive scoring on most groups, you'd set
279 ; `gnus-use-adaptive-scoring' to `t', and insert an `(adapt ignore)'
280 ; in the groups where you do not want adaptive scoring. If you only
281 ; want adaptive scoring in a few groups, you'd set
282 ; `gnus-use-adaptive-scoring' to `nil', and insert `(adapt t)' in
283 ; the score files of the groups where you want it.
284 ;; This isn't implemented in the old version of (ding) I use.
286 ; List of local variables to bind in the summary buffer.
288 (defconst gnus-score-custom-type-properties
289 '((gnus-score-custom-maybe-type
291 (data ((type . integer)
296 (gnus-score-custom-string-type
298 (data ((type . repeat)
300 (data . ((type . list)
302 (data ((tag . "Match")
310 (data ((type . integer)
318 (data ((tag . "Exact")
330 ((tag . "Exact (fold)")
333 ((tag . "Substring (fold)")
336 ((tag . "Regexp (fold)")
339 ((tag . "Fuzzy (fold)")
341 (type . const))))))))))
342 (gnus-score-custom-integer-type
344 (data ((type . repeat)
346 (data . ((type . list)
348 (data ((tag . "Match")
354 (data ((type . integer)
376 (type . const))))))))))
377 (gnus-score-custom-date-type
379 (data ((type . repeat)
381 (data . ((type . list)
383 (data ((tag . "Match")
391 (data ((type . integer)
399 (data ((tag . "Before")
407 (type . const))))))))))))
409 (defvar gnus-score-custom-file nil
410 "Name of SCORE file being customized.")
412 (defun gnus-score-customize ()
413 "Create a buffer for editing gnus SCORE files."
415 (let (gnus-score-alist)
416 (custom-buffer-create "*Score Edit*" gnus-score-custom-data
417 gnus-score-custom-type-properties
418 'gnus-score-custom-set
419 'gnus-score-custom-get))
420 (make-local-variable 'gnus-score-custom-file)
421 (setq gnus-score-custom-file "SCORE")
422 (make-local-variable 'gnus-score-alist)
423 (setq gnus-score-alist nil)
426 (defun gnus-score-custom-get (name)
428 gnus-score-custom-file
429 (let ((entry (assoc (symbol-name name) gnus-score-alist)))
431 (mapcar 'gnus-score-custom-sanify (cdr entry))
432 (setq entry (assoc name gnus-score-alist))
433 (if (memq name '(files))
435 (car (cdr entry)))))))
437 (defun gnus-score-custom-set (name value)
438 (cond ((eq name 'file)
439 (setq gnus-score-custom-file value))
440 ((assoc (symbol-name name) gnus-score-alist)
442 (setcdr (assoc (symbol-name name) gnus-score-alist) value)
443 (setq gnus-score-alist (delq (assoc (symbol-name name)
446 ((assoc (symbol-name name) gnus-header-index)
448 (setq gnus-score-alist
449 (cons (cons (symbol-name name) value) gnus-score-alist))))
450 ((assoc name gnus-score-alist)
452 (setq gnus-score-alist (delq (assoc name gnus-score-alist)
455 (setcdr (assoc name gnus-score-alist) value))
457 (setcdr (assoc name gnus-score-alist) (list value)))))
460 (setq gnus-score-alist (cons (cons name value) gnus-score-alist)))
462 (setq gnus-score-alist
463 (cons (cons name (list value)) gnus-score-alist)))))
465 (defun gnus-score-custom-sanify (entry)
467 (or (nth 1 entry) gnus-score-interactive-default-score)
469 (if (null (nth 3 entry))
471 (intern (substring (symbol-name (nth 3 entry)) 0 1)))))
473 (defvar gnus-score-cache nil)
475 (defun gnus-score-custom-load ()
477 (let ((file (custom-name-value 'file)))
478 (if (eq file custom-nil)
479 (error "You must specify a file name"))
480 (setq file (expand-file-name file "~/News"))
481 (gnus-score-load file)
482 (setq gnus-score-custom-file file)
486 (defun gnus-score-custom-save ()
489 (gnus-score-remove-from-cache gnus-score-custom-file)
490 (let ((file gnus-score-custom-file)
491 (score gnus-score-alist)
492 emacs-lisp-mode-hook)
494 (set-buffer (get-buffer-create "*Score*"))
495 (buffer-disable-undo (current-buffer))
497 (pp score (current-buffer))
498 (gnus-make-directory (file-name-directory file))
499 (write-region (point-min) (point-max) file nil 'silent)
500 (kill-buffer (current-buffer))))
505 ;;; gnus-edit.el end here