vmail 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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