vmail 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/NOTES CHANGED
@@ -481,13 +481,20 @@ DONE
481
481
  - can specify a directory, then all the items in the directory get
482
482
  attached and sent
483
483
  - extract and append text of all selected messages into file (append)
484
-
485
- next:
484
+ - ,s s confusion? Star vs search
485
+ - ,* is star now
486
+ - archive function (shortcut for move to all mail)
487
+ - remember searches?
488
+ - perma-delete from Trash
489
+ - put move to> and copy to> prompts
490
+ - create mailboxes if they don't exist on move or copy
486
491
  - tweak contacts list script (plus instruction for how to use)
492
+ - .vmailrc is a yaml file, first in current dir, then check home, can
493
+ also use command line opts
494
+ - fix tests
495
+
496
+ NEXT:
487
497
  - help doc: just return readme file, or a vim version of it (vim filetype later)
488
- - follow mysql and use -u and -p flags on startup of server?
489
- - omitting -p flag forces prompt
490
- - ,s s confusion? Star vs search
491
498
  - note dependence on lynx in README
492
499
 
493
500
  later:
@@ -495,8 +502,6 @@ later:
495
502
  - mvim redrawstatus line bug
496
503
  http://vim.1045645.n5.nabble.com/Redrawing-bug-in-MacVim-Command-T-since-commit-ba44868-td3248742.html
497
504
  - allow one daemon, multiple clients (select mailbox?)
498
- - remember searches?
499
- - archive function (shortcut for move to all mail)
500
505
  - crate to package app as a OS X app?
501
506
  http://rubyconf2008.confreaks.com/crate-packaging-your-ruby-application.html
502
507
  - need to bundle macvim
@@ -523,7 +528,9 @@ later:
523
528
  - enhance contacts auto fix with more advanced vim script
524
529
  - create contacts database on first startup
525
530
 
526
-
531
+ won't do
532
+ - follow mysql and use -u and -p flags on startup of server?
533
+ - omitting -p flag forces prompt
527
534
 
528
535
  ------------------------------------------------------------------------
529
536
 
