rnote 0.0.1

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/bin/rnote ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'rnote'
4
+
5
+ include GLI::App
6
+
7
+ program_desc 'Evernote Command Line'
8
+
9
+ # version Rnote::VERSION
10
+
11
+ desc 'prompt for input and run an editor when necessary'
12
+ default_value true
13
+ switch :interactive
14
+
15
+ pre do |global,command,options,args|
16
+ # Pre logic here
17
+ # Return true to proceed; false to abort and not call the
18
+ # chosen command
19
+ # Use skips_pre before a command to skip this block
20
+ # on that command only
21
+ true
22
+ end
23
+
24
+ post do |global,command,options,args|
25
+ # Post logic here
26
+ # Use skips_post before a command to skip this
27
+ # block on that command only
28
+ end
29
+
30
+ def error_code_to_name(error_code)
31
+ Evernote::EDAM::Error::EDAMErrorCode.constants.select { |constant| Evernote::EDAM::Error::EDAMErrorCode.const_get(constant) == error_code }.first.to_s
32
+ end
33
+
34
+ on_error do |exception|
35
+ # Error logic here
36
+ # return false to skip default error handling
37
+
38
+ if exception.instance_of?(Evernote::EDAM::Error::EDAMUserException)
39
+ if exception.errorCode == Evernote::EDAM::Error::EDAMErrorCode::AUTH_EXPIRED
40
+ puts "Authorization issue. perhaps your password is incorrect or expired."
41
+ false
42
+ else
43
+ puts exception.error_message
44
+ true
45
+ end
46
+ elsif exception.instance_of?(Evernote::EDAM::Error::EDAMSystemException)
47
+ puts exception.error_message
48
+ true
49
+ elsif exception.class == Evernote::EDAM::Error::InvalidXmlError
50
+ puts exception.xml
51
+ true
52
+ else
53
+ true
54
+ end
55
+
56
+ end
57
+
58
+ $app = Rnote::App.new
59
+
60
+ exit run(ARGV)
data/lib/rnote.rb ADDED
@@ -0,0 +1,75 @@
1
+
2
+
3
+ # environment detection
4
+ begin
5
+ # this file only exists in development
6
+ # its not included in the gem,
7
+ # and thus not found in production (the installed gem)
8
+ require_relative 'rnote/environment'
9
+ rescue LoadError
10
+ # production environment
11
+ # should only happen in the installed gem.
12
+ RNOTE_HOME ||= ENV['HOME'] + '/.rnote'
13
+ RNOTE_TESTING_OK = false
14
+ RNOTE_SANDBOX_ONLY = false
15
+ end
16
+
17
+ require 'rnote/version'
18
+ require 'rnote/converter'
19
+ require 'rnote/persister'
20
+ require 'rnote/auth'
21
+
22
+ # verbs
23
+ Dir[File.absolute_path(File.dirname(__FILE__)) + '/rnote/cmd/*.rb'].each do |file|
24
+ require file
25
+ end
26
+
27
+
28
+ module Rnote
29
+
30
+ class App
31
+
32
+ attr_reader :persister,:auth
33
+
34
+ def initialize
35
+ @persister = Persister.new
36
+ @auth = Auth.new(@persister)
37
+ end
38
+
39
+ def client
40
+ auth.client
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ module EDAMErrors
48
+
49
+ def error_code_string
50
+ Evernote::EDAM::Error::EDAMErrorCode.constants.select { |constant_sym|
51
+ Evernote::EDAM::Error::EDAMErrorCode.const_get(constant_sym) == self.errorCode
52
+ }.first.to_s
53
+ end
54
+
55
+ end
56
+
57
+ class Evernote::EDAM::Error::EDAMSystemException
58
+
59
+ include EDAMErrors
60
+
61
+ def error_message
62
+ "#{self.error_code_string}(#{self.errorCode}: #{self.message})"
63
+ end
64
+
65
+ end
66
+
67
+ class Evernote::EDAM::Error::EDAMUserException
68
+
69
+ include EDAMErrors
70
+
71
+ def error_message
72
+ "#{self.error_code_string}(#{self.errorCode}): #{self.parameter}"
73
+ end
74
+
75
+ end
data/lib/rnote/auth.rb ADDED
@@ -0,0 +1,130 @@
1
+
2
+ require 'mechanize'
3
+ require 'evernote_oauth'
4
+ require 'rnote/persister'
5
+
6
+ DUMMY_CALLBACK_URL = 'http://www.evernote.com'
7
+
8
+ module Rnote
9
+
10
+ class Auth
11
+
12
+ def initialize(persister=Persister.new)
13
+ @persister = persister
14
+ end
15
+
16
+ def login_with_developer_token(developer_token,sandbox)
17
+ if is_logged_in
18
+ if @persister.get_developer_token == developer_token
19
+ return
20
+ else
21
+ logout
22
+ end
23
+ end
24
+ @persister.persist_developer_token(developer_token)
25
+ @persister.persist_sandbox(sandbox)
26
+ end
27
+
28
+ def login_with_password(username,password,sandbox)
29
+
30
+ if is_logged_in
31
+ if who == username
32
+ # already logged in (we don't check against service though)
33
+ # if a re-login is truely required, the user can just logout first.
34
+ return
35
+ else
36
+ logout
37
+ end
38
+ end
39
+
40
+ ## Get a user key using these crednetials
41
+
42
+ # this client isn't authorized, and can only request authorization. no api calls.
43
+ auth_client = EvernoteOAuth::Client.new(
44
+ consumer_key: @persister.get_consumer_key,
45
+ consumer_secret: @persister.get_consumer_secret,
46
+ sandbox: sandbox
47
+ )
48
+
49
+ request_token = auth_client.request_token(:oauth_callback => DUMMY_CALLBACK_URL)
50
+ oauth_verifier = mechanize_login(request_token.authorize_url, username, password)
51
+ access_token = request_token.get_access_token(oauth_verifier: oauth_verifier)
52
+ user_token = access_token.token
53
+
54
+ @persister.persist_username(username)
55
+ @persister.persist_user_token(user_token)
56
+ @persister.persist_sandbox(sandbox)
57
+
58
+ end
59
+
60
+ def client
61
+ # not the same as the client used to get the token.
62
+ # this one is fully authorized and can make actual api calls.
63
+
64
+ if not is_logged_in
65
+ raise "not logged in"
66
+ end
67
+
68
+ token = @persister.get_user_token || @persister.get_developer_token
69
+
70
+ @client ||= EvernoteOAuth::Client.new(token: token, sandbox: @persister.get_sandbox)
71
+
72
+ @client
73
+ end
74
+
75
+ def note_store
76
+ client.note_store
77
+ end
78
+
79
+ def mechanize_login(url, username, password)
80
+
81
+ agent = Mechanize.new
82
+ login_page = agent.get(url)
83
+ login_form = login_page.form('login_form')
84
+ raise unless login_form
85
+ login_form.username = username
86
+ login_form.password = password
87
+ accept_page = agent.submit(login_form,login_form.buttons.first)
88
+
89
+ if accept_page.form('login_form')
90
+ # sent us back to the login page
91
+ raise "bad username/password"
92
+ elsif not accept_page.form('oauth_authorize_form')
93
+ raise "failed to login"
94
+ end
95
+
96
+ accept_form = accept_page.form('oauth_authorize_form')
97
+ # we don't need to go so far as to retrieve the callback url.
98
+ agent.redirect_ok = false
99
+ callback_redirect = agent.submit(accept_form, accept_form.buttons.first)
100
+ response_url = callback_redirect.response['location']
101
+ oauth_verifier = CGI.parse(URI.parse(response_url).query)['oauth_verifier'][0]
102
+
103
+ oauth_verifier
104
+ end
105
+
106
+ def is_logged_in
107
+ @persister.get_user_token or @persister.get_developer_token
108
+ end
109
+
110
+ def who
111
+ if is_logged_in
112
+ @persister.get_username or @persister.get_developer_token
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+
119
+ def logout
120
+ # unfortunately, no way to revoke a token via API
121
+ # TODO perhaps I can redo the oauth, and choose revoke instead of re-accept
122
+ @persister.forget_user_token
123
+ @persister.forget_username
124
+ @persister.forget_developer_token
125
+ @persister.forget_sandbox
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,32 @@
1
+
2
+ require 'gli'
3
+
4
+ require 'rnote/edit'
5
+
6
+ include GLI::App
7
+
8
+
9
+ desc 'create a note and launch the editor for it'
10
+ command :create do |verb|
11
+ verb.command :note do |noun|
12
+
13
+
14
+ Rnote::Edit.include_set_options(noun)
15
+ Rnote::Edit.include_editor_options(noun)
16
+
17
+ noun.action do |global_options,options,args|
18
+
19
+ if args.length > 0
20
+ raise "create doesn't take a search query"
21
+ end
22
+
23
+ edit = Rnote::Edit.new($app.auth)
24
+ edit.options(options.merge(global_options))
25
+ edit.edit_action
26
+
27
+
28
+ end
29
+ end
30
+
31
+ verb.default_command :note
32
+ end
@@ -0,0 +1,36 @@
1
+
2
+ require 'rnote/edit'
3
+ require 'rnote/find'
4
+
5
+ include GLI::App
6
+
7
+
8
+ desc 'Describe edit here'
9
+ arg_name 'Describe arguments to edit here'
10
+ command :edit do |verb|
11
+
12
+ verb.command :note do |noun|
13
+
14
+ Rnote::Edit.include_set_options(noun)
15
+ Rnote::Edit.include_editor_options(noun)
16
+ Rnote::Find.include_search_options(noun)
17
+
18
+ noun.action do |global_options,options,args|
19
+
20
+ find = Rnote::Find.new($app.auth,$app.persister)
21
+ note = find.find_note(options.merge(global_options),args)
22
+
23
+ edit = Rnote::Edit.new($app.auth)
24
+ edit.options(options.merge(global_options))
25
+ edit.note(note)
26
+ edit.edit_action
27
+
28
+ end
29
+
30
+ end
31
+
32
+ verb.default_command :note
33
+
34
+ end
35
+
36
+
@@ -0,0 +1,24 @@
1
+
2
+ require 'rnote/find'
3
+
4
+ include GLI::App
5
+
6
+
7
+ desc 'search for notes/tags/notebooks'
8
+ command :find do |verb|
9
+
10
+ verb.command :note do |noun|
11
+
12
+ Rnote::Find.include_search_options(noun)
13
+
14
+ noun.action do |global_options,options,args|
15
+
16
+ find = Rnote::Find.new($app.auth,$app.persister)
17
+ find.find_cmd(options.merge(global_options),args)
18
+
19
+ end
20
+ end
21
+
22
+ verb.default_command :note
23
+ end
24
+
@@ -0,0 +1,68 @@
1
+
2
+ require 'highline/import'
3
+
4
+ include GLI::App
5
+
6
+ desc 'provide rnote credentials'
7
+ command :login do |c|
8
+
9
+ c.desc "username"
10
+ c.flag [:u,:user,:username]
11
+
12
+ c.desc "password (if not provided, will ask)"
13
+ c.flag [:p,:pass,:password]
14
+
15
+ c.desc "developer token, if you wish to forgoe a password."
16
+ c.flag [:d,:'dev-token',:'developer-token']
17
+
18
+ c.desc "use the sandbox environment in lue of the production evernote system."
19
+ c.default_value false
20
+ c.switch [:s,:sandbox]
21
+
22
+ c.desc "provide a consumer key, instead of the included one."
23
+ c.flag [:k,:key,:'consumer-key']
24
+
25
+ c.desc "provide a consumer secret to go along with the consumer key."
26
+ c.flag [:c,:secret,:'consumer-secret']
27
+
28
+ c.action do |global_options,options,args|
29
+ raise "This command takes no arguments, only options (i.e. --username" unless args.length == 0
30
+
31
+ if options[:key]
32
+ $app.persister.persist_consumer_key(options[:key])
33
+ end
34
+
35
+ if options[:secret]
36
+ $app.persister.persist_consumer_secret(options[:secret])
37
+ end
38
+
39
+ if options[:d]
40
+ # first check for a dev token
41
+ $app.auth.login_with_developer_token(options[:d],options[:sandbox])
42
+ puts "login successful using developer key"
43
+ else
44
+ # then fall back to using a username and password
45
+
46
+ if not options[:u]
47
+ answer = ask("Enter your username: ")
48
+ options[:u] = answer
49
+ end
50
+
51
+ if not options[:p]
52
+ answer = ask("Enter your password: ") { |q| q.echo = 'x' }
53
+ options[:p] = answer
54
+ end
55
+
56
+ $app.auth.login_with_password(options[:user],options[:password], options[:sandbox])
57
+
58
+ # test the login with a harmless api call.
59
+ $app.auth.client.user_store.getUser
60
+
61
+ puts "you are now logged in as '#{$app.auth.who}'"
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+
@@ -0,0 +1,24 @@
1
+
2
+ include GLI::App
3
+
4
+ =begin
5
+
6
+ only drops the token.
7
+ there is no way to ask evernote to revoke the token, from an api.
8
+
9
+ will forget about the user,password or developer key.
10
+ but won't forget about a consumer key, as that by itself is not considered a login.
11
+
12
+ =end
13
+
14
+ desc 'log user out of rnote'
15
+ command :logout do |c|
16
+ c.action do |global_options,options,args|
17
+ raise unless args.length == 0
18
+
19
+ $app.auth.logout
20
+
21
+ end
22
+ end
23
+
24
+