things-fetcher 0.1.0
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/.gitignore +5 -0
- data/CHANGELOG +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +51 -0
- data/Rakefile +1 -0
- data/bin/things-fetcher +16 -0
- data/lib/fetcher/base.rb +63 -0
- data/lib/fetcher/imap.rb +88 -0
- data/lib/fetcher/plain_imap.rb +21 -0
- data/lib/fetcher.rb +21 -0
- data/lib/keychain.rb +23 -0
- data/lib/things_fetcher/config.rb +122 -0
- data/lib/things_fetcher/fetcher.rb +38 -0
- data/lib/things_fetcher/mail_handler.rb +29 -0
- data/lib/things_fetcher/version.rb +3 -0
- data/lib/things_fetcher.rb +2 -0
- data/things-fetcher.gemspec +30 -0
- metadata +100 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.1.0. Initial release.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Andrey Subbotin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= things-fetcher
|
2
|
+
|
3
|
+
A simple tool to fetch messages from an IMAP folder and create a new to-do item in the Things app for each.
|
4
|
+
|
5
|
+
== How It Works
|
6
|
+
|
7
|
+
Say, you use Gmail. Say, you want all the mail with +[todo]+ in the subject field to be automatically routed to your Things' Inbox.
|
8
|
+
|
9
|
+
1. Create a filter so that all subject mails end up in the same folder/label.
|
10
|
+
2. <tt>gem install things-fetcher</tt>
|
11
|
+
3. Run <tt>things-fetcher</tt> in Terminal to generate a default config file at <tt>~/.things_fetcher</tt>
|
12
|
+
4. Edit the config file at <tt>~/.things_fetcher</tt> to specify your email and so on.
|
13
|
+
5. Run <tt>things-fetcher</tt> in Terminal.
|
14
|
+
|
15
|
+
Set up a cron task to run <tt>things-fetcher</tt> periodically.
|
16
|
+
|
17
|
+
== Configuration
|
18
|
+
|
19
|
+
The following options can be specified in the <tt>~/.things_fetcher</tt> file:
|
20
|
+
|
21
|
+
[<tt>username</tt>] The username to authenticate with. You must specify one.
|
22
|
+
[<tt>password</tt>] The password to authenticate with. Default is to use the Keychain.
|
23
|
+
[<tt>server</tt>] The IP address or domain name of the IMAP server. Defaults to +imap.gmail.com+.
|
24
|
+
[<tt>port</tt>] The port to connect to. Defaults to +993+.
|
25
|
+
[<tt>ssl</tt>] Set to any value to use SSL encryption. Enabled by default.
|
26
|
+
[<tt>use_login</tt>] Set to any value to use the LOGIN command instead of AUTHENTICATE. Some servers, like GMail, do not support AUTHENTICATE. Enabled by default.
|
27
|
+
[<tt>in_folder</tt>] The name of the folder from which to read incoming mail. Defaults to +Things+.
|
28
|
+
[<tt>error_folder</tt>] The name a folder to move mail that causes an error during processing. Defaults to +Things+.
|
29
|
+
[<tt>list</tt>] The list in Things to create TODOs in. Defaults to +Inbox+.
|
30
|
+
[<tt>tag_names</tt>] The tags to apply to the newly created TODOs. Defaults to +Texted+.
|
31
|
+
|
32
|
+
== Note on Patches/Pull Requests
|
33
|
+
|
34
|
+
* Fork the project.
|
35
|
+
* Make your feature addition or bug fix.
|
36
|
+
* Add tests for it. This is important so I don't break it in a
|
37
|
+
future version unintentionally.
|
38
|
+
* Commit, do not mess with rakefile, version, or history.
|
39
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
40
|
+
* Send me a pull request. Bonus points for topic branches.
|
41
|
+
|
42
|
+
== Copyright
|
43
|
+
|
44
|
+
Copyright (c) 2012 Andrey Subbotin. See LICENSE for details.
|
45
|
+
|
46
|
+
== The IMAP fetching code
|
47
|
+
|
48
|
+
Created by Dan Weinand and Luke Francl. Development supported by {Slantwise Design}[http://slantwisedesign.com].
|
49
|
+
Licensed under the terms of the MIT License. Be excellent to each other.
|
50
|
+
|
51
|
+
The original repo can be found at: https://github.com/look/fetcher
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/things-fetcher
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'things_fetcher'
|
5
|
+
|
6
|
+
# Main block
|
7
|
+
|
8
|
+
def main
|
9
|
+
config = ThingsFetcher::Config.new("~/.things_fetcher")
|
10
|
+
fetcher = ThingsFetcher::Fetcher.new(config)
|
11
|
+
fetcher.run
|
12
|
+
end
|
13
|
+
|
14
|
+
# Aux methods
|
15
|
+
|
16
|
+
main
|
data/lib/fetcher/base.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Fetcher
|
2
|
+
class Base
|
3
|
+
# Options:
|
4
|
+
# * <tt>:server</tt> - Server to connect to.
|
5
|
+
# * <tt>:username</tt> - Username to use when connecting to server.
|
6
|
+
# * <tt>:password</tt> - Password to use when connecting to server.
|
7
|
+
# * <tt>:receiver</tt> - Receiver object to pass messages to. Assumes the
|
8
|
+
# receiver object has a receive method that takes a message as it's argument
|
9
|
+
#
|
10
|
+
# Additional protocol-specific options implimented by sub-classes
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# Fetcher::Base.new(:server => 'mail.example.com',
|
14
|
+
# :username => 'pam',
|
15
|
+
# :password => 'test',
|
16
|
+
# :receiver => IncomingMailHandler)
|
17
|
+
def initialize(options={})
|
18
|
+
%w(server username password receiver).each do |opt|
|
19
|
+
raise ArgumentError, "#{opt} is required" unless options[opt.to_sym]
|
20
|
+
# convert receiver to a Class if it isn't already.
|
21
|
+
if opt == "receiver" && options[:receiver].is_a?(String)
|
22
|
+
options[:receiver] = Kernel.const_get(options[:receiver])
|
23
|
+
end
|
24
|
+
|
25
|
+
instance_eval("@#{opt} = options[:#{opt}]")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Run the fetching process
|
30
|
+
def fetch
|
31
|
+
establish_connection
|
32
|
+
get_messages
|
33
|
+
close_connection
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# Stub. Should be overridden by subclass.
|
39
|
+
def establish_connection #:nodoc:
|
40
|
+
raise NotImplementedError, "This method should be overridden by subclass"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Stub. Should be overridden by subclass.
|
44
|
+
def get_messages #:nodoc:
|
45
|
+
raise NotImplementedError, "This method should be overridden by subclass"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stub. Should be overridden by subclass.
|
49
|
+
def close_connection #:nodoc:
|
50
|
+
raise NotImplementedError, "This method should be overridden by subclass"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Send message to receiver object
|
54
|
+
def process_message(message)
|
55
|
+
@receiver.receive(message)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Stub. Should be overridden by subclass.
|
59
|
+
def handle_bogus_message(message) #:nodoc:
|
60
|
+
raise NotImplementedError, "This method should be overridden by subclass"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/fetcher/imap.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
(RUBY_VERSION < '1.9.0') ? require('system_timer') : require('timeout')
|
2
|
+
require_relative 'plain_imap'
|
3
|
+
|
4
|
+
module Fetcher
|
5
|
+
class Imap < Base
|
6
|
+
|
7
|
+
PORT = 143
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
# Additional Options:
|
12
|
+
# * <tt>:authentication</tt> - authentication type to use, defaults to PLAIN
|
13
|
+
# * <tt>:port</tt> - port to use (defaults to 143)
|
14
|
+
# * <tt>:ssl</tt> - use SSL to connect
|
15
|
+
# * <tt>:use_login</tt> - use LOGIN instead of AUTHENTICATE to connect (some IMAP servers, like GMail, do not support AUTHENTICATE)
|
16
|
+
# * <tt>:processed_folder</tt> - if set to the name of a mailbox, messages will be moved to that mailbox instead of deleted after processing. The mailbox will be created if it does not exist.
|
17
|
+
# * <tt>:error_folder:</tt> - the name of a mailbox where messages that cannot be processed (i.e., your receiver throws an exception) will be moved. Defaults to "bogus". The mailbox will be created if it does not exist.
|
18
|
+
def initialize(options={})
|
19
|
+
@authentication = options.delete(:authentication) || 'PLAIN'
|
20
|
+
@port = options.delete(:port) || PORT
|
21
|
+
@ssl = options.delete(:ssl)
|
22
|
+
@use_login = options.delete(:use_login)
|
23
|
+
@in_folder = options.delete(:in_folder) || 'INBOX'
|
24
|
+
@processed_folder = options.delete(:processed_folder)
|
25
|
+
@error_folder = options.delete(:error_folder) || 'bogus'
|
26
|
+
super(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Open connection and login to server
|
30
|
+
def establish_connection
|
31
|
+
timeout_call = (RUBY_VERSION < '1.9.0') ? "SystemTimer.timeout_after(15.seconds) do" : "Timeout::timeout(15) do"
|
32
|
+
|
33
|
+
eval("#{timeout_call}
|
34
|
+
@connection = Net::IMAP.new(@server, @port, @ssl)
|
35
|
+
if @use_login
|
36
|
+
@connection.login(@username, @password)
|
37
|
+
else
|
38
|
+
@connection.authenticate(@authentication, @username, @password)
|
39
|
+
end
|
40
|
+
end")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Retrieve messages from server
|
44
|
+
def get_messages
|
45
|
+
@connection.select(@in_folder)
|
46
|
+
@connection.uid_search(['ALL']).each do |uid|
|
47
|
+
msg = @connection.uid_fetch(uid,'RFC822').first.attr['RFC822']
|
48
|
+
begin
|
49
|
+
process_message(msg)
|
50
|
+
add_to_processed_folder(uid) if @processed_folder
|
51
|
+
rescue
|
52
|
+
handle_bogus_message(msg)
|
53
|
+
end
|
54
|
+
# Mark message as deleted
|
55
|
+
@connection.uid_store(uid, "+FLAGS", [:Seen, :Deleted])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Store the message for inspection if the receiver errors
|
60
|
+
def handle_bogus_message(message)
|
61
|
+
create_mailbox(@error_folder)
|
62
|
+
@connection.append(@error_folder, message)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Delete messages and log out
|
66
|
+
def close_connection
|
67
|
+
@connection.expunge
|
68
|
+
@connection.logout
|
69
|
+
begin
|
70
|
+
@connection.disconnect unless @connection.disconnected?
|
71
|
+
rescue
|
72
|
+
Rails.logger.info("Fetcher: Remote closed connection before I could disconnect.")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_to_processed_folder(uid)
|
77
|
+
create_mailbox(@processed_folder)
|
78
|
+
@connection.uid_copy(uid, @processed_folder)
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_mailbox(mailbox)
|
82
|
+
unless @connection.list("", mailbox)
|
83
|
+
@connection.create(mailbox)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'net/imap'
|
2
|
+
# add plain as an authentication type...
|
3
|
+
# This is taken from:
|
4
|
+
# http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/net/imap.rb?revision=7657&view=markup&pathrev=10966
|
5
|
+
|
6
|
+
# Authenticator for the "PLAIN" authentication type. See
|
7
|
+
# #authenticate().
|
8
|
+
class PlainAuthenticator
|
9
|
+
def process(data)
|
10
|
+
return "\0#{@user}\0#{@password}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(user, password)
|
16
|
+
@user = user
|
17
|
+
@password = password
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Net::IMAP.add_authenticator "PLAIN", PlainAuthenticator
|
data/lib/fetcher.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fetcher
|
2
|
+
# Use factory-style initialization or insantiate directly from a subclass
|
3
|
+
#
|
4
|
+
# Options:
|
5
|
+
# * <tt>:type</tt> - Name of class as a symbol to instantiate
|
6
|
+
#
|
7
|
+
# Other options are the same as Fetcher::Base.new
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# Fetcher.create(:type => :pop) is equivalent to
|
12
|
+
# Fetcher::Pop.new()
|
13
|
+
def self.create(options={})
|
14
|
+
klass = options.delete(:type)
|
15
|
+
raise ArgumentError, 'Must supply a type' unless klass
|
16
|
+
module_eval "#{klass.to_s.capitalize}.new(options)"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require_relative 'fetcher/base'
|
21
|
+
require_relative 'fetcher/imap'
|
data/lib/keychain.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# blog post: http://blog.slashpoundbang.com/post/1521530410/accessing-the-os-x-keychain-from-ruby
|
2
|
+
|
3
|
+
class KeyChain
|
4
|
+
def self.method_missing(meth, *args)
|
5
|
+
run args.unshift(meth)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.find_internet_password(*args)
|
9
|
+
# -g: Display the password for the item found
|
10
|
+
output = quiet args.unshift('find-internet-password', '-g')
|
11
|
+
output[/^password: "(.*)"$/, 1]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.run(*args)
|
17
|
+
`security #{args.join(' ')}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.quiet(*args)
|
21
|
+
run args.unshift('2>&1 >/dev/null')
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require_relative '../keychain'
|
4
|
+
|
5
|
+
module ThingsFetcher
|
6
|
+
class Config
|
7
|
+
def initialize(path)
|
8
|
+
super
|
9
|
+
|
10
|
+
@data = default_config
|
11
|
+
@data.merge! load_from_file(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
data.has_key?(key) ? data[key] : send(key.to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def password
|
19
|
+
KeyChain.find_internet_password '-s', data[:server], '-a', data[:username]
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
attr_accessor :data
|
24
|
+
|
25
|
+
def default_config
|
26
|
+
{
|
27
|
+
:server => 'imap.gmail.com',
|
28
|
+
:port => 993,
|
29
|
+
:ssl => true,
|
30
|
+
:use_login => true,
|
31
|
+
:in_folder => 'Things',
|
32
|
+
:error_folder => 'Things',
|
33
|
+
:tag_names => 'Texted',
|
34
|
+
:list => 'Inbox'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_from_file(path)
|
39
|
+
expanded_path = File.expand_path(path)
|
40
|
+
if File.exists?(expanded_path)
|
41
|
+
loaded_config = YAML::load(File.open(expanded_path))
|
42
|
+
result = {}
|
43
|
+
config_keys.each do |k|
|
44
|
+
if loaded_config.has_key?(k.to_s)
|
45
|
+
result[k] = loaded_config[k.to_s]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
if result.has_key?(:username)
|
49
|
+
result
|
50
|
+
else
|
51
|
+
puts "ERROR: No username specified in the config."
|
52
|
+
puts " Please edit the config file at: #{expanded_path}"
|
53
|
+
exit 0
|
54
|
+
end
|
55
|
+
else
|
56
|
+
generate_config_file(expanded_path)
|
57
|
+
puts "ERROR: No config file found at: #{expanded_path}"
|
58
|
+
puts " A new config has been generated."
|
59
|
+
puts " Please edit."
|
60
|
+
exit 0
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def config_keys
|
65
|
+
[
|
66
|
+
:server,
|
67
|
+
:port,
|
68
|
+
:ssl,
|
69
|
+
:use_login,
|
70
|
+
:in_folder,
|
71
|
+
:error_folder,
|
72
|
+
:tag_names,
|
73
|
+
:list,
|
74
|
+
:username,
|
75
|
+
:password
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_config_file(path)
|
80
|
+
File.open(path, "w") do |f|
|
81
|
+
f << <<-EOS
|
82
|
+
# Any line that starts with a # is a comment.
|
83
|
+
# Leave any of the configuration options commented out to use the default value.
|
84
|
+
|
85
|
+
# The username to authenticate with.
|
86
|
+
# Uncoment and edit the following line.
|
87
|
+
# username: email@domain.com
|
88
|
+
|
89
|
+
# The password to authenticate with.
|
90
|
+
# Leave the following line commented out to you the password stored in the Keychain.
|
91
|
+
# Uncomment and edit otherwise.
|
92
|
+
# password: somepassword
|
93
|
+
|
94
|
+
# The IP address or domain name of the IMAP server
|
95
|
+
# server: imap.gmail.com
|
96
|
+
|
97
|
+
# The port to connect to (defaults to the standard port for the type of server)
|
98
|
+
# port: 993
|
99
|
+
|
100
|
+
# Set to any value to use SSL encryption
|
101
|
+
# ssl: true
|
102
|
+
|
103
|
+
# Set to any value to use the LOGIN command instead of AUTHENTICATE.
|
104
|
+
# Some servers, like GMail, do not support AUTHENTICATE (IMAP only).
|
105
|
+
# use_login: true
|
106
|
+
|
107
|
+
# The name of the folder from which to read incoming mail (IMAP only). Defaults to INBOX.
|
108
|
+
# in_folder: Things
|
109
|
+
|
110
|
+
# The name a folder to move mail that causes an error during processing (IMAP only).
|
111
|
+
# error_folder: Things
|
112
|
+
|
113
|
+
# The tags to apply to the newly created TODOs.
|
114
|
+
# tag_names: Texted
|
115
|
+
|
116
|
+
# The list in Things to create TODOs in.
|
117
|
+
# list: Inbox
|
118
|
+
EOS
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '../fetcher'
|
2
|
+
require_relative 'mail_handler'
|
3
|
+
|
4
|
+
module ThingsFetcher
|
5
|
+
class Fetcher
|
6
|
+
def initialize(config)
|
7
|
+
super
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
fetcher = ::Fetcher.create(fetcher_options)
|
13
|
+
fetcher.fetch
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
attr_accessor :config
|
18
|
+
|
19
|
+
def fetcher_options
|
20
|
+
{
|
21
|
+
:type => :imap,
|
22
|
+
:receiver => handler,
|
23
|
+
:server => config[:server],
|
24
|
+
:port => config[:port],
|
25
|
+
:ssl => config[:ssl],
|
26
|
+
:use_login => config[:use_login],
|
27
|
+
:username => config[:username],
|
28
|
+
:password => config[:password],
|
29
|
+
:in_folder => config[:in_folder],
|
30
|
+
:error_folder => config[:error_folder]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def handler
|
35
|
+
ThingsFetcher::MailHandler.new(config)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'appscript'
|
2
|
+
require 'mail'
|
3
|
+
require 'osax'
|
4
|
+
|
5
|
+
module ThingsFetcher
|
6
|
+
class MailHandler
|
7
|
+
def initialize(config)
|
8
|
+
super
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def receive(data)
|
13
|
+
msg = Mail.read_from_string(data)
|
14
|
+
make_todo msg.subject
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
include Appscript
|
19
|
+
include OSAX
|
20
|
+
|
21
|
+
attr_accessor :config
|
22
|
+
|
23
|
+
def make_todo(name)
|
24
|
+
things = app("Things")
|
25
|
+
todo = things.make(:new => :to_do, :at => things.lists[config[:list]], :with_properties => { :name => name })
|
26
|
+
todo.tag_names.set config[:tag_names]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "things_fetcher/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "things-fetcher"
|
7
|
+
s.version = ThingsFetcher::VERSION
|
8
|
+
s.authors = ["Andrey Subbotin"]
|
9
|
+
s.email = ["andrey@subbotin.me"]
|
10
|
+
s.homepage = "http://github.com/eploko/things-fetcher"
|
11
|
+
s.summary = "Imports to-dos from IMAP to Things.app"
|
12
|
+
s.description = "A simple daemon to periodically check an IMAP folder and create a new to-dos in the Things app for each new message."
|
13
|
+
|
14
|
+
s.rubyforge_project = "things-fetcher"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.executables = ["things-fetcher"]
|
20
|
+
s.default_executable = ["things-fetcher"]
|
21
|
+
s.extra_rdoc_files = [
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc"
|
24
|
+
]
|
25
|
+
|
26
|
+
s.add_development_dependency "rake"
|
27
|
+
|
28
|
+
s.add_runtime_dependency "mail"
|
29
|
+
s.add_runtime_dependency "rb-appscript"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: things-fetcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andrey Subbotin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-19 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70233798421180 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70233798421180
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mail
|
27
|
+
requirement: &70233798420760 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70233798420760
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rb-appscript
|
38
|
+
requirement: &70233798420340 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70233798420340
|
47
|
+
description: A simple daemon to periodically check an IMAP folder and create a new
|
48
|
+
to-dos in the Things app for each new message.
|
49
|
+
email:
|
50
|
+
- andrey@subbotin.me
|
51
|
+
executables:
|
52
|
+
- things-fetcher
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- CHANGELOG
|
60
|
+
- Gemfile
|
61
|
+
- LICENSE
|
62
|
+
- README.rdoc
|
63
|
+
- Rakefile
|
64
|
+
- bin/things-fetcher
|
65
|
+
- lib/fetcher.rb
|
66
|
+
- lib/fetcher/base.rb
|
67
|
+
- lib/fetcher/imap.rb
|
68
|
+
- lib/fetcher/plain_imap.rb
|
69
|
+
- lib/keychain.rb
|
70
|
+
- lib/things_fetcher.rb
|
71
|
+
- lib/things_fetcher/config.rb
|
72
|
+
- lib/things_fetcher/fetcher.rb
|
73
|
+
- lib/things_fetcher/mail_handler.rb
|
74
|
+
- lib/things_fetcher/version.rb
|
75
|
+
- things-fetcher.gemspec
|
76
|
+
homepage: http://github.com/eploko/things-fetcher
|
77
|
+
licenses: []
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project: things-fetcher
|
96
|
+
rubygems_version: 1.8.10
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Imports to-dos from IMAP to Things.app
|
100
|
+
test_files: []
|