things-fetcher 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|