This repository has been archived by the owner on Mar 4, 2018. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow for broader range of phone numbers to llok for; define our own “first found” phone order (excluding fax numbers); include nickname into searched name fields (fixes #4); refine queries incrementally against all fields, word by word.
- Loading branch information
Showing
1 changed file
with
81 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,73 +1,99 @@ | |||
#!/usr/bin/env bash | #!/usr/bin/env bash | ||
scriptdir="${0%/*}" | |||
export LC_CTYPE='en_US.UTF-8' # UTF-8 case awareness | export LC_CTYPE='en_US.UTF-8' # UTF-8 case awareness | ||
|
|
||
# normalize Unicode representation of characters with diacritics | |||
not_ascii='[^ -~]' | |||
[[ "$1" =~ $not_ascii ]] && query="$(echo "$1" | iconv -s -f 'UTF-8-Mac' -t 'UTF-8')" || query="$1" | |||
scriptdir="${BASH_SOURCE[0]%/*}" | scriptdir="${BASH_SOURCE[0]%/*}" | ||
source "$scriptdir"/lib/strings.sh | |||
|
|
||
# assign arguments | # Parse arguments | ||
query=($query) | desc=''; icon='' | ||
desc="${2:-main phone number}" | case "$1" in | ||
code="${3:-%p}" | --found) | ||
icon="${4:-vCard.png}" | code=$':%n\t%u\t:%nn\t:%c\t%hp\t%wp\t%mp\t%Mp\t%op\t%pp' | ||
desc='first found number' ;; | |||
--home) | |||
code=$':%n\t%u\t:%nn\t:%c\t%hp' ;; | |||
--work) | |||
code=$':%n\t%u\t:%nn\t:%c\t%wp' ;; | |||
--mobile) | |||
code=$':%n\t%u\t:%nn\t:%c\t%mp' ;; | |||
--main) | |||
code=$':%n\t%u\t:%nn\t:%c\t%Mp' ;; | |||
--other) | |||
code=$':%n\t%u\t:%nn\t:%c\t%op' ;; | |||
--pager) | |||
code=$':%n\t%u\t:%nn\t:%c\t%pp' | |||
desc='pager number' ;; | |||
--fax) | |||
code=$':%n\t%u\t:%nn\t:%c\t%fp' | |||
desc='fax number' ;; | |||
*) | |||
exit 1 ;; | |||
esac | |||
|
|
||
# Searches beginning with a lower case letter are case insensitive | desc="${desc:-${1#--} phone number}" | ||
ucase='[[:upper:]]' | icon="${icon:-vCard.png}" | ||
[[ ${query[0]:0:1} =~ $ucase ]] && shopt -u nocasematch || shopt -s nocasematch | |||
|
|
||
blank='^[[:space:]]*$' | if ! is_blank $2; then | ||
if [[ ! "${query[0]}" =~ $blank ]]; then | # Normalize Unicode representation of characters with diacritics | ||
# start XML output | is_7bit "${*:2}" && query=(${*:2}) || query=($(utf8_normalize "${*:2}")) | ||
|
|||
# Start XML output | |||
source "$scriptdir"/lib/feedback.sh | source "$scriptdir"/lib/feedback.sh | ||
feedback --start | feedback --start | ||
|
|||
# get Contacts data for query | # Get contact data matching query | ||
names=(); numbers=(); uids=() | prev_title=''; prev_number=''; matches=0 | ||
while read -r || [[ -n "$REPLY" ]]; do | while read -r || [[ -n "$REPLY" ]]; do | ||
# split names and numbers on UID (as a makeshift record separator) | # Catch contacts’ stdout message on 0 matches | ||
# filtering out non-matching names (as contacts itself only accepts a single, | [[ $REPLY == 'error: no one found' ]] && break | ||
# unquoted word as its query) and results without a phone number. |
|
||
uid_regex='[A-Z0-9]{8}(-[A-Z0-9]{4}){3}-[A-Z0-9]{12}:ABPerson' | # Parse contact output line into record items | ||
has_phone="(.*) ($uid_regex) (.*) $uid_regex (.+)" | IFS=$'\t' record=($REPLY) | ||
has_query=".*${query[*]}.*" | name="${record[0]#:}" | ||
if [[ $REPLY =~ $has_phone ]]; then | uid="${record[1]}" | ||
[[ ${BASH_REMATCH[1]} != ' ' ]] && name="${BASH_REMATCH[1]}" || unset name | nick="${record[2]#:}" | ||
uid="${BASH_REMATCH[2]}" | [[ ${record[3]#:} != "$name" ]] && company="${record[3]#:}" || company='' | ||
company="${BASH_REMATCH[4]}" | number="${record[4]}" | ||
number="${BASH_REMATCH[6]}" | title='' | ||
# if we match on name, add company name (if any), and vice versa |
|
||
if [[ $name =~ $has_query ]]; then names+=("$name${company:+ ($company)}") | # Only keep records with a phone number | ||
elif [[ $company =~ $has_query ]]; then names+=("$company${name:+ – $name}") | if [[ -n "$number" ]]; then | ||
else continue # skip when no full match is found | # Word incremental search; first match defines display format | ||
fi | for q in "${query[@]}"; do | ||
numbers+=("$number") | # Query words starting with an upper case letter are case sensitive | ||
uids+=("$uid") | is_lower "$q" && shopt -s nocasematch || shopt -u nocasematch | ||
if [[ $name =~ "$q" ]]; then | |||
[[ -z "$title" ]] && title="$name${company:+ ($company)}" | |||
elif [[ $nick =~ "$q" ]]; then | |||
[[ -z "$title" ]] && title="“${nick}”${name:+ $name}${company:+ ($company)}" | |||
elif [[ $company =~ "$q" ]]; then | |||
[[ -z "$title" ]] && title="$company${name:+ – $name}" | |||
else # next record | |||
continue 2 | |||
fi | |||
done | |||
|
|||
# Skip duplicate records | |||
[[ $title == "$prev_title" && $number == "$prev_number" ]] && continue | |||
|
|||
# Output XML feedback item | |||
text="Call $desc: $number" | |||
feedback "$title" --subtitle="$text" --uid="$uid" --action="$number" --valid --icon="$icon" | |||
|
|||
# Store control var values | |||
prev_title="$title"; prev_number="$number"; let matches++ | |||
fi | fi | ||
done < <("$scriptdir"/contacts -SHs -f "%fn %ln %u %c %u $code" ${query[0]}) | done < <("$scriptdir"/contacts -SH -f "$code" ${query[0]} | sort) | ||
|
|
||
# create XML items from matching contact items | # Create fallback item if no matching records are found | ||
# (check for error message as sends these to stdout) | if (( $matches < 1 )); then | ||
if (( ${#names[@]} > 0 )) && [[ ${names[0]} != 'error: no one found' ]]; then | title='No matching contacts found' | ||
for (( i=0; i < ${#names[@]}; i++ )); do | |||
name="${names[$i]}" | |||
number="${numbers[$i]}" | |||
uid="${uids[$i]}" | |||
# deduplicate items | |||
(( i > 0 )) && [[ $name == "${names[$(($i-1))]}" && $number == "${numbers[$(($i-1))]}" ]] && continue | |||
# XML output | |||
text="Dial $desc: $number" | |||
feedback "$name" --subtitle="$text" --uid="$uid" --action="$number" --valid --icon="$icon" | |||
done | |||
else # Create fallback XML item | |||
title="No matching contacts found" | |||
text="Action this item to run Alfred default searches for “${query[*]}”." | text="Action this item to run Alfred default searches for “${query[*]}”." | ||
arg="alfredsearch:${query[*]}" | arg="alfredsearch:${query[*]}" | ||
icon='/Applications/Contacts.app' | icon='/Applications/Contacts.app' | ||
feedback "$title" --subtitle="$text" --uid='noresults' --action="$arg" --icon="$icon" --fileicon | feedback "$title" --subtitle="$text" --uid='noresults' --action="$arg" --icon="$icon" --fileicon | ||
fi | fi | ||
|
|||
# end XML output | # End XML output | ||
feedback --end | feedback --end | ||
fi | fi |