<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>sacha chua :: enterprise 2.0 consultant, storyteller, geek &#187; bbdb</title>
	<atom:link href="http://sachachua.com/wp/category/bbdb/feed/" rel="self" type="application/rss+xml" />
	<link>http://sachachua.com/wp</link>
	<description>I help people connect through blogs, wikis, other Web 2.0 tools. I'm also writing a book about Emacs.</description>
	<pubDate>Fri, 21 Nov 2008 21:28:16 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<item>
		<title>Emacs: Someone who&#039;s even geekier about BBDB!</title>
		<link>http://sachachua.com/wp/2008/07/25/emacs-someone-whos-even-geekier-about-bbdb/</link>
		<comments>http://sachachua.com/wp/2008/07/25/emacs-someone-whos-even-geekier-about-bbdb/#comments</comments>
		<pubDate>Sat, 26 Jul 2008 01:40:24 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/07/25/emacs-someone-whos-even-geekier-about-bbdb/</guid>
		<description><![CDATA[Remember all those tweaks I posted for working with contacts using Emacs&#039; Big Brother Database? John Sturdy has even cooler stuff. Check out his BBDB extensions. His bbdb-export-lists.el can divide people into different phone and address lists, and his bbdb-human-names.el will even try to address people properly. Clearly, I don&#039;t talk to enough Reverend Doctors [...]]]></description>
			<content:encoded><![CDATA[<p>Remember all those tweaks I posted for <a href="http://sachachua.com/wp/category/bbdb">working with contacts using Emacs&#039; Big Brother Database</a>? John Sturdy has even cooler stuff. Check out his <a href="http://www.cb1.com/~john/computing/emacs/lisp/my-extensions-to-packages/bbdb/index.html">BBDB extensions</a>. His <a href="http://www.cb1.com/~john/computing/emacs/lisp/my-extensions-to-packages/bbdb/bbdb-export-lists.el">bbdb-export-lists.el</a> can divide people into different phone and address lists, and his <a href="http://www.cb1.com/~john/computing/emacs/lisp/my-extensions-to-packages/bbdb/bbdb-human-names.el">bbdb-human-names.el</a> will even try to address people properly. Clearly, I don&#039;t talk to enough Reverend Doctors to require code like this. (And shouldn&#039;t there be &#034;Right Honorables&#034; and &#034;His Grace&#034;s in there, too? <img src='http://sachachua.com/wp/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> )</p>

<!-- start wp-tags-to-technorati 0.95 -->

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/07/25/emacs-someone-whos-even-geekier-about-bbdb/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs: BBDB: Modifying the record creation process</title>
		<link>http://sachachua.com/wp/2008/07/20/emacs-bbdb-modifying-the-record-creation-process/</link>
		<comments>http://sachachua.com/wp/2008/07/20/emacs-bbdb-modifying-the-record-creation-process/#comments</comments>
		<pubDate>Sun, 20 Jul 2008 15:26:53 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/07/20/emacs-bbdb-modifying-the-record-creation-process/</guid>
		<description><![CDATA[You might want to add fields to the record creation process in M-x bbdb-create. In addition to asking for name, company, e-mail address, addresses, phone numbers, and notes, you may want to make sure you remember to put in birthdates or other information.  If you want to prompt for custom fields after the regular [...]]]></description>
			<content:encoded><![CDATA[<p>You might want to add fields to the record creation process in M-x bbdb-create. In addition to asking for name, company, e-mail address, addresses, phone numbers, and notes, you may want to make sure you remember to put in birthdates or other information.  If you want to prompt for custom fields after the regular BBDB creation form, modify this simple example to suit your needs:</p>
<pre>
(defadvice bbdb-read-new-record (after wicked activate)
  "Prompt for the birthdate as well."
  (bbdb-record-putprop ad-return-value 'birthdate
		       (bbdb-read-string "Birthdate (YYYY.MM.DD): ")))
</pre>
<p>Thanks to Kaleb Yilma for the question!</p>

<!-- start wp-tags-to-technorati 0.95 -->

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/07/20/emacs-bbdb-modifying-the-record-creation-process/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Wicked Cool Emacs: BBDB: Import CSV and vCard Files</title>
		<link>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-import-csv-and-vcard-files/</link>
		<comments>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-import-csv-and-vcard-files/#comments</comments>
		<pubDate>Sat, 12 Apr 2008 22:19:11 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-import-csv-and-vcard-files/</guid>
		<description><![CDATA[If you have many contacts in another address book program, you can import them into BBDB. Two popular formats are comma-separated value files (CSV) and vCard files (VCF).
Project XXX: Import a CSV File into BBDB
To import a CSV file into BBDB, you will need csv.el from http://ulf.epplejasper.de/downloads/csv.el and lookout.el from http://ulf.epplejasper.de/downloads/lookout.el . Save both files [...]]]></description>
			<content:encoded><![CDATA[<p>If you have many contacts in another address book program, you can import them into BBDB. Two popular formats are comma-separated value files (CSV) and vCard files (VCF).</p>
<h2>Project XXX: Import a CSV File into BBDB</h2>
<p>To import a CSV file into BBDB, you will need csv.el from http://ulf.epplejasper.de/downloads/csv.el and lookout.el from http://ulf.epplejasper.de/downloads/lookout.el . Save both files to your ~/elisp directory. Make sure that your ~/elisp directory is in your load-path by adding the following line to your ~/.emacs:</p>
<pre>
(add-to-list 'load-path "~/elisp")
</pre>
<p>Export your contacts as an Outlook-style CSV file, then open the file in Emacs. After loading the following code, call M-x wicked/bbdb-import-csv-buffer to merge the CSV data into your address book. Emacs will try to update existing records based on the e-mail address or name provided, creating new records if necessary. After Emacs updates the records, the relevant records are displayed in the *BBDB* buffer. Here is the code to make that work:</p>
<p>ch6-bbdb-import-csv-buffer.el:</p>
<pre>
(require 'lookout)
(defconst wicked/lookout-bbdb-mapping-table-outlook
  '(("name" "Name")
    ("net" "E-mail Address")
    ("notes" "Notes")
    ("phones" "Mobile Phone"
     "Home Phone"
     "Home Phone 2"
     "Home Fax"
     "Business Phone"
     "Business Phone 2"
     "Business Fax"
     "Other Phone"
     "Other Fax")
    ("addr1" "Home Address")
    ("addr2" "Business Address")
    ("addr3" "Other Address")
    ("lastname" "Last Name")
    ("firstname" "First Name")
    ("job" "Job Title")
    ("company" "Company")
    ("otherfields" ""))
  "Field mappings for Outlook-type CSVs exported from Outlook, Gmail, LinkedIn, etc.")

(defun wicked/bbdb-import-csv-line (line)
  "Import LINE as a CSV, trying to merge it with existing records."
  (let* (record
	 (name  (lookout-bbdb-get-value "name" line))
	 (lastname (lookout-bbdb-get-value "lastname" line))
	 (firstname (lookout-bbdb-get-value "firstname" line))
	 (company   (lookout-bbdb-get-value "company" line))
         (job       (lookout-bbdb-get-value "job" line))
	 (net       (lookout-bbdb-get-value "net" line))
	 (addr1     (lookout-bbdb-get-value "addr1" line))
	 (addr2     (lookout-bbdb-get-value "addr2" line))
	 (addr3     (lookout-bbdb-get-value "addr3" line))
	 (phones    (lookout-bbdb-get-value "phones" line t)) ;; !
	 (notes     (lookout-bbdb-get-value "notes" line ))
         (j (concat job ", " company))
	 (otherfields (lookout-bbdb-get-value "otherfields" line t))
	 (addrs nil)
         name-search
	 (message ""))
    (if (string= company "") (setq company nil))
    (if (string= notes "") (setq notes nil))
    (if (string= name "") (setq name nil))
    (setq name-search (concat "^" (or name (concat firstname " " lastname))))
    (setq record (or (bbdb-search (bbdb-records) nil nil net)
		     (bbdb-search (bbdb-records) name-search)))
    (if record
	(progn
	  ;; Matching records found, update first matching record
	  (setq record (car record))
	  (let ((nets (bbdb-record-net record)))
	    (unless (member net nets)
	      ;; New e-mail address noticed, add to front of list
	      (add-to-list 'nets net)
	      (bbdb-record-set-net record nets)
	      (message "%s: New e-mail address noticed: %s"
		       (or name (concat firstname " " lastname)) net)))
	  ;; Check if job title and company have changed
	  (when (or job company)
	    (cond
	     ((string= (or (bbdb-record-company record) "") "")
	      (bbdb-record-set-company record j))
	     ((string= (bbdb-record-company record) j)
	      nil)
	     (t
	      (bbdb-record-set-notes
	       record
	       (concat "Noticed change from job title of "
		       (bbdb-record-company record)
		       "\n"
		       (bbdb-record-notes record)))
	      (message "%s: Noticed change from job title of %s to %s"
		       (or name (concat firstname " " lastname))
		       (bbdb-record-company record) j)
	      (bbdb-record-set-company record j)))))
      ;; No record found, create record
      (if (and addr1 (> (length addr1) 0))
	  (add-to-list 'addrs
		       (vector "Address 1" (list addr1) "" "" "" "")))
      (if (and addr2 (> (length addr2) 0))
	  (add-to-list 'addrs
		       (vector "Address 2" (list addr2) "" "" "" "")))
      (if (and addr3 (> (length addr3) 0))
	  (add-to-list 'addrs
		       (vector "Address 3" (list addr3) "" "" "" "")))
      (setq record (list
		    (wicked/lookout-bbdb-create-entry
		     (or name (concat firstname " " lastname))
		     (concat job ", " company)
		     net
		     addrs
		     phones
		     notes
		     otherfields))))
    record))

(defun wicked/lookout-bbdb-create-entry (name company net addrs phones notes
					      &amp;amp;optional otherfields)
  (when (or t (y-or-n-p (format "Add %s to bbdb? " name)))
    ;;(message "Adding record to bbdb: %s" name)
    (let ((record (bbdb-create-internal name company net addrs phones notes)))
      (unless record (error "Error creating bbdb record"))
      (mapcar (lambda (i)
		(let ((field (make-symbol (aref i 0)))
		      (value (aref i 1)))
		  (when (and value (not (string= "" value)))
		    (bbdb-insert-new-field record field value))))
	      otherfields)
      record)))

(defun wicked/bbdb-import-csv-buffer ()
  "Import this buffer."
  (interactive)
  (let ((lookout-bbdb-mapping-table
	 wicked/lookout-bbdb-mapping-table-outlook))
    (bbdb-display-records
     (mapcar
      'wicked/bbdb-import-csv-line
      (csv-parse-buffer t)))))
</pre>
<h2>Project xxx: Import a vCard File into BBDB</h2>
<p>To import a vCard file (VCF) into BBDB, you will need vcard.el from http://www.splode.com/~friedman/software/emacs-lisp/src/vcard.el and bbdb-vcard-import.el from http://www-pu.informatik.uni-tuebingen.de/users/crestani/downloads/bbdb-vcard-import.el . By default, these files allow you to import names and e-mail addresses from vCard files exported from various address book programs. Save vcard.el and bbdb-vcard-import.el to your ~/elisp directory and add the following lines to your ~/.emacs:</p>
<pre>
(add-to-list 'load-path "~/elisp")
(require 'bbdb-vcard-import)
</pre>
<p>Back up your ~/.bbdb file before calling M-x bbdb-vcard-import to import a file or M-x bbdb-vcard-import-buffer to import the current buffer. WARNING: If your vCard file includes fields with multiline values, you may get silent errors. Verify your import by browsing through the displayed entries. If some of them have been misread, revert to your backup ~/.bbdb by closing Emacs and copying your backup over the ~/.bbdb file. To fix the multi-line error, include the following lines in your ~/.emacs:</p>
<pre>
(defun wicked/vcard-parse-region (beg end &amp;amp;optional filter)
  "Parse the raw vcard data in region, and return an alist representing data.
This function is just like `vcard-parse-string' except that it operates on
a region of the current buffer rather than taking a string as an argument.

Note: this function modifies the buffer!"
  (or filter
      (setq filter 'vcard-standard-filter))
  (let ((case-fold-search t)
        (vcard-data nil)
        (pos (make-marker))
        (newpos (make-marker))
        properties value)
    (save-restriction
      (narrow-to-region beg end)
      (save-match-data
        ;; Unfold folded lines and delete naked carriage returns
        (goto-char (point-min))
        (while (re-search-forward "\r$\\|\n[ \t]" nil t)
          (goto-char (match-beginning 0))
          (delete-char 1))
        (goto-char (point-min))
        (re-search-forward "^begin:[ \t]*vcard[ \t]*\n")
        (set-marker pos (point))
        (while (and (not (looking-at "^end[ \t]*:[ \t]*vcard[ \t]*$"))
                    (re-search-forward ":[ \t]*" nil t))
          (set-marker newpos (match-end 0))
          (setq properties
                (vcard-parse-region-properties pos (match-beginning 0)))
          (set-marker pos (marker-position newpos))
          (re-search-forward "\n[-A-Z0-9;=]+:")   ;; change to deal with multiline
          (set-marker newpos (1+ (match-beginning 0))) ;; change to deal with multiline
          (setq value
                (vcard-parse-region-value properties pos (match-beginning 0)))
          (set-marker pos (marker-position newpos))
          (goto-char pos)
          (funcall filter properties value)
          (setq vcard-data (cons (cons properties value) vcard-data)))))
    (nreverse vcard-data)))
;; Replace vcard.el's definition
(fset 'vcard-parse-region 'wicked/vcard-parse-region)
</pre>
<p>Because address book programs don&#039;t use standard labels for addresses and phone numbers, bbdb-vcard-import.el ignores those fields. For example, Gmail uses the generic field &#034;Label&#034; for address information and does not use separate fields for city, state, zip code, and country. While bbdb-snarf.el makes an attempt to extract addresses from plain text, it seems to be less trouble to export to the Outlook CSV format instead, or even to type the address in yourself. If you want to import addresses, see Project XXX: Import a CSV File into BBDB.</p>
<p>Here&#039;s a partial workaround to enable you to import phone numbers. I tested this code with vCard files from Gmail and LinkedIn. To try it out, add the following modifications to your ~/.emacs:</p>
<pre>
(defun wicked/bbdb-vcard-merge (record)
  "Merge data from vcard interactively into bbdb."
  (let* ((name (bbdb-vcard-values record "fn"))
	 (company (bbdb-vcard-values record "org"))
	 (net (bbdb-vcard-get-emails record))
	 (addrs (bbdb-vcard-get-addresses record))
	 (phones (bbdb-vcard-get-phones record))
	 (categories (bbdb-vcard-values record "categories"))
	 (notes (and (not (string= "" categories))
		     (list (cons 'categories categories))))
	 ;; TODO: addrs are not yet imported.  To do this right,
	 ;; figure out a way to map the several labels to
	 ;; `bbdb-default-label-list'.  Note, some phone number
	 ;; conversion may break the format of numbers.
	 (bbdb-north-american-phone-numbers-p nil)
	 (new-record (bbdb-vcard-merge-interactively name
						     company
						     net
						     nil ;; Skip addresses
						     phones ;; Include phones
						     notes)))
    (setq bbdb-vcard-merged-records (append bbdb-vcard-merged-records
					    (list new-record)))))
;; Replace bbdb-vcard-import.el's definition
(fset 'bbdb-vcard-merge 'wicked/bbdb-vcard-merge)
</pre>
<p>Evaluate this code or restart Emacs, then call M-x bbdb-import-vcard again, which should merge phone numbers into your BBDB records.</p>

<!-- start wp-tags-to-technorati 0.95 -->

<p class='technorati-tags'>Technorati Tags: <a class='technorati-link' href='http://technorati.com/tag/bbdb' rel='tag' target='_self'>bbdb</a>, <a class='technorati-link' href='http://technorati.com/tag/emacs' rel='tag' target='_self'>emacs</a>, <a class='technorati-link' href='http://technorati.com/tag/wickedcoolemacs' rel='tag' target='_self'>wickedcoolemacs</a></p>

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-import-csv-and-vcard-files/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Wicked Cool Emacs: BBDB: Work with Records</title>
		<link>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-work-with-records-2/</link>
		<comments>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-work-with-records-2/#comments</comments>
		<pubDate>Sat, 12 Apr 2008 19:21:58 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-work-with-records-2/</guid>
		<description><![CDATA[Creating Records

Creating a record in BBDB is not like creating a record in graphical address book programs. You will be prompted for each field through the minibuffer, one field at a time. Don&#039;t worry about making mistakes while entering data, as you can always edit the records afterwards.


To create a record, use the command M-x [...]]]></description>
			<content:encoded><![CDATA[<h2>Creating Records</h2>
<p>
Creating a record in BBDB is not like creating a record in graphical address book programs. You will be prompted for each field through the minibuffer, one field at a time. Don&#039;t worry about making mistakes while entering data, as you can always edit the records afterwards.
</p>
<p>
To create a record, use the command M-x bbdb-create. Here are the prompts you&#039;ll encounter:
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<col align="left"></col><col align="left"></col><col align="left"></col></p>
<tbody>
<tr>
<td>Prompt</td>
<td>Notes</td>
<td>Example</td>
</tr>
<tr>
<td>Name</td>
<td>Full name</td>
<td>John Doe</td>
</tr>
<tr>
<td>Company</td>
<td>Company or organization</td>
<td>ACME</td>
</tr>
<tr>
<td>Network Address</td>
<td>E-mail address (comma-separated list)</td>
<td>john@example.com</td>
</tr>
<tr>
<td>Address Description</td>
<td>Short identifier for address (Home, Office, etc.) - tab completion available. Leave blank if you have no address information, or if you are done.</td>
<td>Home</td>
</tr>
<tr>
<td>Street, line 1</td>
<td>Street address, line 1 (not including city, state, postal code or country)</td>
<td>1 Acme Road</td>
</tr>
<tr>
<td>Street, line &#8230;</td>
<td>Street address, more lines - press RET to indicate the end of the street address</td>
<td></td>
</tr>
<tr>
<td>City</td>
<td></td>
<td>Acme City</td>
</tr>
<tr>
<td>State</td>
<td>Abbreviations are okay. Consistency helps.</td>
<td>AC</td>
</tr>
<tr>
<td>Country</td>
<td></td>
<td>Acme Country</td>
</tr>
<tr>
<td>Phone Location</td>
<td>Short identifier for phone number (Home, Office, etc.) - tab completion available. Leave blank if you have no phone information, or if you are done.</td>
<td>Home</td>
</tr>
<tr>
<td>Phone</td>
<td>Phone number. I tend to specify the full number, using spaces to break it into readable chunks.</td>
<td>+1 111 111 1111 x1111</td>
</tr>
<tr>
<td>Additional Comments</td>
<td>Notes about the person, such as interests, how you met, and so on</td>
<td>Likes rockets</td>
</tr>
</tbody>
</table>
<p>
Press RET to skip any fields for which you don&#039;t have information. To<br />
cancel the entry process, type <kbd>C-g</kbd> (keyboard-quit).
</p>
<p>
After you create the record, Emacs will display the record in another<br />
window. You can then switch to the record and edit it. See Project XXX: Edit a BBDB record.
</p>
<h2>
Searching Records<br />
</h2>
<p>
To search for a specific record, type M-x bbdb, or press <kbd>b</kbd><br />
(bbdb) while in the <b>BBDB</b> buffer. This prompts for a regular<br />
expression and searches the name, company, network address, and notes<br />
fields of all the records for a match against the regular expression<br />
supplied. M-x bbdb-name, M-x bbdb-company, M-x bbdb-net, M-x<br />
bbdb-notes, and M-x bbdb-phones search the corresponding fields only.
</p>
<h2>
Updating Records<br />
</h2>
<p>
After creating or searching for a record, you can switch to the <b>BBDB</b><br />
window to edit it. Press <kbd>C-o</kbd> (bbdb-insert-field) to insert<br />
custom fields. You can use tab completion on existing field names, and<br />
you can also define your own fields by typing any field name. For<br />
example, you may want to store people&#039;s job titles in a field called<br />
&#034;job&#034;.
</p>
<p>
To edit the value of a field, move your cursor to the field and press<br />
<kbd>e</kbd> (bbdb-edit-current-field) to change the value. To delete<br />
a field, move your cursor to the field and press <kbd>C-k</kbd><br />
(bbdb-delete-current-field-or-record).
</p>
<h2>
Deleting Records<br />
</h2>
<p>
To delete an entire record, move the text cursor to the name and press <kbd>C-k</kbd> (bbdb-delete-current-field-or-record).  You will be prompted for confirmation. Be careful! If you mistakenly delete a record, there&#039;s no easy way to get it back. Fortunately, BBDB stores its data in a plain text file (~/.bbdb). Back up that file regularly and you&#039;ll be able to recover from mistakes. You can also set up automatic file backups in Emacs (see Project XXX: Make Automatic Backups).
</p>
<p>
Now you know how to work with individual records. How can you import your address book information from other programs?</p>

<!-- start wp-tags-to-technorati 0.95 -->

<p class='technorati-tags'>Technorati Tags: <a class='technorati-link' href='http://technorati.com/tag/bbdb' rel='tag' target='_self'>bbdb</a>, <a class='technorati-link' href='http://technorati.com/tag/emacs' rel='tag' target='_self'>emacs</a>, <a class='technorati-link' href='http://technorati.com/tag/wickedcoolemacs' rel='tag' target='_self'>wickedcoolemacs</a></p>

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-work-with-records-2/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Wicked Cool Emacs: BBDB: Set up BBDB</title>
		<link>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-set-up-bbdb/</link>
		<comments>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-set-up-bbdb/#comments</comments>
		<pubDate>Sat, 12 Apr 2008 19:10:14 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-set-up-bbdb/</guid>
		<description><![CDATA[The main address book and contact management module for Emacs is the Insidious Big Brother Database (BBDB), which can be integrated into several mail clients and other modules within Emacs. If you use BBDB to keep track of contact information, you&#039;ll be able to look up phone numbers or add notes to people&#039;s records from [...]]]></description>
			<content:encoded><![CDATA[<p>The main address book and contact management module for Emacs is the Insidious Big Brother Database (BBDB), which can be integrated into several mail clients and other modules within Emacs. If you use BBDB to keep track of contact information, you&#039;ll be able to look up phone numbers or add notes to people&#039;s records from your Emacs-based mail. Even if you don&#039;t do e-mail within Emacs, you&#039;ll find that BBDB&#039;s customizability makes it surprisingly powerful.</p>
<p>In this project, you will learn how to set up BBDB as a basic address book.  The BBDB homepage is at <i>http://bbdb.sourceforge.net/</i>.  The development version fixes a number of bugs, so I recommend you try it instead of the stable version. However, if you are on Microsoft Windows or you do not have development tools handy, you might find the stable version easier to install. As of this writing, the stable version (2.35) can be downloaded from http://bbdb.sourceforge.net/bbdb-2.35.tar.gz . Download and unpack it to ~/elisp/bbdb-2.35, and save the pre-built bbdb-autoloads.el from http://bbdb.sourceforge.net/bbdb-autoloads.el into ~/elisp/bbdb-2.35/lisp .</p>
<p>To check out the development version, change to your <i>~/elisp</i> directory and type in the following lines at the command prompt:</p>
<pre>
cvs -d :pserver:anonymous@bbdb.cvs.sourceforge.net:/cvsroot/bbdb login
cvs -d :pserver:anonymous@bbdb.cvs.sourceforge.net:/cvsroot/bbdb checkout bbdb
</pre>
<p>You should now have a directory called <i>~/elisp/bbdb</i>. Change to that directory and run the following commands:</p>
<pre>
autoconf
./configure
make autoloads
make all
</pre>
<p>After installing either the stable or development version of BBDB, include it in your load-path by adding the appropriate line to your <i>~/.emacs</i>:</p>
<pre>
(add-to-list 'load-path "~/elisp/bbdb-2.35/lisp")    ;; (1)
(add-to-list 'load-path "~/elisp/bbdb/lisp")         ;; (2)

(require 'bbdb) ;; (3)
(bbdb-initialize 'gnus 'message)   ;; (4)
(setq bbdb-north-american-phone-numbers-p nil)   ;; (5)
</pre>
<p>Use either <i>~/elisp/bbdb-2.35/lisp</i>(1) or <i>~/elisp/bbdb/lisp</i>(2) depending on the location of the installed BBDB Lisp files. Then load BBDB(3) and configure it for the Gnus mail client and the Message mode used to compose mail(4). It&#039;s also a good idea to configure BBDB to accept any kind of phone number(5), not just North American numbers with a particular syntax.</p>
<p>After you evaluate this code or restart Emacs, BBDB should be part of your system. Next step: enter your address book!</p>

<!-- start wp-tags-to-technorati 0.95 -->

<p class='technorati-tags'>Technorati Tags: <a class='technorati-link' href='http://technorati.com/tag/bbdb' rel='tag' target='_self'>bbdb</a>, <a class='technorati-link' href='http://technorati.com/tag/emacs' rel='tag' target='_self'>emacs</a>, <a class='technorati-link' href='http://technorati.com/tag/wickedcoolemacs' rel='tag' target='_self'>wickedcoolemacs</a></p>

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/04/12/wicked-cool-emacs-bbdb-set-up-bbdb/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Wicked Cool Emacs: BBDB: Use nicknames and custom salutations</title>
		<link>http://sachachua.com/wp/2008/03/24/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/</link>
		<comments>http://sachachua.com/wp/2008/03/24/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/#comments</comments>
		<pubDate>Mon, 24 Mar 2008 08:26:48 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<category><![CDATA[elisp]]></category>

		<category><![CDATA[personalization]]></category>

		<category><![CDATA[wicked cool emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/03/24/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/</guid>
		<description><![CDATA[I like starting my e-mail with a short salutation such as &#034;Hello, Mike!&#034;, &#034;Hello, Michael&#034;, or &#034;Hello, Mikong!&#034;, but it can be hard to remember which nicknames people prefer to use, and calling someone by the wrong name is a bit of a faux pas. Sometimes people sign e-mail with their preferred name, but what [...]]]></description>
			<content:encoded><![CDATA[<p>I like starting my e-mail with a short salutation such as &#034;Hello, Mike!&#034;, &#034;Hello, Michael&#034;, or &#034;Hello, Mikong!&#034;, but it can be hard to remember which nicknames people prefer to use, and calling someone by the wrong name is a bit of a faux pas. Sometimes people sign e-mail with their preferred name, but what if you haven&#039;t sent e-mail to or received e-mail from someone in a while? In this project, you&#039;ll learn how to set up my BBDB to remember people&#039;s nicknames for you using a custom &#034;nick&#034; field, and to use those nicknames when replying to messages in Gnus or composing messages from my BBDB.</p>
<p>The nickname code worked so well that I started thinking of what else I could customize. It was easy to go from nicknames to personalized salutations. This hack started because one of my friends is from Romania, so I thought I&#039;d greet her in Romanian with &#034;Salut, Letitia!&#034;  instead of just &#034;Hello, Letitia!&#034;. The code in this project uses a &#034;hello&#034; field to store these salutations in your BBDB.</p>
<p>To set up personalized nicknames and salutations, add the following code to your ~/.emacs:</p>
<pre>
(defvar wicked/gnus-nick-threshold 5 "*Number of people to stop greeting individually. Nil means always greet individually.")  ;; (1)
(defvar wicked/bbdb-hello-string "Hello, %s!" "Format string for hello. Example: \"Hello, %s!\"")
(defvar wicked/bbdb-hello-all-string "Hello, all!" "String for hello when there are many people. Example: \"Hello, all!\"")
(defvar wicked/bbdb-nick-field 'nick "Symbol name for nickname field in BBDB.")
(defvar wicked/bbdb-salutation-field 'hello "Symbol name for salutation field in BBDB.")

(defun wicked/gnus-add-nick-to-message ()
  "Inserts \"Hello, NICK!\" in messages based on the recipient's nick field."
  (interactive)
  (save-excursion
    (let* ((bbdb-get-addresses-headers ;; (2)
            (list (assoc 'recipients bbdb-get-addresses-headers)))
           (recipients (bbdb-get-addresses
                        nil
                        gnus-ignored-from-addresses
                        'gnus-fetch-field))
           recipient nicks rec net salutations)
      (goto-char (point-min))
      (when (re-search-forward "--text follows this line--" nil t)
        (forward-line 1)
        (if (and wicked/gnus-nick-threshold
                 (&gt;= (length recipients) wicked/gnus-nick-threshold))
            (insert wicked/bbdb-hello-all-string "\n\n") ;; (3)
          (while recipients
            (setq recipient (car (cddr (car recipients))))
            (setq net (nth 1 recipient))
            (setq rec (car (bbdb-search (bbdb-records) nil nil net)))
            (cond
             ((null rec) ;; (4)
              (add-to-list 'nicks (car recipient)))
             ((bbdb-record-getprop rec wicked/bbdb-salutation-field) ;; (5)
              (add-to-list 'salutations
                           (bbdb-record-getprop rec wicked/bbdb-salutation-field)))
             ((bbdb-record-getprop rec wicked/bbdb-nick-field) ;; (6)
              (add-to-list 'nicks
                           (bbdb-record-getprop rec wicked/bbdb-nick-field)))
             (t (bbdb-record-name rec))) ;; (7)
            (setq recipients (cdr recipients))))
        (when nicks ;; (8)
          (insert (format wicked/bbdb-hello-string
                          (mapconcat 'identity (nreverse nicks) ", "))
                  " "))
        (when salutations ;; (9)
          (insert (mapconcat 'identity salutations " ")))
        (when (or nicks salutations)
          (insert "\n\n")))))
  (goto-char (point-min)))

(defadvice gnus-post-news (after wicked/bbdb activate)
  "Insert nicknames or custom salutations."
  (wicked/gnus-add-nick-to-message))

(defadvice gnus-msg-mail (after wicked/bbdb activate)
  "Insert nicknames or custom salutations."
  (wicked/gnus-add-nick-to-message))

(defadvice gnus-summary-reply (after wicked/bbdb activate)
  "Insert nicknames or custom salutations."
  (wicked/gnus-add-nick-to-message))
</pre>
<p>After you add this code, you can store personalized nicknames and salutations in your BBDB. Nicknames and salutations will be looked up using people&#039;s e-mail addresses. While in the *BBDB* buffer, you can type C-o (bbdb-insert-new-field) to add a field to the current record. Add a &#034;nick&#034; field with the person&#039;s nickname, or a &#034;hello&#034; field with a custom salutation. When you compose a message to or reply to a message from that person, the salutation or nickname will be included. If no nickname can be found, the recipient&#039;s name will be used instead.</p>
<p>A number of variables can be used to modify the behavior of this code(1). For example, you may or may not want to greet 20 people individually. The default value of wicked/gnus-nick-threshold is to greet up to four people individually, and greet more people collectively. If you always want to greet people individually, add (setq wicked/gnus-nick-threshold nil) to your ~/.emacs. If you want to change the strings used to greet people individually or collectively, change wicked/bbdb-hello-string and wicked/bbdb-hello-all-string. If you want to store the data into different fields, change wicked/bbdb-nick-field and wicked/bbdb-salutation-field, but note that old data will not be automatically copied to the new fields.</p>
<p>Here&#039;s how the code works. First, it retrieves the list of addresses from the header(2). If there are more addresses than wicked/gnus-nick-threshold, then wicked/bbdb-hello-all-string is used to greet everyone. If not, each recipient address is looked up. If the recipient cannot be found in your BBDB, then the recipient&#039;s name or e-mail address is used(4). If there is a personalized salutation, it is used(5). If there is a nickname, it is used(6). If the person has a record but neither salutation or nickname, then the name of the record is used(7). After all recipients have been processed, the names are added to the message(8), followed by the salutations(9). This function is added to the different Gnus message-posting functions, so it should be called whenever you compose or reply to messages.</p>
<p>You can use BBDB to personalize even more. Check out &#034;Project XXX: Personalize signatures&#034; for more ideas.</p>

<!-- start wp-tags-to-technorati 0.95 -->

<p class='technorati-tags'>Technorati Tags: <a class='technorati-link' href='http://technorati.com/tag/bbdb' rel='tag' target='_self'>bbdb</a>, <a class='technorati-link' href='http://technorati.com/tag/elisp' rel='tag' target='_self'>elisp</a>, <a class='technorati-link' href='http://technorati.com/tag/emacs' rel='tag' target='_self'>emacs</a>, <a class='technorati-link' href='http://technorati.com/tag/personalization' rel='tag' target='_self'>personalization</a>, <a class='technorati-link' href='http://technorati.com/tag/wicked+cool+emacs' rel='tag' target='_self'>wicked cool emacs</a></p>

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/03/24/wicked-cool-emacs-bbdb-use-nicknames-and-custom-salutations/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BBDB: Filtering by Mail Alias</title>
		<link>http://sachachua.com/wp/2008/02/16/bbdb-filtering-by-mail-alias/</link>
		<comments>http://sachachua.com/wp/2008/02/16/bbdb-filtering-by-mail-alias/#comments</comments>
		<pubDate>Sat, 16 Feb 2008 06:11:42 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/02/16/bbdb-filtering-by-mail-alias/</guid>
		<description><![CDATA[You can use &#034;a&#034; (bbdb-add-or-remove-mail-alias) in BBDB buffers to add a mail alias to the current entry, or &#034;* a&#034; to add a mail alias to all displayed entries. I use mail aliases to tag or categorize my contacts (example: emacs, writing, etc.). The following functions can make it easy for you to filter displayed [...]]]></description>
			<content:encoded><![CDATA[<p>You can use &#034;a&#034; (bbdb-add-or-remove-mail-alias) in BBDB buffers to add a mail alias to the current entry, or &#034;* a&#034; to add a mail alias to all displayed entries. I use mail aliases to tag or categorize my contacts (example: emacs, writing, etc.). The following functions can make it easy for you to filter displayed records using a combination of keywords:</p>
<table>
<tr>
<td>Display records matching ALIAS and ALIAS</td>
<td>M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias</td>
</tr>
<tr>
<td>Display records matching ALIAS or ALIAS</td>
<td>C-u M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias</td>
</tr>
<tr>
<td>Omit records matching ALIAS and ALIAS</td>
<td>M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias</td>
</tr>
<tr>
<td>Omit records matching ALIAS or ALIAS</td>
<td>C-u M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias</td>
</tr>
</table>
<p>Here&#039;s the code:</p>
<pre>
(defun sacha/bbdb-filter-by-alias-match-all (query-aliases record-aliases)
  "Return non-nil if all QUERY-ALIASES are in RECORD-ALIASES."
  (let ((result t))
    (while query-aliases
      (unless (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result nil))
      (setq query-aliases (cdr query-aliases)))
    result))

(defun sacha/bbdb-filter-by-alias-match-any (query-aliases record-aliases)
  "Return non-nil if any in QUERY-ALIASES can be found in RECORD-ALIASES."
  (let (result)
    (while query-aliases
      (when (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result t))
      (setq query-aliases (cdr query-aliases)))
    result))

;; Moved this to a convenience function so that we don't
;; have to deal with invert and property splitting.
(defun sacha/bbdb-filter-by-alias (bbdb-records
                                   alias-filter-function
                                   query
                                   &#038;optional invert)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept two arguments:
 - QUERY, a list of keywords to search for
 - aliases, a list of keywords from the record
If INVERT is non-nil, return only the records that do
not match."
  (delq nil
        (mapcar
         (lambda (rec)
           (if (funcall alias-filter-function
                        query
                        (split-string
                         (or (bbdb-record-getprop
                              (if (vectorp rec)
                                  rec
                                (car rec))
                              'mail-alias) "")
                         "[ \n\t,]+"))
               (when (null invert) rec)
             (when invert rec)))
         bbdb-records)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-filter-displayed-records-by-alias (query &#038;optional any)
  "Display only records whose mail-aliases match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.
See also `sacha/bbdb-omit-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases)))
                current-prefix-arg))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-omit-displayed-records-by-alias (query &#038;optional any)
  "Display only records whose mail-aliases do not match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.

See also `sacha/bbdb-filter-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases))
                  current-prefix-arg)))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query
    t)))
</pre>
<p>This will be part of my book, <a href="http://sachachua.com/wp/category/wickedcoolemacs">Wicked Cool Emacs</a>. Looking forward to putting it together!</p>

<!-- start wp-tags-to-technorati 0.95 -->

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/02/16/bbdb-filtering-by-mail-alias/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BBDB: Show a phone list</title>
		<link>http://sachachua.com/wp/2008/02/14/bbdb-show-a-phone-list/</link>
		<comments>http://sachachua.com/wp/2008/02/14/bbdb-show-a-phone-list/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 03:27:50 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/02/14/bbdb-show-a-phone-list/</guid>
		<description><![CDATA[When I find myself in an airport, I sometimes take a little time to say hi to a bunch of people who are suddenly just a local call a way. Or sometimes I&#039;m thinking of going somewhere, and instead of flipping through my phone&#039;s address book, I&#039;ll check my computer to see who might be [...]]]></description>
			<content:encoded><![CDATA[<p>When I find myself in an airport, I sometimes take a little time to say hi to a bunch of people who are suddenly just a local call a way. Or sometimes I&#039;m thinking of going somewhere, and instead of flipping through my phone&#039;s address book, I&#039;ll check my computer to see who might be interested.</p>
<p>You can use this function to filter phone numbers in your BBDB based on a regular expression. As usual, leaving the regular expression blank means that all records with phone numbers will be displayed. By default, the function works on the currently displayed records, allowing you to apply multiple filters. You can call it with a universal prefix argument (C-u M-x sacha/bbdb-find-people-with-phones) to match against all contacts in your database.</p>
<p>Here&#039;s the code:</p>
<pre>
(defun sacha/bbdb-find-people-with-phones (&#038;optional regexp records)
  "Search for phone numbers that match REGEXP in BBDB RECORDS.
Without a prefix argument, filter the list of displayed records.
Call with a prefix argument to search the entire database.  This
works best if you use a consistent format to store your phone
numbers.  The search will strip out non-numeric characters. For
example, +1-888-123-4567 will be treated as +18001234567.

To search for all numbers in Toronto, search for
\"+1\\(416\\|647\\)\". If you search for certain areas
frequently, it might be a good idea to define a function for
them."
  (interactive (list (read-string "Regexp: ")
		     (if current-prefix-arg
			 (bbdb-records)
		       (or bbdb-records (bbdb-records)))))
  (let (filtered next)
    (while records
      (when
          (and (bbdb-record-get-field-internal
		(if (arrayp (car records))
		    (car records)
		  (caar records)) 'phone)
               (or
                (null regexp)
		(string= regexp "")
                (delq nil
                      (mapcar
                       (lambda (phone)
			 (when (string-match regexp (sacha/bbdb-phone-string phone))
			   (concat (bbdb-phone-location phone) ": " (bbdb-phone-string phone))))
                       (bbdb-record-get-field-internal
                        (if (arrayp (car records))
                            (car records)
                          (caar records)) 'phone)))))
        (setq filtered (cons (if (arrayp (car records))
                                 (car records)
                               (caar records)) filtered)))
      (setq records (cdr records)))
    (bbdb-display-records (nreverse filtered))))

(defun sacha/bbdb-phone-string (&#038;optional phone)
  "Strip non-numeric characters from PHONE, except for +."
  (replace-regexp-in-string "[^+1234567890]" "" (bbdb-phone-string phone)))

(defun sacha/bbdb-yank-phones ()
  "Copy a phone list into the kill ring."
  (interactive)
  (kill-new
   (mapconcat
    (lambda (record)
      (mapconcat
       (lambda (phone)
	 (concat (bbdb-record-name (car record)) "\t"
                 (bbdb-phone-location phone) "\t"
		 (bbdb-phone-string phone)))
        (bbdb-record-get-field-internal (car record) 'phone)
        "\n"))
    bbdb-records
    "\n")))
</pre>

<!-- start wp-tags-to-technorati 0.95 -->

<p class='technorati-tags'>Technorati Tags: <a class='technorati-link' href='http://technorati.com/tag/bbdb' rel='tag' target='_self'>bbdb</a></p>

<!-- end wp-tags-to-technorati -->
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2008/02/14/bbdb-show-a-phone-list/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BBDB: Print birthdates</title>
		<link>http://sachachua.com/wp/2007/01/15/bbdb-print-birthdates/</link>
		<comments>http://sachachua.com/wp/2007/01/15/bbdb-print-birthdates/#comments</comments>
		<pubDate>Tue, 16 Jan 2007 00:30:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[crm]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2007.01.15.php#anchor-2</guid>
		<description><![CDATA[<p>This snippet goes through all the records in my Big Brother Database,
prints out birthdate and a link to the record, and then sorts the
results.</p>

<pre class="example">
(defun sacha/bbdb-insert-birthdates ()
  "Insert a list of birthdates, sorted by month.
For best effect, dates should be of the form yyyy.mm.dd."
  (insert
   (with-temp-buffer
     (mapcar
      (lambda (rec)
        (when (bbdb-record-getprop rec 'birthdate)
          (insert
           (if (string-match "..\\...$" (bbdb-record-getprop rec 'birthdate))
               (match-string 0 (bbdb-record-getprop rec 'birthdate))
             (bbdb-record-getprop rec 'birthdate))
           " &#124; "
           (planner-make-link
            (concat "bbdb://"
                    (planner-replace-regexp-in-string
                     " " "." (bbdb-record-name rec)))
            (bbdb-record-name rec))
           "\n")))
      (bbdb-records))
     (sort-lines nil (point-min) (point-max))
     (buffer-string)))
  nil)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a></p>

<p>Random Emacs symbol: find-tag-noselect - Command: Find tag (in current tags table) whose name contains TAGNAME.</p>
]]></description>
			<content:encoded><![CDATA[<p>This snippet goes through all the records in my Big Brother Database,
prints out birthdate and a link to the record, and then sorts the
results.</p>

<pre class="example">
(defun sacha/bbdb-insert-birthdates ()
  "Insert a list of birthdates, sorted by month.
For best effect, dates should be of the form yyyy.mm.dd."
  (insert
   (with-temp-buffer
     (mapcar
      (lambda (rec)
        (when (bbdb-record-getprop rec 'birthdate)
          (insert
           (if (string-match "..\\...$" (bbdb-record-getprop rec 'birthdate))
               (match-string 0 (bbdb-record-getprop rec 'birthdate))
             (bbdb-record-getprop rec 'birthdate))
           " | "
           (planner-make-link
            (concat "bbdb://"
                    (planner-replace-regexp-in-string
                     " " "." (bbdb-record-name rec)))
            (bbdb-record-name rec))
           "\n")))
      (bbdb-records))
     (sort-lines nil (point-min) (point-max))
     (buffer-string)))
  nil)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a></p>

<p>Random Emacs symbol: find-tag-noselect - Command: Find tag (in current tags table) whose name contains TAGNAME.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2007/01/15/bbdb-print-birthdates/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Contact report</title>
		<link>http://sachachua.com/wp/2006/11/02/contact-report/</link>
		<comments>http://sachachua.com/wp/2006/11/02/contact-report/#comments</comments>
		<pubDate>Thu, 02 Nov 2006 20:07:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[gnus]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<category><![CDATA[planner]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.11.02.php#anchor-2</guid>
		<description><![CDATA[<p>I started tracking e-mail sent on 2006.09.01 with a
<a href="http://sachachua.com/notebook/wiki/2006.09.01#2">nifty piece of Emacs Lisp code</a> I wrote just for the
purpose. Now I have two months of interesting data which include not
only e-mail but also the occasional in-person contact or phone call
that I remember to note. It's not complete - e-mail's the only thing
that gets automatically tracked - but it does give me interesting
information. Here's the contact report for your amusement:</p>

<p><a href="http://sachachua.com/notebook/wiki/2006.10#contact">Contact report</a></p>

<p>It's sorted by overall frequency and then by regular frequency.
Warning! Parentheses follow.</p>

<pre class="example">
(defun sacha/count-matches (regexp string)
  (let ((count 0)
        (start 0))
    (while (string-match regexp string start)
      (setq start (match-end 0)
            count (1+ count)))
    count))

(defun sacha/bbdb-contact-report-as-alist (&#038;rest regexps)
  "Creates a list of (name count-regexp1 count-regexp2 count-regexp3)..."
  (setq regexps (reverse regexps))
  (delq nil
        (mapcar
         (lambda (rec)
           (when (bbdb-record-name (car rec))
             (let ((reg regexps)
                   (notes (bbdb-record-notes (car rec)))
                   list)
               (while reg
                 (setq list (cons (sacha/count-matches (car reg) notes)
                                  list))
                 (setq reg (cdr reg)))
               (cons (sacha/planner-bbdb-annotation-from-bbdb rec)
                     list))))
         bbdb-records)))

(defun sacha/bbdb-alist-sort-by-total (alist)
  "Sort ALIST by total contact."
  (sort alist 'sacha/bbdb-contact-sort-predicate))

(defun sacha/bbdb-contact-sort-predicate (a b)
  (and a b
       (let ((count-a (apply '+ (cdr a)))
             (count-b (apply '+ (cdr b))))
         (or
          (> count-a count-b)
          (and (= count-a count-b)
               ;; If equal, look at the subtotal of the rest
               (sacha/bbdb-contact-sort-predicate (cdr a) (cdr b)))))))

(defun sacha/bbdb-kill-contact-barchart (alist)
  "Kill a barchart with the contact report for ALIST."
  (kill-new
   (mapconcat
    (lambda (entry)
      (concat
       (car entry)
       " &#124; "
       (mapconcat (lambda (count)
                    (if (= count 0)
                        "&#160;"
                      (make-string count ?-)))
                  (cdr entry)
                  " &#124; ")))
    alist
    "\n")))

;; Usage: (sacha/bbdb-kill-contact-barchart
;;         (sacha/bbdb-alist-sort-by-total
;;          (sacha/bbdb-contact-report-as-alist "2006.09" "2006.10")))
;; Then yank (paste) this into another buffer
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a></p>

<p>Random Emacs symbol: standard-display-cyrillic-translit - Command: Display a cyrillic buffer using a transliteration.</p>
]]></description>
			<content:encoded><![CDATA[<p>I started tracking e-mail sent on 2006.09.01 with a
<a href="http://sachachua.com/notebook/wiki/2006.09.01#2">nifty piece of Emacs Lisp code</a> I wrote just for the
purpose. Now I have two months of interesting data which include not
only e-mail but also the occasional in-person contact or phone call
that I remember to note. It's not complete - e-mail's the only thing
that gets automatically tracked - but it does give me interesting
information. Here's the contact report for your amusement:</p>

<p><a href="http://sachachua.com/notebook/wiki/2006.10#contact">Contact report</a></p>

<p>It's sorted by overall frequency and then by regular frequency.
Warning! Parentheses follow.</p>

<pre class="example">
(defun sacha/count-matches (regexp string)
  (let ((count 0)
        (start 0))
    (while (string-match regexp string start)
      (setq start (match-end 0)
            count (1+ count)))
    count))

(defun sacha/bbdb-contact-report-as-alist (&rest regexps)
  "Creates a list of (name count-regexp1 count-regexp2 count-regexp3)..."
  (setq regexps (reverse regexps))
  (delq nil
        (mapcar
         (lambda (rec)
           (when (bbdb-record-name (car rec))
             (let ((reg regexps)
                   (notes (bbdb-record-notes (car rec)))
                   list)
               (while reg
                 (setq list (cons (sacha/count-matches (car reg) notes)
                                  list))
                 (setq reg (cdr reg)))
               (cons (sacha/planner-bbdb-annotation-from-bbdb rec)
                     list))))
         bbdb-records)))

(defun sacha/bbdb-alist-sort-by-total (alist)
  "Sort ALIST by total contact."
  (sort alist 'sacha/bbdb-contact-sort-predicate))

(defun sacha/bbdb-contact-sort-predicate (a b)
  (and a b
       (let ((count-a (apply '+ (cdr a)))
             (count-b (apply '+ (cdr b))))
         (or
          (> count-a count-b)
          (and (= count-a count-b)
               ;; If equal, look at the subtotal of the rest
               (sacha/bbdb-contact-sort-predicate (cdr a) (cdr b)))))))

(defun sacha/bbdb-kill-contact-barchart (alist)
  "Kill a barchart with the contact report for ALIST."
  (kill-new
   (mapconcat
    (lambda (entry)
      (concat
       (car entry)
       " | "
       (mapconcat (lambda (count)
                    (if (= count 0)
                        "&nbsp;"
                      (make-string count ?-)))
                  (cdr entry)
                  " | ")))
    alist
    "\n")))

;; Usage: (sacha/bbdb-kill-contact-barchart
;;         (sacha/bbdb-alist-sort-by-total
;;          (sacha/bbdb-contact-report-as-alist "2006.09" "2006.10")))
;; Then yank (paste) this into another buffer
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a></p>

<p>Random Emacs symbol: standard-display-cyrillic-translit - Command: Display a cyrillic buffer using a transliteration.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/11/02/contact-report/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Crazy Emacs: Personalized signatures with random taglines</title>
		<link>http://sachachua.com/wp/2006/10/02/crazy-emacs-personalized-signatures-with-random-taglines/</link>
		<comments>http://sachachua.com/wp/2006/10/02/crazy-emacs-personalized-signatures-with-random-taglines/#comments</comments>
		<pubDate>Mon, 02 Oct 2006 19:05:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[elisp]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[hack]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.10.02.php#anchor-11</guid>
		<description><![CDATA[<p>Of course, that naturally leads to the crazy idea: "What if I can
personalize my signatures?" Knowing that Paul Lussier is an Emacs geek, I can reward him for reading all the way
to the bottom of my message... ;)</p>

<pre class="example">
(defun sacha/gnus-personalize-signature ()
  "Personalizes signature based on BBDB signature field.
BBDB signature field should be a lambda expression.
First person with a custom signature field gets used."
  (let* ((bbdb-get-addresses-headers
          (list (assoc 'recipients bbdb-get-addresses-headers)))
         (records (bbdb-update-records
                   (bbdb-get-addresses
                    nil
                    gnus-ignored-from-addresses 'gnus-fetch-field)
                   nil
                   nil))
         signature)
    (while (and records (not signature))
      (when (bbdb-record-getprop (car records) 'signature)
        (setq signature
              (eval (read (bbdb-record-getprop (car records)
                                               'signature)))))
      (setq records (cdr records)))
    (or signature t)))
(setq-default message-signature 'sacha/gnus-personalize-signature)
</pre>

<p>So then all I have to do is add the following field to his record:</p>

<pre class="example">
      signature: (concat "Sacha Chua - Emacs geek
                 What crazy idea can I help you hack next?
                 Random Emacs symbol: "
                 (sacha/random-tagline
                  "~/.taglines.random-emacs-symbols"))
</pre>

<p>Emacs. One crazy idea at a time. Now I can use this to select random
information, like my favorite networking books or a list of my
upcoming events...</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a></p>

<p>Random Emacs symbol: sort-coding-systems-predicate - Variable: If non-nil, a predicate function to sort coding systems.</p>
]]></description>
			<content:encoded><![CDATA[<p>Of course, that naturally leads to the crazy idea: "What if I can
personalize my signatures?" Knowing that Paul Lussier is an Emacs geek, I can reward him for reading all the way
to the bottom of my message... ;)</p>

<pre class="example">
(defun sacha/gnus-personalize-signature ()
  "Personalizes signature based on BBDB signature field.
BBDB signature field should be a lambda expression.
First person with a custom signature field gets used."
  (let* ((bbdb-get-addresses-headers
          (list (assoc 'recipients bbdb-get-addresses-headers)))
         (records (bbdb-update-records
                   (bbdb-get-addresses
                    nil
                    gnus-ignored-from-addresses 'gnus-fetch-field)
                   nil
                   nil))
         signature)
    (while (and records (not signature))
      (when (bbdb-record-getprop (car records) 'signature)
        (setq signature
              (eval (read (bbdb-record-getprop (car records)
                                               'signature)))))
      (setq records (cdr records)))
    (or signature t)))
(setq-default message-signature 'sacha/gnus-personalize-signature)
</pre>

<p>So then all I have to do is add the following field to his record:</p>

<pre class="example">
      signature: (concat "Sacha Chua - Emacs geek
                 What crazy idea can I help you hack next?
                 Random Emacs symbol: "
                 (sacha/random-tagline
                  "~/.taglines.random-emacs-symbols"))
</pre>

<p>Emacs. One crazy idea at a time. Now I can use this to select random
information, like my favorite networking books or a list of my
upcoming events...</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a></p>

<p>Random Emacs symbol: sort-coding-systems-predicate - Variable: If non-nil, a predicate function to sort coding systems.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/10/02/crazy-emacs-personalized-signatures-with-random-taglines/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs BBDB: Prioritize exact matches</title>
		<link>http://sachachua.com/wp/2006/09/29/emacs-bbdb-prioritize-exact-matches/</link>
		<comments>http://sachachua.com/wp/2006/09/29/emacs-bbdb-prioritize-exact-matches/#comments</comments>
		<pubDate>Fri, 29 Sep 2006 19:54:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[crm]]></category>

		<category><![CDATA[elisp]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.29.php#anchor-8</guid>
		<description><![CDATA[<p>I often include people's names in my notes on other people, such as
when I'm tracking who introduced me to whom. The following code
modifies BBDB's behavior to put exact matches for name, company, or
network address above matches for notes.</p>

<pre class="example">
(defun sacha/bbdb (string elidep)
  "Display all entries in the BBDB matching the regexp STRING
in either the name(s), company, network address, or notes.
Prioritize non-note matches."
  (interactive
   (list (bbdb-search-prompt "Search records %m regexp: ")
         current-prefix-arg))
  (let* ((bbdb-display-layout (bbdb-grovel-elide-arg elidep))
         (notes (cons '* string))
         (records-top
          (bbdb-search (bbdb-records) string string string nil
                       nil))
         (records
          (bbdb-search (bbdb-records) string string string notes
                       nil))
         temp)
    (setq temp records-top)
    (while temp
      (setq records (delete (car temp) records))
      (setq temp (cdr temp)))
    (if (or records-top records)
        (bbdb-display-records (append
                               records-top
                               records))
      ;; we could use error here, but it's not really an error.
      (message "No records matching '%s'" string))))

(defalias 'bbdb 'sacha/bbdb)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></description>
			<content:encoded><![CDATA[<p>I often include people's names in my notes on other people, such as
when I'm tracking who introduced me to whom. The following code
modifies BBDB's behavior to put exact matches for name, company, or
network address above matches for notes.</p>

<pre class="example">
(defun sacha/bbdb (string elidep)
  "Display all entries in the BBDB matching the regexp STRING
in either the name(s), company, network address, or notes.
Prioritize non-note matches."
  (interactive
   (list (bbdb-search-prompt "Search records %m regexp: ")
         current-prefix-arg))
  (let* ((bbdb-display-layout (bbdb-grovel-elide-arg elidep))
         (notes (cons '* string))
         (records-top
          (bbdb-search (bbdb-records) string string string nil
                       nil))
         (records
          (bbdb-search (bbdb-records) string string string notes
                       nil))
         temp)
    (setq temp records-top)
    (while temp
      (setq records (delete (car temp) records))
      (setq temp (cdr temp)))
    (if (or records-top records)
        (bbdb-display-records (append
                               records-top
                               records))
      ;; we could use error here, but it's not really an error.
      (message "No records matching '%s'" string))))

(defalias 'bbdb 'sacha/bbdb)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/29/emacs-bbdb-prioritize-exact-matches/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs BBDB: Filtering tags with the power of lambda expressions</title>
		<link>http://sachachua.com/wp/2006/09/29/emacs-bbdb-filtering-tags-with-the-power-of-lambda-expressions/</link>
		<comments>http://sachachua.com/wp/2006/09/29/emacs-bbdb-filtering-tags-with-the-power-of-lambda-expressions/#comments</comments>
		<pubDate>Fri, 29 Sep 2006 15:28:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[crm]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[networking]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.29.php#anchor-2</guid>
		<description><![CDATA[<p>What do you do when you're into both Emacs geeks and social
networking? Well, you build a really really weird contact management
tool, that's what!</p>

<p>One of the things I often need to do is filter my contacts for a
particular set of interests. I would have no idea how to do this in
Microsoft Outlook and other proprietary contact management systems.
Because Emacs is infinitely programmable, though, I can just hack it
in.</p>

<p>You'd expect the intersection of the set "emacs geek" and the set
"social networker" to be a null set or a singleton (me!). As it turns
out, there's at least one other geek in this space - hooray!</p>

<p>Paul Lussier's been bouncing all sorts of
crazy ideas off me, which explains all the weird
porridge-and-toe-nails posts of Emacs Lisp code on my blog lately.
He's responsible for my puttting together yesterday's LinkedIn
importer. Today, he wrote:</p>

<blockquote> Then found your sacha/bbdb-search-tags stuff which
totally, completely rocks. I just wish I had the first inkling as to
how it worked :) Now, the question I have is: How can I use
sacha/bbdb-search-tags to search for entries which are tagged with one
label, but NOT with another? For example, I want to search on: (and
(taq eq "planner") (not (tag eq "muse"))) </blockquote>

<p>I'd completely forgotten about sacha/bbdb-search-tags! Anyway, I'd
been meaning to write a fancy alias management thing for a while now,
and this code does a reasonable job for me. I can now filter my
displayed records by arbitrary Lisp expressions, bringing me closer to
insane contact relationship management. I mean, c'mon...</p>

<pre class="example">
;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET
</pre>

<p>If I do this often enough, I might make up an easier syntax, but
lisp expressions work fine for me.</p>

<p>MWAHAHAHAHA!</p>

<p>Here's the code:</p>

<pre class="example">
;;;_+ Mail aliases

;; Code for working with aliases

;; You can use "a" (bbdb-add-or-remove-mail-alias) in BBDB buffers to add
;; a mail alias to the current entry, or "* a" to add a mail alias to
;; all displayed entries.

;; Goal: Be able to specify ALIAS and ALIAS
;; M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify ALIAS or ALIAS
;; C-u M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify not ...
;; M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias
;; C-u M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias

(defun sacha/bbdb-filter-by-alias-match-all (query-aliases record-aliases)
  "Return non-nil if all QUERY-ALIASES are in RECORD-ALIASES."
  (let ((result t))
    (while query-aliases
      (unless (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result nil))
      (setq query-aliases (cdr query-aliases)))
    result))

(defun sacha/bbdb-filter-by-alias-match-any (query-aliases record-aliases)
  "Return non-nil if any in QUERY-ALIASES can be found in RECORD-ALIASES."
  (let (result)
    (while query-aliases
      (when (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result t))
      (setq query-aliases (cdr query-aliases)))
    result))

;; Moved this to a convenience function so that we don't
;; have to deal with invert and property splitting.
(defun sacha/bbdb-filter-by-alias (bbdb-records
                                   alias-filter-function
                                   query
                                   &#038;optional invert)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept two arguments:
 - QUERY, a list of keywords to search for
 - aliases, a list of keywords from the record
If INVERT is non-nil, return only the records that do
not match."
  (delq nil
        (mapcar
         (lambda (rec)
           (if (funcall alias-filter-function
                        query
                        (split-string
                         (or (bbdb-record-getprop
                              (if (vectorp rec)
                                  rec
                                (car rec))
                              propsym) "")
                         "[ \n\t,]+"))
               (when (null invert) rec)
             (when invert rec)))
         bbdb-records)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-filter-displayed-records-by-alias (query &#038;optional any)
  "Display only records whose mail-aliases match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.
See also `sacha/bbdb-omit-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases)))
                current-prefix-arg))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-omit-displayed-records-by-alias (query &#038;optional any)
  "Display only records whose mail-aliases do not match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.

See also `sacha/bbdb-filter-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases))
                  current-prefix-arg)))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query
    t)))

;;;_+ Advanced mail alias queries

;; Goal: Use complicated lambda expressions to filter displayed records
;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET
;; Thanks to Paul Lussier for the suggestion!

(defun sacha/bbdb-filter-by-alias-function (bbdb-records
                                            alias-filter-function)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept one argument:
 - aliases, a list of keywords from the record."
  (interactive (list (or bbdb-records (bbdb-records))
                     (read t)))
  (let (records)
    (setq records
          (delq nil
                (mapcar
                 (lambda (rec)
                   (when (funcall alias-filter-function
                                  (split-string
                                   (or (bbdb-record-getprop
                                        (if (vectorp rec)
                                            rec
                                          (car rec))
                                        propsym) "")
                                   "[ \n\t,]+"))
                     rec))
                 bbdb-records)))
    (if (interactive-p) (bbdb-display-records records))
    records))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></description>
			<content:encoded><![CDATA[<p>What do you do when you're into both Emacs geeks and social
networking? Well, you build a really really weird contact management
tool, that's what!</p>

<p>One of the things I often need to do is filter my contacts for a
particular set of interests. I would have no idea how to do this in
Microsoft Outlook and other proprietary contact management systems.
Because Emacs is infinitely programmable, though, I can just hack it
in.</p>

<p>You'd expect the intersection of the set "emacs geek" and the set
"social networker" to be a null set or a singleton (me!). As it turns
out, there's at least one other geek in this space - hooray!</p>

<p>Paul Lussier's been bouncing all sorts of
crazy ideas off me, which explains all the weird
porridge-and-toe-nails posts of Emacs Lisp code on my blog lately.
He's responsible for my puttting together yesterday's LinkedIn
importer. Today, he wrote:</p>

<blockquote> Then found your sacha/bbdb-search-tags stuff which
totally, completely rocks. I just wish I had the first inkling as to
how it worked :) Now, the question I have is: How can I use
sacha/bbdb-search-tags to search for entries which are tagged with one
label, but NOT with another? For example, I want to search on: (and
(taq eq "planner") (not (tag eq "muse"))) </blockquote>

<p>I'd completely forgotten about sacha/bbdb-search-tags! Anyway, I'd
been meaning to write a fancy alias management thing for a while now,
and this code does a reasonable job for me. I can now filter my
displayed records by arbitrary Lisp expressions, bringing me closer to
insane contact relationship management. I mean, c'mon...</p>

<pre class="example">
;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET
</pre>

<p>If I do this often enough, I might make up an easier syntax, but
lisp expressions work fine for me.</p>

<p>MWAHAHAHAHA!</p>

<p>Here's the code:</p>

<pre class="example">
;;;_+ Mail aliases

;; Code for working with aliases

;; You can use "a" (bbdb-add-or-remove-mail-alias) in BBDB buffers to add
;; a mail alias to the current entry, or "* a" to add a mail alias to
;; all displayed entries.

;; Goal: Be able to specify ALIAS and ALIAS
;; M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify ALIAS or ALIAS
;; C-u M-x sacha/bbdb-filter-displayed-records-by-alias RET alias alias
;; Goal: Be able to specify not ...
;; M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias
;; C-u M-x sacha/bbdb-omit-displayed-records-by-alias RET alias alias

(defun sacha/bbdb-filter-by-alias-match-all (query-aliases record-aliases)
  "Return non-nil if all QUERY-ALIASES are in RECORD-ALIASES."
  (let ((result t))
    (while query-aliases
      (unless (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result nil))
      (setq query-aliases (cdr query-aliases)))
    result))

(defun sacha/bbdb-filter-by-alias-match-any (query-aliases record-aliases)
  "Return non-nil if any in QUERY-ALIASES can be found in RECORD-ALIASES."
  (let (result)
    (while query-aliases
      (when (member (car query-aliases) record-aliases)
        (setq query-aliases nil
              result t))
      (setq query-aliases (cdr query-aliases)))
    result))

;; Moved this to a convenience function so that we don't
;; have to deal with invert and property splitting.
(defun sacha/bbdb-filter-by-alias (bbdb-records
                                   alias-filter-function
                                   query
                                   &optional invert)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept two arguments:
 - QUERY, a list of keywords to search for
 - aliases, a list of keywords from the record
If INVERT is non-nil, return only the records that do
not match."
  (delq nil
        (mapcar
         (lambda (rec)
           (if (funcall alias-filter-function
                        query
                        (split-string
                         (or (bbdb-record-getprop
                              (if (vectorp rec)
                                  rec
                                (car rec))
                              propsym) "")
                         "[ \n\t,]+"))
               (when (null invert) rec)
             (when invert rec)))
         bbdb-records)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-filter-displayed-records-by-alias (query &optional any)
  "Display only records whose mail-aliases match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.
See also `sacha/bbdb-omit-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases)))
                current-prefix-arg))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query)))

;; Splitting this into two functions because of interactive calling.
(defun sacha/bbdb-omit-displayed-records-by-alias (query &optional any)
  "Display only records whose mail-aliases do not match QUERY.
If ANY is non-nil, match if any of the keywords in QUERY are
present.

See also `sacha/bbdb-filter-displayed-records-by-alias'."
  (interactive (list
                (let ((crm-separator " "))
                  (completing-read-multiple
                   "Mail aliases: "
                   (bbdb-get-mail-aliases))
                  current-prefix-arg)))
  (when (stringp query)
    (setq query (split-string query "[ \n\t,]+")))
  (bbdb-display-records
   (sacha/bbdb-filter-by-alias-by-function
    (or bbdb-records (bbdb-records))
    (if any
        'sacha/bbdb-filter-by-alias-match-any
      'sacha/bbdb-filter-by-alias-match-all)
    query
    t)))

;;;_+ Advanced mail alias queries

;; Goal: Use complicated lambda expressions to filter displayed records
;; M-x sacha/bbdb-filter-by-alias-function RET
;;     (lambda (aliases) (and (member "planner" aliases)
;;                       (not (member "muse" aliases))))) RET
;; Thanks to Paul Lussier for the suggestion!

(defun sacha/bbdb-filter-by-alias-function (bbdb-records
                                            alias-filter-function)
  "Return only the BBDB-RECORDS that match ALIAS-FILTER-FUNCTION.
ALIAS-FILTER-FUNCTION should accept one argument:
 - aliases, a list of keywords from the record."
  (interactive (list (or bbdb-records (bbdb-records))
                     (read t)))
  (let (records)
    (setq records
          (delq nil
                (mapcar
                 (lambda (rec)
                   (when (funcall alias-filter-function
                                  (split-string
                                   (or (bbdb-record-getprop
                                        (if (vectorp rec)
                                            rec
                                          (car rec))
                                        propsym) "")
                                   "[ \n\t,]+"))
                     rec))
                 bbdb-records)))
    (if (interactive-p) (bbdb-display-records records))
    records))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/29/emacs-bbdb-filtering-tags-with-the-power-of-lambda-expressions/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs: BBDB rapid serial visualization</title>
		<link>http://sachachua.com/wp/2006/09/28/emacs-bbdb-rapid-serial-visualization/</link>
		<comments>http://sachachua.com/wp/2006/09/28/emacs-bbdb-rapid-serial-visualization/#comments</comments>
		<pubDate>Thu, 28 Sep 2006 15:35:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[crm]]></category>

		<category><![CDATA[elisp]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.28.php#anchor-4</guid>
		<description><![CDATA[<p>And because it's good to quickly flash through records once in a while
to refresh my memory...</p>

<pre class="example">
(defvar sacha/bbdb-rapid-serial-visualization-delay
 1
 "*Number of seconds to wait between records.
Set to 0 to wait for input.")

(defun sacha/bbdb-rapid-serial-visualization ()
  "Breeze through everyone's name and notes."
  (interactive)
  (window-configuration-to-register ?a)
  ;; Copy the currently visible records
  (let ((records bbdb-records)
        (default-size (face-attribute 'default :height))
        (new-size 400)
        (continue t))
    (set-face-attribute 'default nil :height new-size)
    (pop-to-buffer (get-buffer-create "BBDB-Serial"))
    (delete-other-windows)
    (while (and records continue)
      (insert (bbdb-record-name (caar records))
              "\n\n"
              (or (car (bbdb-record-net (caar records))) "No e-mail")
              "\n\n"
              (or (bbdb-record-notes (caar records)) "")
              (make-string 50 ?\n))
      (goto-char (point-min))
      (sit-for sacha/bbdb-rapid-serial-visualization-delay)
      (setq records (cdr records)))
    (set-face-attribute 'default nil :height default-size)
    (when continue
      (jump-to-register ?a))))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></description>
			<content:encoded><![CDATA[<p>And because it's good to quickly flash through records once in a while
to refresh my memory...</p>

<pre class="example">
(defvar sacha/bbdb-rapid-serial-visualization-delay
 1
 "*Number of seconds to wait between records.
Set to 0 to wait for input.")

(defun sacha/bbdb-rapid-serial-visualization ()
  "Breeze through everyone's name and notes."
  (interactive)
  (window-configuration-to-register ?a)
  ;; Copy the currently visible records
  (let ((records bbdb-records)
        (default-size (face-attribute 'default :height))
        (new-size 400)
        (continue t))
    (set-face-attribute 'default nil :height new-size)
    (pop-to-buffer (get-buffer-create "BBDB-Serial"))
    (delete-other-windows)
    (while (and records continue)
      (insert (bbdb-record-name (caar records))
              "\n\n"
              (or (car (bbdb-record-net (caar records))) "No e-mail")
              "\n\n"
              (or (bbdb-record-notes (caar records)) "")
              (make-string 50 ?\n))
      (goto-char (point-min))
      (sit-for sacha/bbdb-rapid-serial-visualization-delay)
      (setq records (cdr records)))
    (set-face-attribute 'default nil :height default-size)
    (when continue
      (jump-to-register ?a))))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/28/emacs-bbdb-rapid-serial-visualization/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs: Show only people whom I haven&#039;t pinged since&#8230;</title>
		<link>http://sachachua.com/wp/2006/09/28/emacs-show-only-people-whom-i-havent-pinged-since/</link>
		<comments>http://sachachua.com/wp/2006/09/28/emacs-show-only-people-whom-i-havent-pinged-since/#comments</comments>
		<pubDate>Thu, 28 Sep 2006 15:23:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[elisp]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[hack]]></category>

		<category><![CDATA[networking]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<category><![CDATA[planner]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.28.php#anchor-3</guid>
		<description><![CDATA[<p>One of the things I want in a contact management system is a quick way
to find out who I haven't pinged in a while. The following code
filters currently-displayed contacts to show who I might want to get
back in touch with. Call it from a *BBDB* window and specify the date
(could be 2006.01.01 for annual, -7 for the last seven days, etc.).
This works incredibly well with the following hacks:</p>

<ul>
<li><a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-2">Emacs: Keep track of messages sent</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.06.03.php#anchor-5">BBDB pinging code</a></li>
</ul>

<p>I should write a small book about how to build a contact management
system with Emacs. ;) It's insanely powerful, you know.</p>

<pre class="example">
(require 'planner)
(require 'bbdb)
(defun sacha/bbdb-show-only-no-contact-since (date)
  "Show only people who haven't been pinged since DATE or at all."
  (interactive (list (planner-read-date)))
  (let ((records bbdb-records)
        new-records
        last-match
        omit
        notes)
    (while records
      ;; Find the latest date mentioned in the entry
      (setq notes (or (bbdb-record-notes (caar records)) ""))
      (setq last-match nil omit nil)
      (while (string-match
              "[0-9][0-9][0-9][0-9]\\.[0-9][0-9]\\.[0-9][0-9]"
              notes
              (or last-match 0))
        (unless (string> date (match-string 0 notes))
          (setq omit t)
          (setq last-match (length notes)))
        (setq last-match (match-end 0)))
      (unless (and last-match omit)
        (add-to-list 'new-records (caar records) t))
      (setq records (cdr records)))
    (bbdb-display-records new-records)))
</pre>

<p>One of the other things I'd like to smooth over is keeping track of
who owes whom e-mail... &#60;laugh&#62;</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a></p>
]]></description>
			<content:encoded><![CDATA[<p>One of the things I want in a contact management system is a quick way
to find out who I haven't pinged in a while. The following code
filters currently-displayed contacts to show who I might want to get
back in touch with. Call it from a *BBDB* window and specify the date
(could be 2006.01.01 for annual, -7 for the last seven days, etc.).
This works incredibly well with the following hacks:</p>

<ul>
<li><a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-2">Emacs: Keep track of messages sent</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.06.03.php#anchor-5">BBDB pinging code</a></li>
</ul>

<p>I should write a small book about how to build a contact management
system with Emacs. ;) It's insanely powerful, you know.</p>

<pre class="example">
(require 'planner)
(require 'bbdb)
(defun sacha/bbdb-show-only-no-contact-since (date)
  "Show only people who haven't been pinged since DATE or at all."
  (interactive (list (planner-read-date)))
  (let ((records bbdb-records)
        new-records
        last-match
        omit
        notes)
    (while records
      ;; Find the latest date mentioned in the entry
      (setq notes (or (bbdb-record-notes (caar records)) ""))
      (setq last-match nil omit nil)
      (while (string-match
              "[0-9][0-9][0-9][0-9]\\.[0-9][0-9]\\.[0-9][0-9]"
              notes
              (or last-match 0))
        (unless (string> date (match-string 0 notes))
          (setq omit t)
          (setq last-match (length notes)))
        (setq last-match (match-end 0)))
      (unless (and last-match omit)
        (add-to-list 'new-records (caar records) t))
      (setq records (cdr records)))
    (bbdb-display-records new-records)))
</pre>

<p>One of the other things I'd like to smooth over is keeping track of
who owes whom e-mail... &lt;laugh&gt;</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/elisp" rel="tag">elisp</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/28/emacs-show-only-people-whom-i-havent-pinged-since/feed/</wfw:commentRss>
		</item>
		<item>
		<title>More Emacs goodness: Refresh your memory when you e-mail using notes from BBDB</title>
		<link>http://sachachua.com/wp/2006/09/02/more-emacs-goodness-refresh-your-memory-when-you-e-mail-using-notes-from-bbdb/</link>
		<comments>http://sachachua.com/wp/2006/09/02/more-emacs-goodness-refresh-your-memory-when-you-e-mail-using-notes-from-bbdb/#comments</comments>
		<pubDate>Sat, 02 Sep 2006 19:58:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[crm]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[gnus]]></category>

		<category><![CDATA[networking]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.02.php#anchor-1</guid>
		<description><![CDATA[<p>Inspired by an e-mail-based customer relationship management system briefly described by <a href="http://blog.digitalketchup.net/">Daniel Charles</a> of <a href="http://www.digitalketchup.net">digital ketchup</a> at Shoeless Joe's last Friday, I decided to hack together a system that would allow me to see the notes from my contact database (aptly named the Big Brother Database, or BBDB) when I write e-mail using the Gnus mail client in Emacs.</p>

<p>The first thing I needed to build, of course, was something that
removed my notes from outgoing messages. People really don't need to
see the kinds of notes I keep on them. ;) Well, they're fairly
innocuous notes: how we met and what they're interested in, usually,
although sometimes I'll have notes on people's food preferences or
shoe sizes. I've recently started keeping track of the subjects of
e-mail I send them, too.</p>

<pre class="example">
(defun sacha/gnus-remove-notes ()
  "Remove everything from --- NOTES --- to the signature."
  (goto-char (point-min))
  (when (re-search-forward "^--- NOTES ---" nil t)
    (let ((start (match-beginning 0))
          (end (and (re-search-forward "^--- END NOTES ---") (match-end 0))))
      (delete-region start end))))
(add-hook 'message-send-hook 'sacha/gnus-remove-notes)
</pre>

<p>Then it was easy to write another function that composed individual
messages to all the people currently displayed in the BBDB buffer,
adding notes to each message.</p>

<pre class="example">
(defun sacha/gnus-send-message-to-all (subject)
  "Compose message to everyone, with notes."
  (interactive "MSubject: ")
  (let ((records bbdb-records))
    (while records
      (when (bbdb-record-net (caar records))
        (bbdb-send-mail (caar records) subject)
        (when (bbdb-record-notes (caar records))
          (save-excursion
            (insert "\n--- NOTES ---\n"
                    (bbdb-record-notes (caar records))
                    "\n--- END NOTES ---\n"))))
      (setq records (cdr records)))))
</pre>

<p>I use BBDB to display only the people I want to e-mail, then I call
M-x sacha/gnus-send-message-to-all and specify a message subject. This
creates a gazillion message buffers which I can then edit. If I feel
particularly paranoid, I can remove the notes section myself with C-c
C-z (message-kill-to-signature), but sacha/gnus-remove-notes does it
as long as it's in message-send-hook.</p>

<p>This code works particularly well with these other customizations:</p>

<ul>
<li><a href="http://sachachua.com/notebook/wiki/2006.07.20.php#anchor-1">Emacs BBDB magic: Greeting people with nicknames</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-2">Emacs: Keep track of messages sent</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.06.03.php#anchor-5">BBDB pinging code</a> - for adding datestamped notes to people's entries; can be rewritten without the use of Planner</li>
</ul>

<p>It supersedes <a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-4">More Emacs fun: Composing mail to everyone with notes</a>.</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>

<p><a href="../emacs/dotgnus.el">../emacs/dotgnus.el</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Inspired by an e-mail-based customer relationship management system briefly described by <a href="http://blog.digitalketchup.net/">Daniel Charles</a> of <a href="http://www.digitalketchup.net">digital ketchup</a> at Shoeless Joe's last Friday, I decided to hack together a system that would allow me to see the notes from my contact database (aptly named the Big Brother Database, or BBDB) when I write e-mail using the Gnus mail client in Emacs.</p>

<p>The first thing I needed to build, of course, was something that
removed my notes from outgoing messages. People really don't need to
see the kinds of notes I keep on them. ;) Well, they're fairly
innocuous notes: how we met and what they're interested in, usually,
although sometimes I'll have notes on people's food preferences or
shoe sizes. I've recently started keeping track of the subjects of
e-mail I send them, too.</p>

<pre class="example">
(defun sacha/gnus-remove-notes ()
  "Remove everything from --- NOTES --- to the signature."
  (goto-char (point-min))
  (when (re-search-forward "^--- NOTES ---" nil t)
    (let ((start (match-beginning 0))
          (end (and (re-search-forward "^--- END NOTES ---") (match-end 0))))
      (delete-region start end))))
(add-hook 'message-send-hook 'sacha/gnus-remove-notes)
</pre>

<p>Then it was easy to write another function that composed individual
messages to all the people currently displayed in the BBDB buffer,
adding notes to each message.</p>

<pre class="example">
(defun sacha/gnus-send-message-to-all (subject)
  "Compose message to everyone, with notes."
  (interactive "MSubject: ")
  (let ((records bbdb-records))
    (while records
      (when (bbdb-record-net (caar records))
        (bbdb-send-mail (caar records) subject)
        (when (bbdb-record-notes (caar records))
          (save-excursion
            (insert "\n--- NOTES ---\n"
                    (bbdb-record-notes (caar records))
                    "\n--- END NOTES ---\n"))))
      (setq records (cdr records)))))
</pre>

<p>I use BBDB to display only the people I want to e-mail, then I call
M-x sacha/gnus-send-message-to-all and specify a message subject. This
creates a gazillion message buffers which I can then edit. If I feel
particularly paranoid, I can remove the notes section myself with C-c
C-z (message-kill-to-signature), but sacha/gnus-remove-notes does it
as long as it's in message-send-hook.</p>

<p>This code works particularly well with these other customizations:</p>

<ul>
<li><a href="http://sachachua.com/notebook/wiki/2006.07.20.php#anchor-1">Emacs BBDB magic: Greeting people with nicknames</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-2">Emacs: Keep track of messages sent</a></li>
<li><a href="http://sachachua.com/notebook/wiki/2006.06.03.php#anchor-5">BBDB pinging code</a> - for adding datestamped notes to people's entries; can be rewritten without the use of Planner</li>
</ul>

<p>It supersedes <a href="http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-4">More Emacs fun: Composing mail to everyone with notes</a>.</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/crm" rel="tag">crm</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>

<p><a href="../emacs/dotgnus.el">../emacs/dotgnus.el</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/02/more-emacs-goodness-refresh-your-memory-when-you-e-mail-using-notes-from-bbdb/feed/</wfw:commentRss>
		</item>
		<item>
		<title>More Emacs fun: Composing mail to everyone with notes</title>
		<link>http://sachachua.com/wp/2006/09/01/more-emacs-fun-composing-mail-to-everyone-with-notes/</link>
		<comments>http://sachachua.com/wp/2006/09/01/more-emacs-fun-composing-mail-to-everyone-with-notes/#comments</comments>
		<pubDate>Fri, 01 Sep 2006 16:24:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[gnus]]></category>

		<category><![CDATA[hack]]></category>

		<category><![CDATA[lisp]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-4</guid>
		<description><![CDATA[<pre class="example">
(defun sacha/compose-mail-to-everyone (&#038;optional subject)
  (mapc (lambda (rec)
          (setq rec (car rec))
          (when (bbdb-record-net rec)
            (bbdb-send-mail rec subject)
            (save-excursion
              (message-goto-signature)
              (forward-line -2)
              (insert "\n---- NOTES ---\n" (bbdb-record-notes rec) "\n"))))
        bbdb-records))

(defun sacha/gnus-delete-notes ()
  (goto-char (point-min))
  (when (re-search-forward "^--- NOTES ---" nil t)
    (goto-char (match-beginning 0))
    (message-kill-to-signature)))
(add-hook 'message-send-hook 'sacha/gnus-delete-notes)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/lisp" rel="tag">lisp</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></description>
			<content:encoded><![CDATA[<pre class="example">
(defun sacha/compose-mail-to-everyone (&optional subject)
  (mapc (lambda (rec)
          (setq rec (car rec))
          (when (bbdb-record-net rec)
            (bbdb-send-mail rec subject)
            (save-excursion
              (message-goto-signature)
              (forward-line -2)
              (insert "\n---- NOTES ---\n" (bbdb-record-notes rec) "\n"))))
        bbdb-records))

(defun sacha/gnus-delete-notes ()
  (goto-char (point-min))
  (when (re-search-forward "^--- NOTES ---" nil t)
    (goto-char (match-beginning 0))
    (message-kill-to-signature)))
(add-hook 'message-send-hook 'sacha/gnus-delete-notes)
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/lisp" rel="tag">lisp</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/gnus" rel="tag">gnus</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/01/more-emacs-fun-composing-mail-to-everyone-with-notes/feed/</wfw:commentRss>
		</item>
		<item>
		<title>More Emacs coolness: List of contacts</title>
		<link>http://sachachua.com/wp/2006/09/01/more-emacs-coolness-list-of-contacts/</link>
		<comments>http://sachachua.com/wp/2006/09/01/more-emacs-coolness-list-of-contacts/#comments</comments>
		<pubDate>Fri, 01 Sep 2006 16:14:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[hack]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<category><![CDATA[planner]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-3</guid>
		<description><![CDATA[<p>This bit of Emacs Lisp code produces a Planner-ready list of the contacts displayed in the BBDB window.</p>

<pre class="example">
(defun sacha/planner-bbdb-annotation-from-bbdb (&#038;optional record)
  "If called from a bbdb buffer, return an annotation.
Suitable for use in `planner-annotation-functions'."
  (when (or record (eq major-mode 'bbdb-mode))
    (setq record (if record (car record) (bbdb-current-record)))
    (or (bbdb-record-getprop record 'plan)
        ;; From a BBDB entry with a plan page; use that. Yay!
        (planner-make-link
         (concat "bbdb://"
                 (planner-replace-regexp-in-string
                    " " "." (bbdb-record-name record)))
         (bbdb-record-name record)))))
(defalias 'planner-bbdb-annotation-from-bbdb 'sacha/planner-bbdb-annotation-from-bbdb)

(defun sacha/yank-planner-bbdb-list ()
  "Copy the list of people displayed in the buffer."
  (interactive)
  (kill-new
   (mapconcat 'sacha/planner-bbdb-annotation-from-bbdb
              bbdb-records
              ", "))
        (sacha/planner-bbdb-annotation-from-bbdb rec))
</pre>

<p>It allows me to say, for example, that I met 23 people yesterday:
Bruce, <a href="http://blog.digitalketchup.net/">Daniel Charles</a>, Shane D'Costa, Emily, <a href="http://www.mistergadgets.ca">Greg A. Fitz</a>, Clara Fong, <a href="http://www.radiantcore.com">Jay Goldman</a>, Harvey, Kai Fai Ho, Iris, KC, Charles McCulloch, <a href="http://www.scimatic.com">Jamie McQuay</a>, Joshua Meles, Naomi, <a href="http://www.nonlinear.ca">Helen Overland</a>, <a href="http://www.aiim.com">Wayne Penney</a>, <a href="http://www.simonrowland.com">Simon Rowland</a>, San, Colin Smillie, Solomon, Le Quan Truong, Perry Wong</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></description>
			<content:encoded><![CDATA[<p>This bit of Emacs Lisp code produces a Planner-ready list of the contacts displayed in the BBDB window.</p>

<pre class="example">
(defun sacha/planner-bbdb-annotation-from-bbdb (&optional record)
  "If called from a bbdb buffer, return an annotation.
Suitable for use in `planner-annotation-functions'."
  (when (or record (eq major-mode 'bbdb-mode))
    (setq record (if record (car record) (bbdb-current-record)))
    (or (bbdb-record-getprop record 'plan)
        ;; From a BBDB entry with a plan page; use that. Yay!
        (planner-make-link
         (concat "bbdb://"
                 (planner-replace-regexp-in-string
                    " " "." (bbdb-record-name record)))
         (bbdb-record-name record)))))
(defalias 'planner-bbdb-annotation-from-bbdb 'sacha/planner-bbdb-annotation-from-bbdb)

(defun sacha/yank-planner-bbdb-list ()
  "Copy the list of people displayed in the buffer."
  (interactive)
  (kill-new
   (mapconcat 'sacha/planner-bbdb-annotation-from-bbdb
              bbdb-records
              ", "))
        (sacha/planner-bbdb-annotation-from-bbdb rec))
</pre>

<p>It allows me to say, for example, that I met 23 people yesterday:
Bruce, <a href="http://blog.digitalketchup.net/">Daniel Charles</a>, Shane D'Costa, Emily, <a href="http://www.mistergadgets.ca">Greg A. Fitz</a>, Clara Fong, <a href="http://www.radiantcore.com">Jay Goldman</a>, Harvey, Kai Fai Ho, Iris, KC, Charles McCulloch, <a href="http://www.scimatic.com">Jamie McQuay</a>, Joshua Meles, Naomi, <a href="http://www.nonlinear.ca">Helen Overland</a>, <a href="http://www.aiim.com">Wayne Penney</a>, <a href="http://www.simonrowland.com">Simon Rowland</a>, San, Colin Smillie, Solomon, Le Quan Truong, Perry Wong</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a>, <a href="http://www.technorati.com/tag/planner" rel="tag">planner</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/01/more-emacs-coolness-list-of-contacts/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Emacs: Keep track of messages sent</title>
		<link>http://sachachua.com/wp/2006/09/01/emacs-keep-track-of-messages-sent/</link>
		<comments>http://sachachua.com/wp/2006/09/01/emacs-keep-track-of-messages-sent/#comments</comments>
		<pubDate>Fri, 01 Sep 2006 16:06:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[hack]]></category>

		<category><![CDATA[lisp]]></category>

		<category><![CDATA[pimpmyemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.09.01.php#anchor-2</guid>
		<description><![CDATA[<p>Because a Big Brother Database of my contacts isn't complete if I
don't keep track of what e-mail I sent them and when I sent it, this
bit of Emacs Lisp code adds Gnus subjects to the BBDB records of the
people to whom I sent e-mail.</p>

<pre class="example">
(defun sacha/gnus-add-subject-to-bbdb-record ()
  "Add datestamped subject note for each person this message has been sent to."
  (let* ((subject (concat (planner-today)
                          ": E-mail: " (message-fetch-field "Subject") "\n"))
         (bbdb-get-addresses-headers
          (list (assoc 'recipients bbdb-get-addresses-headers)))
         records)
    (setq records
          (bbdb-update-records
           (bbdb-get-addresses nil gnus-ignored-from-addresses 'gnus-fetch-field)
           nil nil))
    (mapc (lambda (rec)
            (bbdb-record-set-notes rec
                                   (concat subject
                                           (bbdb-record-notes rec))))
            records)))
(add-hook 'message-send-hook 'sacha/gnus-add-subject-to-bbdb-record)
</pre>

<p>It should be really easy to set up Gnus to expand some kind of
!followup macro into a TODO item in my planner and an "I hope to hear
from you by ....". Ridiculously easy with Emacs Lisp and an insanely
customizable editor, but I might not have enough battery life. I've
got 28 minutes, and then I'm off PC for a while.</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/lisp" rel="tag">lisp</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a></p>

<p><a href="/mnt/media/sacha/notebook/emacs/dotgnus.el">/mnt/media/sacha/notebook/emacs/dotgnus.el</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Because a Big Brother Database of my contacts isn't complete if I
don't keep track of what e-mail I sent them and when I sent it, this
bit of Emacs Lisp code adds Gnus subjects to the BBDB records of the
people to whom I sent e-mail.</p>

<pre class="example">
(defun sacha/gnus-add-subject-to-bbdb-record ()
  "Add datestamped subject note for each person this message has been sent to."
  (let* ((subject (concat (planner-today)
                          ": E-mail: " (message-fetch-field "Subject") "\n"))
         (bbdb-get-addresses-headers
          (list (assoc 'recipients bbdb-get-addresses-headers)))
         records)
    (setq records
          (bbdb-update-records
           (bbdb-get-addresses nil gnus-ignored-from-addresses 'gnus-fetch-field)
           nil nil))
    (mapc (lambda (rec)
            (bbdb-record-set-notes rec
                                   (concat subject
                                           (bbdb-record-notes rec))))
            records)))
(add-hook 'message-send-hook 'sacha/gnus-add-subject-to-bbdb-record)
</pre>

<p>It should be really easy to set up Gnus to expand some kind of
!followup macro into a TODO item in my planner and an "I hope to hear
from you by ....". Ridiculously easy with Emacs Lisp and an insanely
customizable editor, but I might not have enough battery life. I've
got 28 minutes, and then I'm off PC for a while.</p>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/lisp" rel="tag">lisp</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/pimpmyemacs" rel="tag">pimpmyemacs</a>, <a href="http://www.technorati.com/tag/hack" rel="tag">hack</a></p>

<p><a href="/mnt/media/sacha/notebook/emacs/dotgnus.el">/mnt/media/sacha/notebook/emacs/dotgnus.el</a></p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/09/01/emacs-keep-track-of-messages-sent/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BBDB pinging code</title>
		<link>http://sachachua.com/wp/2006/06/03/bbdb-pinging-code/</link>
		<comments>http://sachachua.com/wp/2006/06/03/bbdb-pinging-code/#comments</comments>
		<pubDate>Sat, 03 Jun 2006 19:14:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
		
		<category><![CDATA[bbdb]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[networking]]></category>

		<guid isPermaLink="false">http://sachachua.com/notebook/wiki/2006.06.03.php#anchor-5</guid>
		<description><![CDATA[<p>I love tweaking Emacs to fit the way I work. Here's some code to make it easier to keep track of pinged people.</p>

<pre class="example">
(defun sacha/bbdb-ping-bbdb-record (bbdb-record text &#038;optional date regrind)
  "Adds a note for today to the current BBDB record.
Call with a prefix to specify date."
  (interactive (list (bbdb-current-record t)
                     (read-string "Notes: ")
                     (if current-prefix-arg (planner-read-date) (planner-today))
                     t))
  (bbdb-record-set-notes bbdb-record (concat date ": " text "\n" (bbdb-record-notes bbdb-record)))
  (if regrind
      (save-excursion
        (set-buffer bbdb-buffer-name)
        (bbdb-redisplay-one-record bbdb-record)))
  nil)

(defun sacha/bbdb-gnus-ping (text)
  "Add a ping for authors/recipients of this message.
Call with a prefix to specify a manual note."
  (interactive (list (if current-prefix-arg (read-string "Notes: "))))
  (let* ((from-me-p
          (string-match gnus-ignored-from-addresses
                        (message-fetch-field "From")))
         (bbdb-get-only-first-address-p nil)
         (bbdb-get-addresses-headers
          (list (assoc (if from-me-p 'recipients 'authors) bbdb-get-addresses-headers)))
         (bbdb/gnus-update-records-mode 'annotating)
         (bbdb-message-cache nil)
         (bbdb-user-mail-names nil)
         (gnus-ignored-from-addresses nil)
         records)
    (setq records (bbdb/gnus-update-records t))
    (if records
        (bbdb-display-records records)
      (bbdb-undisplay-records))
    (while records
      (sacha/bbdb-ping-bbdb-record
       (car records)
       (concat
        (if from-me-p "-> " "<- ")
        (or text (message-fetch-field "Subject")))
       (planner-date-to-filename
        (date-to-time (message-fetch-field "Date"))))
      (setq records (cdr records)))
    (setq records (bbdb/gnus-update-records t))
    (if records
        (bbdb-display-records records)
      (bbdb-undisplay-records))))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a></p>

<p><a href="../emacs/bbdb-config.el">/home/sacha/notebook/emacs/bbdb-config.el</a></p>

<p>Random Japanese sentence: ÃƒÂ§Ã‚ÂŒÃ‚Â«ÃƒÂ£Ã‚ÂÃ‚ÂŒÃƒÂ£Ã‚Â‚Ã‚Â½ÃƒÂ£Ã‚ÂƒÃ‚Â•ÃƒÂ£Ã‚Â‚Ã‚Â¡ÃƒÂ£Ã‚ÂÃ‚Â®ÃƒÂ¤Ã‚Â¸Ã‚ÂŠÃƒÂ£Ã‚ÂÃ‚Â«ÃƒÂ¥Ã‚Â¯Ã‚ÂÃƒÂ£Ã‚ÂÃ‚Â¦ÃƒÂ£Ã‚ÂÃ‚Â„ÃƒÂ£Ã‚Â‚Ã‚Â‹ÃƒÂ£Ã‚Â€Ã‚Â‚	A cat is lying on the sofa.</p>
]]></description>
			<content:encoded><![CDATA[<p>I love tweaking Emacs to fit the way I work. Here's some code to make it easier to keep track of pinged people.</p>

<pre class="example">
(defun sacha/bbdb-ping-bbdb-record (bbdb-record text &optional date regrind)
  "Adds a note for today to the current BBDB record.
Call with a prefix to specify date."
  (interactive (list (bbdb-current-record t)
                     (read-string "Notes: ")
                     (if current-prefix-arg (planner-read-date) (planner-today))
                     t))
  (bbdb-record-set-notes bbdb-record (concat date ": " text "\n" (bbdb-record-notes bbdb-record)))
  (if regrind
      (save-excursion
        (set-buffer bbdb-buffer-name)
        (bbdb-redisplay-one-record bbdb-record)))
  nil)

(defun sacha/bbdb-gnus-ping (text)
  "Add a ping for authors/recipients of this message.
Call with a prefix to specify a manual note."
  (interactive (list (if current-prefix-arg (read-string "Notes: "))))
  (let* ((from-me-p
          (string-match gnus-ignored-from-addresses
                        (message-fetch-field "From")))
         (bbdb-get-only-first-address-p nil)
         (bbdb-get-addresses-headers
          (list (assoc (if from-me-p 'recipients 'authors) bbdb-get-addresses-headers)))
         (bbdb/gnus-update-records-mode 'annotating)
         (bbdb-message-cache nil)
         (bbdb-user-mail-names nil)
         (gnus-ignored-from-addresses nil)
         records)
    (setq records (bbdb/gnus-update-records t))
    (if records
        (bbdb-display-records records)
      (bbdb-undisplay-records))
    (while records
      (sacha/bbdb-ping-bbdb-record
       (car records)
       (concat
        (if from-me-p "-> " "<- ")
        (or text (message-fetch-field "Subject")))
       (planner-date-to-filename
        (date-to-time (message-fetch-field "Date"))))
      (setq records (cdr records)))
    (setq records (bbdb/gnus-update-records t))
    (if records
        (bbdb-display-records records)
      (bbdb-undisplay-records))))
</pre>

<p>On Technorati: <a href="http://www.technorati.com/tag/emacs" rel="tag">emacs</a>, <a href="http://www.technorati.com/tag/bbdb" rel="tag">bbdb</a>, <a href="http://www.technorati.com/tag/networking" rel="tag">networking</a></p>

<p><a href="../emacs/bbdb-config.el">/home/sacha/notebook/emacs/bbdb-config.el</a></p>

<p>Random Japanese sentence: ÃƒÂ§Ã‚ÂŒÃ‚Â«ÃƒÂ£Ã‚ÂÃ‚ÂŒÃƒÂ£Ã‚Â‚Ã‚Â½ÃƒÂ£Ã‚ÂƒÃ‚Â•ÃƒÂ£Ã‚Â‚Ã‚Â¡ÃƒÂ£Ã‚ÂÃ‚Â®ÃƒÂ¤Ã‚Â¸Ã‚ÂŠÃƒÂ£Ã‚ÂÃ‚Â«ÃƒÂ¥Ã‚Â¯Ã‚ÂÃƒÂ£Ã‚ÂÃ‚Â¦ÃƒÂ£Ã‚ÂÃ‚Â„ÃƒÂ£Ã‚Â‚Ã‚Â‹ÃƒÂ£Ã‚Â€Ã‚Â‚	A cat is lying on the sofa.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/wp/2006/06/03/bbdb-pinging-code/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