@@ -536,4 +543,8 @@ imap.search ["HEADER", "MESSAGE-ID", "<757FC46C-9394-494E-91B4-B051F48419DA@prx.
536
543
 
537
544
  % is filename
538
545
 
546
+ " TODO the range doesn't quite work as expect, need <line1> <line2>
547
+ " trying to make user defined commands that work from : prompt
548
+ " command -buffer -range VmailDelete call s:toggle_star("Deleted")
549
+ " command -buffer -range VmailStar call s:toggle_star("Flagged")
539
550
 
data/bin/vmail CHANGED
@@ -4,7 +4,7 @@ begin
4
4
  require 'vmail'
5
5
  rescue LoadError
6
6
  require 'rubygems'
7
- require 'gmail'
7
+ require 'vmail'
8
8
  end
9
9
 
10
10
  Vmail.start
@@ -1,3 +1,4 @@
1
+ require 'vmail/options'
1
2
  require 'vmail/imap_client'
2
3
 
3
4
  module Vmail
@@ -6,13 +7,23 @@ module Vmail
6
7
  def start
7
8
  vim = ENV['VMAIL_VIM'] || 'vim'
8
9
 
9
- config = YAML::load(File.read(File.expand_path("~/gmail.yml")))
10
+ opts = Vmail::Options.new(ARGV)
11
+ opts.config
12
+
13
+ config = opts.config
14
+ contacts_file = opts.contacts_file
15
+
10
16
  logfile = (vim == 'mvim') ? STDERR : 'vmail.log'
11
17
  config.merge! 'logfile' => logfile
12
18
 
13
- puts "starting vmail imap client with config #{config}"
19
+ puts "starting vmail imap client for #{config['username']}"
14
20
 
15
- drb_uri = Vmail::ImapClient.daemon config
21
+ drb_uri = begin
22
+ Vmail::ImapClient.daemon config
23
+ rescue
24
+ puts "Failure:", $!
25
+ exit(1)
26
+ end
16
27
 
17
28
  server = DRbObject.new_with_uri drb_uri
18
29
 
@@ -34,7 +45,7 @@ module Vmail
34
45
 
35
46
  # invoke vim
36
47
  vimscript = File.expand_path("../vmail.vim", __FILE__)
37
- vim_command = "DRB_URI='#{drb_uri}' VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY=#{String.shellescape(query.join(' '))} #{vim} -S #{vimscript} #{buffer_file}"
48
+ vim_command = "DRB_URI='#{drb_uri}' VMAIL_CONTACTS_FILE=#{contacts_file} VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY=#{String.shellescape(query.join(' '))} #{vim} -S #{vimscript} #{buffer_file}"
38
49
  puts vim_command
39
50
  system(vim_command)
40
51
 
@@ -16,6 +16,7 @@ let s:more_messages_command = s:client_script . "more_messages "
16
16
  let s:flag_command = s:client_script . "flag "
17
17
  let s:append_to_file_command = s:client_script . "append_to_file "
18
18
  let s:move_to_command = s:client_script . "move_to "
19
+ let s:copy_to_command = s:client_script . "copy_to "
19
20
  let s:new_message_template_command = s:client_script . "new_message_template "
20
21
  let s:reply_template_command = s:client_script . "reply_template "
21
22
  let s:forward_template_command = s:client_script . "forward_template "
@@ -24,7 +25,6 @@ let s:save_draft_command = s:client_script . "save_draft "
24
25
  let s:save_attachments_command = s:client_script . "save_attachments "
25
26
  let s:open_html_command = s:client_script . "open_html_part "
26
27
  let s:message_bufname = "MessageWindow"
27
- let s:list_bufname = "MessageListWindow"
28
28
 
29
29
  function! VmailStatusLine()
30
30
  return "%<%f\ " . s:mailbox . " " . s:query . "%r%=%-14.(%l,%c%V%)\ %P"
@@ -256,8 +256,33 @@ func! s:delete_messages(flag) range
256
256
  if len(uids) > 2
257
257
  call feedkeys("\<cr>")
258
258
  endif
259
+ redraw
260
+ echo len(uids) . " message" . (len(uids) == 1 ? '' : 's') . " marked " . a:flag
261
+ endfunc
262
+
263
+ func! s:archive_messages() range
264
+ let lnum = a:firstline
265
+ let n = 0
266
+ let uids = []
267
+ while lnum <= a:lastline
268
+ let line = getline(lnum)
269
+ let message_uid = matchstr(line, '^\d\+')
270
+ call add(uids, message_uid)
271
+ let lnum = lnum + 1
272
+ endwhile
273
+ let uid_set = join(uids, ",")
274
+ let command = s:move_to_command . uid_set . ' ' . shellescape("[Gmail]/All Mail")
275
+ echo "archiving message" . (len(uids) == 1 ? '' : 's')
276
+ let res = system(command)
277
+ setlocal modifiable
278
+ exec a:firstline . "," . a:lastline . "delete"
279
+ setlocal nomodifiable
280
+ write
281
+ redraw
282
+ echo len(uids) . " message" . (len(uids) == 1 ? '' : 's') . " archived"
259
283
  endfunc
260
284
 
285
+
261
286
  " --------------------------------------------------------------------------------
262
287
  " append text bodies of a set of messages to a file
263
288
  func! s:append_messages_to_file() range
@@ -281,7 +306,8 @@ endfunc
281
306
 
282
307
  " --------------------------------------------------------------------------------
283
308
  " move to another mailbox
284
- function! s:move_to_mailbox() range
309
+ function! s:move_to_mailbox(copy) range
310
+ let s:copy_to_mailbox = a:copy
285
311
  let lnum = a:firstline
286
312
  let n = 0
287
313
  let uids = []
@@ -301,31 +327,38 @@ function! s:move_to_mailbox() range
301
327
  setlocal noswapfile
302
328
  setlocal modifiable
303
329
  resize 1
330
+ let prompt = "select mailbox to " . (a:copy ? 'copy' : 'move') . " to: "
331
+ call setline(1, prompt)
332
+ normal $
304
333
  inoremap <silent> <buffer> <cr> <Esc>:call <SID>complete_move_to_mailbox()<CR>
305
334
  inoremap <silent> <buffer> <esc> <Esc>:q<cr>
306
335
  set completefunc=CompleteMoveMailbox
307
336
  " c-p clears the line
308
337
  let s:firstline = a:firstline
309
338
  let s:lastline = a:lastline
310
- call feedkeys("i\<c-x>\<c-u>\<c-p>", 't')
339
+ call feedkeys("a\<c-x>\<c-u>\<c-p>", 't')
311
340
  " save these in script scope to delete the lines when move completes
312
341
  endfunction
313
342
 
314
- " Open command window to choose a mailbox to move a message to.
315
- " Very similar to mailbox_window() function
316
343
  function! s:complete_move_to_mailbox()
317
- let mailbox = getline(line('.'))
344
+ let mailbox = get(split(getline(line('.')), ": "), 1)
318
345
  close
319
- " check if mailbox is a real mailbox
320
- if (index(s:mailboxes, mailbox) == -1)
321
- return
346
+ if s:copy_to_mailbox
347
+ let command = s:copy_to_command . s:uid_set . ' ' . shellescape(mailbox)
348
+ else
349
+ let command = s:move_to_command . s:uid_set . ' ' . shellescape(mailbox)
322
350
  endif
323
- let command = s:move_to_command . s:uid_set . ' ' . shellescape(mailbox)
324
- echo command
351
+ redraw
352
+ echo "moving uids ". s:uid_set . " to mailbox " . mailbox
325
353
  let res = system(command)
326
354
  setlocal modifiable
327
- exec s:firstline . "," . s:lastline . "delete"
355
+ if !s:copy_to_mailbox
356
+ exec s:firstline . "," . s:lastline . "delete"
357
+ end
328
358
  setlocal nomodifiable
359
+ write
360
+ redraw
361
+ echo "done"
329
362
  endfunction
330
363
 
331
364
  function! CompleteMoveMailbox(findstart, base)
@@ -334,7 +367,12 @@ function! CompleteMoveMailbox(findstart, base)
334
367
  endif
335
368
  if a:findstart
336
369
  " locate the start of the word
337
- return 0
370
+ let line = getline('.')
371
+ let start = col('.') - 1
372
+ while start > 0 && line[start - 1] =~ '\a'
373
+ let start -= 1
374
+ endwhile
375
+ return start
338
376
  else
339
377
  " find months matching with "a:base"
340
378
  let res = []
@@ -359,13 +397,37 @@ function! s:get_mailbox_list()
359
397
  let s:mailboxes = split(res, "\n", '')
360
398
  endfunction
361
399
 
400
+ " -------------------------------------------------------------------------------
401
+ " select mailbox
402
+
403
+ function! s:mailbox_window()
404
+ call s:get_mailbox_list()
405
+ topleft split MailboxSelect
406
+ setlocal buftype=nofile
407
+ setlocal noswapfile
408
+ setlocal modifiable
409
+ resize 1
410
+ inoremap <silent> <buffer> <cr> <Esc>:call <SID>select_mailbox()<CR>
411
+ inoremap <silent> <buffer> <esc> <Esc>:q<cr>
412
+ set completefunc=CompleteMailbox
413
+ " c-p clears the line
414
+ call setline(1, "select mailbox to switch to: ")
415
+ normal $
416
+ call feedkeys("a\<c-x>\<c-u>\<c-p>", 't')
417
+ endfunction
418
+
362
419
  function! CompleteMailbox(findstart, base)
363
420
  if !exists("s:mailboxes")
364
421
  call s:get_mailbox_list()
365
422
  endif
366
423
  if a:findstart
367
424
  " locate the start of the word
368
- return 0
425
+ let line = getline('.')
426
+ let start = col('.') - 1
427
+ while start > 0 && line[start - 1] =~ '\a'
428
+ let start -= 1
429
+ endwhile
430
+ return start
369
431
  else
370
432
  " find mailboxes matching with "a:base"
371
433
  let res = []
@@ -378,34 +440,11 @@ function! CompleteMailbox(findstart, base)
378
440
  endif
379
441
  endfun
380
442
 
381
- " -------------------------------------------------------------------------------
382
- " select mailbox
383
-
384
- function! s:mailbox_window()
385
- if !exists("s:mailboxes")
386
- call s:get_mailbox_list()
387
- endif
388
- topleft split MailboxSelect
389
- setlocal buftype=nofile
390
- setlocal noswapfile
391
- setlocal modifiable
392
- resize 1
393
- inoremap <silent> <buffer> <cr> <Esc>:call <SID>select_mailbox()<CR>
394
- inoremap <silent> <buffer> <esc> <Esc>:q<cr>
395
- set completefunc=CompleteMailbox
396
- " c-p clears the line
397
- call feedkeys("i\<c-x>\<c-u>\<c-p>", 't')
398
- endfunction
399
-
400
443
  function! s:select_mailbox()
401
- let mailbox = getline(line('.'))
444
+ let mailbox = get(split(getline(line('.')), ": "), 1)
402
445
  close
403
446
  call s:focus_message_window()
404
447
  close
405
- " check if mailbox is a real mailbox
406
- if (index(s:mailboxes, mailbox) == -1)
407
- return
408
- endif
409
448
  let s:mailbox = mailbox
410
449
  let command = s:select_mailbox_command . shellescape(s:mailbox)
411
450
  call system(command)
@@ -421,6 +460,7 @@ function! s:select_mailbox()
421
460
  execute "normal Gdd\<c-y>"
422
461
  normal G
423
462
  setlocal nomodifiable
463
+ write
424
464
  normal z.
425
465
  endfunction
426
466
 
@@ -460,6 +500,7 @@ function! s:do_search()
460
500
  execute "normal Gdd\<c-y>"
461
501
  normal z.
462
502
  setlocal nomodifiable
503
+ write
463
504
  endfunction
464
505
 
465
506
  function! s:more_messages()
@@ -519,6 +560,7 @@ func! s:open_compose_window(command)
519
560
  close!
520
561
  1,$delete
521
562
  put! =res
563
+ call feedkeys("\<cr>")
522
564
  normal 1G
523
565
  call s:compose_window_mappings()
524
566
  set completefunc=CompleteContact
@@ -540,7 +582,7 @@ function! CompleteContact(findstart, base)
540
582
  return start
541
583
  else
542
584
  " find contacts matching with "a:base"
543
- let matches = system("grep -i " . shellescape(a:base) . " contacts.txt")
585
+ let matches = system("grep -i " . shellescape(a:base) . " " . $VMAIL_CONTACTS_FILE)
544
586
  return split(matches, "\n")
545
587
  endif
546
588
  endfun
@@ -620,8 +662,11 @@ func! s:message_window_mappings()
620
662
  noremap <silent> <buffer> <Leader>c :call <SID>compose_message()<CR>
621
663
  noremap <silent> <buffer> <Leader>h :call <SID>open_html_part()<CR><cr>
622
664
  nnoremap <silent> <buffer> q :close<cr>
623
- nnoremap <silent> <buffer> <leader>d :call <SID>focus_list_window()<cr>:call <SID>delete_messages("Deleted")<cr>
624
- nnoremap <silent> <buffer> s :call <SID>focus_list_window()<cr>:call <SID>toggle_star()<cr>
665
+ nnoremap <silent> <buffer> <leader># :call <SID>focus_list_window()<cr>:call <SID>delete_messages("Deleted")<cr>
666
+ nnoremap <silent> <buffer> <leader>* :call <SID>focus_list_window()<cr>:call <SID>toggle_star()<cr>
667
+ nnoremap <silent> <buffer> <Leader>b :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(0)<CR>
668
+ nnoremap <silent> <buffer> <Leader>B :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(1)<CR>
669
+ nnoremap <silent> <buffer> <leader>e :call <SID>focus_list_window()<cr>:call <SID>archive_messages()<cr>
625
670
  nnoremap <silent> <buffer> u :call <SID>focus_list_window()<cr>:call <SID>update()<CR>
626
671
  nnoremap <silent> <buffer> <Leader>m :call <SID>focus_list_window()<cr>:call <SID>mailbox_window()<CR>
627
672
  nnoremap <silent> <buffer> <Leader>A :call <SID>save_attachments()<cr>
@@ -632,20 +677,18 @@ endfunc
632
677
  func! s:message_list_window_mappings()
633
678
  noremap <silent> <buffer> <cr> :call <SID>show_message()<CR>
634
679
  noremap <silent> <buffer> q :qal!<cr>
635
- noremap <silent> <buffer> s :call <SID>toggle_star()<CR>
636
- noremap <silent> <buffer> <leader>d :call <SID>delete_messages("Deleted")<CR>
637
- " TODO the range doesn't quite work as expect, need <line1> <line2>
638
- " trying to make user defined commands that work from : prompt
639
- " command -buffer -range VmailDelete call s:toggle_star("Deleted")
640
- " command -buffer -range VmailStar call s:toggle_star("Flagged")
680
+ noremap <silent> <buffer> <leader>* :call <SID>toggle_star()<CR>
681
+ noremap <silent> <buffer> <leader># :call <SID>delete_messages("Deleted")<CR>
641
682
  noremap <silent> <buffer> <leader>! :call <SID>delete_messages("[Gmail]/Spam")<CR>
683
+ noremap <silent> <buffer> <leader>e :call <SID>archive_messages()<CR>
642
684
  "open a link browser (os x)
643
685
  "autocmd CursorMoved <buffer> call <SID>show_message()
644
686
  noremap <silent> <buffer> <leader>vp :call <SID>append_messages_to_file()<CR>
645
687
  noremap <silent> <buffer> u :call <SID>update()<CR>
646
688
  noremap <silent> <buffer> <Leader>s :call <SID>search_query()<CR>
647
689
  noremap <silent> <buffer> <Leader>m :call <SID>mailbox_window()<CR>
648
- noremap <silent> <buffer> <Leader>v :call <SID>move_to_mailbox()<CR>
690
+ noremap <silent> <buffer> <Leader>b :call <SID>move_to_mailbox(0)<CR>
691
+ noremap <silent> <buffer> <Leader>B :call <SID>move_to_mailbox(1)<CR>
649
692
  noremap <silent> <buffer> <Leader>c :call <SID>compose_message()<CR>
650
693
  noremap <silent> <buffer> <Leader>r :call <SID>show_message()<cr>:call <SID>compose_reply(0)<CR>
651
694
  noremap <silent> <buffer> <Leader>a :call <SID>show_message()<cr>:call <SID>compose_reply(1)<CR>
@@ -1,27 +1,30 @@
1
1
  require 'net/imap'
2
2
 
3
+ module Vmail
3
4
  class ContactsExtractor
4
- def initialize(config)
5
- @username, @password = config['login'], config['password']
5
+ def initialize(username, password)
6
+ puts "logging as #{username}"
7
+ @username, @password = username, password
6
8
  end
7
9
 
8
10
  def open
9
11
  @imap = Net::IMAP.new('imap.gmail.com', 993, true, nil, false)
10
- @imap.login(@username, @password)
12
+ puts @imap.login(@username, @password)
11
13
  yield @imap
12
14
  @imap.close
13
15
  @imap.disconnect
14
16
  end
15
17
 
16
- def extract!
17
- @contacts = []
18
+ def extract(limit = 500)
18
19
  open do |imap|
19
20
  mailbox = '[Gmail]/Sent Mail'
20
21
  STDERR.puts "selecting #{mailbox}"
21
22
  imap.select(mailbox)
22
- STDERR.puts "fetching last 500 sent messages"
23
+ STDERR.puts "fetching last #{limit} sent messages"
23
24
  all_uids = imap.uid_search('ALL')
24
- limit = [500, all_uids.size].max
25
+ STDERR.puts "total messages: #{all_uids.size}"
26
+ limit = [limit, all_uids.size].min
27
+ STDERR.puts "extracting addresses from #{limit} of them"
25
28
  uids = all_uids[-limit ,limit]
26
29
  imap.uid_fetch(uids, ["FLAGS", "ENVELOPE"]).each do |fetch_data|
27
30
  recipients = fetch_data.attr["ENVELOPE"].to
@@ -30,21 +33,15 @@ class ContactsExtractor
30
33
  email = [address_struct.mailbox, address_struct.host].join('@')
31
34
  name = address_struct.name
32
35
  if name
33
- puts "#{name} <#{email}>"
36
+ name = Mail::Encodings.unquote_and_convert_to(name, 'utf-8')
37
+ yield "#{name} <#{email}>"
34
38
  else
35
- puts email
39
+ yield email
36
40
  end
37
41
  end
38
42
  end
39
43
  end
40
- @contacts
41
44
  end
42
45
  end
43
-
44
- if __FILE__ == $0
45
- require 'yaml'
46
- config = YAML::load(File.read(File.expand_path("../../config/gmail.yml", __FILE__)))
47
- extractor = ContactsExtractor.new(config)
48
- # pipe through uniq | sort
49
- extractor.extract!
50
46
  end
47
+
@@ -269,7 +269,9 @@ EOF
269
269
  if flg == 'Deleted'
270
270
  # for delete, do in a separate thread because deletions are slow
271
271
  Thread.new do
272
- @imap.uid_copy(uid_set, "[Gmail]/Trash")
272
+ unless @mailbox == '[Gmail]/Trash'
273
+ @imap.uid_copy(uid_set, "[Gmail]/Trash")
274
+ end
273
275
  res = @imap.uid_store(uid_set, action, [flg.to_sym])
274
276
  end
275
277
  uid_set.each { |uid| @all_uids.delete(uid) }
@@ -285,11 +287,11 @@ EOF
285
287
  end
286
288
  end
287
289
 
288
- # uid_set is a string comming from the vim client
289
290
  def move_to(uid_set, mailbox)
290
291
  if MailboxAliases[mailbox]
291
292
  mailbox = MailboxAliases[mailbox]
292
293
  end
294
+ create_if_necessary mailbox
293
295
  log "move_to #{uid_set.inspect} #{mailbox}"
294
296
  if uid_set.is_a?(String)
295
297
  uid_set = uid_set.split(",").map(&:to_i)
@@ -298,6 +300,26 @@ EOF
298
300
  log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
299
301
  end
300
302
 
303
+ def copy_to(uid_set, mailbox)
304
+ if MailboxAliases[mailbox]
305
+ mailbox = MailboxAliases[mailbox]
306
+ end
307
+ create_if_necessary mailbox
308
+ log "copy #{uid_set.inspect} #{mailbox}"
309
+ if uid_set.is_a?(String)
310
+ uid_set = uid_set.split(",").map(&:to_i)
311
+ end
312
+ log @imap.uid_copy(uid_set, mailbox)
313
+ end
314
+
315
+ def create_if_necessary(mailbox)
316
+ if !@mailboxes.include?(mailbox)
317
+ log "creating mailbox #{mailbox}"
318
+ log @imap.create(mailbox)
319
+ @mailboxes = nil # force reload next fime list_mailboxes() called
320
+ end
321
+ end
322
+
301
323
  def append_to_file(file, uid_set)
302
324
  if uid_set.is_a?(String)
303
325
  uid_set = uid_set.split(",").map(&:to_i)
@@ -512,7 +534,7 @@ EOF
512
534
 
513
535
  def self.daemon(config)
514
536
  $gmail = self.start(config)
515
- puts DRb.start_service(nil, $gmail)
537
+ DRb.start_service(nil, $gmail)
516
538
  uri = DRb.uri
517
539
  puts "starting gmail service at #{uri}"
518
540
  uri
@@ -0,0 +1,129 @@
1
+ require 'optparse'
2
+ require 'highline/import'
3
+
4
+ module Vmail
5
+ class Options
6
+ DEFAULT_CONTACTS_FILENAME = "vmail-contacts.txt"
7
+ attr_accessor :config
8
+ attr_accessor :contacts_file
9
+ def initialize(argv)
10
+ config_file_locations = ['.vmailrc', "#{ENV['HOME']}/.vmailrc"]
11
+ @config_file = config_file_locations.detect do |path|
12
+ File.exists?(File.expand_path(path))
13
+ end
14
+ @contacts_file = [DEFAULT_CONTACTS_FILENAME, "#{ENV['HOME']}/#{DEFAULT_CONTACTS_FILENAME}"].detect do |path|
15
+ File.exists?(File.expand_path(path))
16
+ end
17
+ @config = {}
18
+ parse argv
19
+ end
20
+
21
+ def parse(argv)
22
+ OptionParser.new do |opts|
23
+ opts.banner = "Usage: vmail [ options ] [ limit ] [ imap search query ]"
24
+ opts.separator ""
25
+ opts.separator "Specific options:"
26
+ opts.on("-c", "--config path", String, "Path to config file") do |config_file|
27
+ @config_file = config_file
28
+ end
29
+ opts.on("-t", "--contacts path", String, "Path to contacts file") do |file|
30
+ @contacts_file = file
31
+ end
32
+ opts.on("-g[n]", "--getcontacts[n]", Integer, "Generate contacts file. n is number of emails to scan (default 500).") do |n|
33
+ @get_contacts = true
34
+ @max_messages_to_scan = n || 500
35
+ end
36
+ opts.on("-h", "--help", "Show this message") do
37
+ puts opts
38
+ exit
39
+ end
40
+ opts.separator ""
41
+ opts.separator INSTRUCTIONS
42
+
43
+ begin
44
+ opts.parse!(argv)
45
+ if @config_file && File.exists?(@config_file)
46
+ puts "Using config file #{@config_file}"
47
+ else
48
+ puts <<EOF
49
+
50
+ Missing config file!
51
+
52
+ #{INSTRUCTIONS}
53
+ EOF
54
+ exit(1)
55
+ end
56
+
57
+ if @contacts_file.nil?
58
+ puts "No contacts file found for auto-completion. See help for how to generate it."
59
+ sleep 0.5
60
+ end
61
+
62
+ @config = YAML::load(File.read(@config_file))
63
+ if @config['password'].nil?
64
+ @config['password'] = ask("Enter gmail password (won't be visible & won't be persisted):") {|q| q.echo = false}
65
+ end
66
+
67
+ if @get_contacts
68
+ require 'vmail/contacts_extractor'
69
+ extractor = ContactsExtractor.new(@config['username'], @config['password'])
70
+ File.open(DEFAULT_CONTACTS_FILENAME, 'w') do |file|
71
+ extractor.extract(@max_messages_to_scan) do |address|
72
+ STDERR.print '.'
73
+ file.puts(address.strip)
74
+ STDERR.flush
75
+ end
76
+ end
77
+ STDERR.print "\n"
78
+ puts "saved file to #{DEFAULT_CONTACTS_FILENAME}"
79
+ puts "sorting address..."
80
+ cmd = "sort #{DEFAULT_CONTACTS_FILENAME} | uniq > vmail-tmp.txt"
81
+ cmd2 = "mv vmail-tmp.txt #{DEFAULT_CONTACTS_FILENAME}"
82
+ `#{cmd}`
83
+ `#{cmd2}`
84
+ puts "done"
85
+ exit
86
+ end
87
+
88
+ rescue OptionParser::ParseError => e
89
+ STDERR.puts e.message, "\n", opts
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+
96
+ INSTRUCTIONS = <<-EOF
97
+ CONFIGURATION FILE
98
+
99
+ To run vmail, you need to create a yaml file called .vmailrc and save it
100
+ either in the current directory (the directory from which you launch
101
+ vmail) or in your home directory.
102
+
103
+ This file should look like this, except using your settings:
104
+
105
+ username: dhchoi@gmail.com
106
+ password: password
107
+ name: Daniel Choi
108
+ signature: |
109
+ --
110
+ Sent via vmail. https://github.com/danchoi/vmail
111
+
112
+ This file should be formatted according to the rules of YAML.
113
+ http://www.yaml.org/spec/1.2/spec.html
114
+
115
+ You can omit the password key-value pair if you'd rather not have the password
116
+ on disk. In that case, you'll prompted for the password each time you
117
+ start vmail.
118
+
119
+ CONTACTS AUTOCOMPLETION
120
+
121
+ Vmail uses vim autocompletion to help you auto-complete email addresses.
122
+ To use this feature, generate a vim-contacts.txt file in the current or
123
+ home directory. This is a simple list of all the email addresses.
124
+ Invoking vmail with the -g option generates this file for you by
125
+ collecting all the recipients and cc's from your last 500 or so sent
126
+ emails.
127
+
128
+ EOF
129
+ end
@@ -1,3 +1,3 @@
1
1
  module Vmail
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -7,7 +7,7 @@ describe Vmail::MessageFormatter do
7
7
  before do
8
8
  @raw = File.read(File.expand_path('../fixtures/google-affiliate.eml', __FILE__))
9
9
  @mail = Mail.new(@raw)
10
- @formatter = MessageFormatter.new(@mail)
10
+ @formatter = Vmail::MessageFormatter.new(@mail)
11
11
  end
12
12
 
13
13
  it "should extract name along with email address" do
@@ -22,7 +22,7 @@ describe Vmail::MessageFormatter do
22
22
  before do
23
23
  @raw = File.read(File.expand_path('../fixtures/textbody-nocontenttype.eml', __FILE__))
24
24
  @mail = Mail.new(@raw)
25
- @formatter = MessageFormatter.new(@mail)
25
+ @formatter = Vmail::MessageFormatter.new(@mail)
26
26
  end
27
27
  it "should return the text body" do
28
28
  @formatter.process_body.wont_be_nil
@@ -34,7 +34,7 @@ describe Vmail::MessageFormatter do
34
34
  before do
35
35
  @raw = File.read(File.expand_path('../fixtures/htmlbody.eml', __FILE__))
36
36
  @mail = Mail.new(@raw)
37
- @formatter = MessageFormatter.new(@mail)
37
+ @formatter = Vmail::MessageFormatter.new(@mail)
38
38
  end
39
39
  it "should turn the body into readable text" do
40
40
  @formatter.process_body.must_match %r{\n Web 1 new result for instantwatcher.com netflix}
@@ -45,7 +45,7 @@ describe Vmail::MessageFormatter do
45
45
  before do
46
46
  @raw = File.read(File.expand_path('../fixtures/euc-kr-header.eml', __FILE__))
47
47
  @mail = Mail.new(@raw)
48
- @formatter = MessageFormatter.new(@mail)
48
+ @formatter = Vmail::MessageFormatter.new(@mail)
49
49
  end
50
50
  it "should know its encoding" do
51
51
  @mail.header.charset.must_equal 'euc-kr'
@@ -60,7 +60,7 @@ describe Vmail::MessageFormatter do
60
60
  before do
61
61
  @raw = File.read(File.expand_path('../fixtures/moleskine-html.eml', __FILE__))
62
62
  @mail = Mail.new(@raw)
63
- @formatter = MessageFormatter.new(@mail)
63
+ @formatter = Vmail::MessageFormatter.new(@mail)
64
64
  end
65
65
  it "should process body" do
66
66
  @formatter.process_body.wont_be_nil
@@ -20,4 +20,5 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ["lib"]
21
21
 
22
22
  s.add_dependency 'mail'
23
+ s.add_dependency 'highline'
23
24
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 6
9
- version: 0.0.6
8
+ - 7
9
+ version: 0.0.7
10
10
  platform: ruby
11
11
  authors:
12
12
  - Daniel Choi
@@ -30,6 +30,19 @@ dependencies:
30
30
  version: "0"
31
31
  type: :runtime
32
32
  version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: highline
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
33
46
  description: Manage your email with Vim
34
47
  email:
35
48
  - dhchoi@gmail.com
@@ -48,11 +61,12 @@ files:
48
61
  - bin/vmail
49
62
  - bin/vmail_client
50
63
  - gmail.vim
51
- - lib/contacts_extractor.rb
52
64
  - lib/vmail.rb
53
65
  - lib/vmail.vim
66
+ - lib/vmail/contacts_extractor.rb
54
67
  - lib/vmail/imap_client.rb
55
68
  - lib/vmail/message_formatter.rb
69
+ - lib/vmail/options.rb
56
70
  - lib/vmail/string_ext.rb
57
71
  - lib/vmail/version.rb
58
72
  - test/base64_test.rb