imap_to_rss 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.tar.gz.sig +1 -0
- data/.autotest +23 -0
- data/History.txt +6 -0
- data/Manifest.txt +11 -0
- data/README.txt +53 -0
- data/Rakefile +17 -0
- data/bin/imap_to_rss +6 -0
- data/lib/imap_to_rss.rb +201 -0
- data/lib/imap_to_rss/handler.rb +101 -0
- data/lib/imap_to_rss/handler/amazon.rb +259 -0
- data/lib/imap_to_rss/handler/hsbc.rb +62 -0
- data/lib/imap_to_rss/handler/ups.rb +34 -0
- metadata +132 -0
- metadata.gz.sig +2 -0
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
+������x�@fj/g�p_sFMUr�@�h ��,31��ލ�e�z���G�lf��zO�2�r�R�|�x��"�i�=z�U�6)��R��U�ڈ��dl��X�k<�B;m��ʖ�ua<e>q�=�>͐x�����i���;tC�q���$��Mb��ZAgy[h�ɖ�H�����">
|
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
= imap_to_rss
|
2
|
+
|
3
|
+
* http://seattlerb.rubyforge.org/imap_to_rss
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
IMAPToRSS turns messages on an IMAP server into RSS entries when the match a
|
8
|
+
handler. Included handlers work for email from Amazon, HSBC and UPS.
|
9
|
+
IMAPToRSS automatically loads handlers for any other mail.
|
10
|
+
|
11
|
+
== SYNOPSIS:
|
12
|
+
|
13
|
+
$ imap_to_rss --boxes INBOX --move _Money
|
14
|
+
[...]
|
15
|
+
$ open imap_to_rss.rss
|
16
|
+
|
17
|
+
See IMAPToRSS::Handler for instructions on writing a handler.
|
18
|
+
|
19
|
+
== REQUIREMENTS:
|
20
|
+
|
21
|
+
* An IMAP server
|
22
|
+
* imap_processor
|
23
|
+
* Email matching one of the handlers
|
24
|
+
|
25
|
+
== INSTALL:
|
26
|
+
|
27
|
+
* gem install imap_to_rss
|
28
|
+
* add imap_to_rss to your crontab
|
29
|
+
|
30
|
+
== LICENSE:
|
31
|
+
|
32
|
+
(The MIT License)
|
33
|
+
|
34
|
+
Copyright (c) 2009 Eric Hodel
|
35
|
+
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
37
|
+
a copy of this software and associated documentation files (the
|
38
|
+
'Software'), to deal in the Software without restriction, including
|
39
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
40
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
41
|
+
permit persons to whom the Software is furnished to do so, subject to
|
42
|
+
the following conditions:
|
43
|
+
|
44
|
+
The above copyright notice and this permission notice shall be
|
45
|
+
included in all copies or substantial portions of the Software.
|
46
|
+
|
47
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
48
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
49
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
50
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
51
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
52
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
53
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
$:.unshift 'lib'
|
6
|
+
require 'imap_to_rss'
|
7
|
+
|
8
|
+
Hoe.new 'imap_to_rss', IMAPToRSS::VERSION do |itor|
|
9
|
+
itor.rubyforge_name = 'seattlerb'
|
10
|
+
itor.developer 'Eric Hodel', 'drbrain@example.com'
|
11
|
+
|
12
|
+
itor.extra_deps << ['imap_processor', '~> 1.0']
|
13
|
+
itor.extra_deps << ['nokogiri', '~> 1.2']
|
14
|
+
itor.extra_deps << ['tmail', '~> 1.2']
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=Ruby
|
data/bin/imap_to_rss
ADDED
data/lib/imap_to_rss.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'imap_processor'
|
3
|
+
require 'tmail'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
##
|
7
|
+
# IMAPToRSS takes messages from your mailboxes, runs them through handlers,
|
8
|
+
# then turns them into an RSS feed. Handlers can be added via a plugin system
|
9
|
+
# so long as they subclass IMAPToRSS::Handler and live in the
|
10
|
+
# <tt>imap_to_rss/handler/</tt> directory.
|
11
|
+
|
12
|
+
class IMAPToRSS < IMAPProcessor
|
13
|
+
|
14
|
+
##
|
15
|
+
# The version of IMAPToRSS you are using
|
16
|
+
|
17
|
+
VERSION = '1.0'
|
18
|
+
|
19
|
+
##
|
20
|
+
# A Struct representing an RSS item for the RSS feed. Contains fields
|
21
|
+
# +title+, +description+, +author+, +pub_date+, +link+, +guid+, and
|
22
|
+
# +category+.
|
23
|
+
#
|
24
|
+
# Typically, the message id of the email can be used for the guid. When
|
25
|
+
# the RSS feed is generated, the guid is never used as a URL (isPermaLink is
|
26
|
+
# set to false).
|
27
|
+
|
28
|
+
RSSItem = Struct.new :title, :description, :author, :pub_date,
|
29
|
+
:link, :guid, :category
|
30
|
+
|
31
|
+
##
|
32
|
+
# All added RSS items
|
33
|
+
|
34
|
+
attr_reader :rss_items
|
35
|
+
|
36
|
+
##
|
37
|
+
# Processes command-line options
|
38
|
+
|
39
|
+
def self.process_args(args)
|
40
|
+
required_args = {
|
41
|
+
:Output => 'imap_to_rss.rss'
|
42
|
+
}
|
43
|
+
|
44
|
+
add_move
|
45
|
+
|
46
|
+
super __FILE__, args, required_args do |opts, options|
|
47
|
+
handlers = IMAPToRSS::Handler.handlers.map do |handler|
|
48
|
+
handler.name.split('::').last
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.separator ''
|
52
|
+
opts.separator "Handlers: #{handlers.join ', '}"
|
53
|
+
opts.separator ''
|
54
|
+
|
55
|
+
opts.on("--handler=HANDLER", handlers,
|
56
|
+
"Handler to run",
|
57
|
+
"Default: all handlers") do |handler|
|
58
|
+
options[:Handler] = handler
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--output=FILE",
|
62
|
+
"File to write the RSS feed to",
|
63
|
+
"Default: #{options[:Output]}",
|
64
|
+
"Options file name: File") do |file|
|
65
|
+
options[:Output] = file
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Creates a new IMAPToRSS and connects to the selected server.
|
72
|
+
|
73
|
+
def initialize(options)
|
74
|
+
super
|
75
|
+
|
76
|
+
@handlers = []
|
77
|
+
@rss_items = []
|
78
|
+
@output = options[:Output]
|
79
|
+
|
80
|
+
connection = connect options[:Host], options[:Port], options[:SSL],
|
81
|
+
options[:Username], options[:Password], options[:Auth]
|
82
|
+
|
83
|
+
@imap = connection.imap
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Builds an RSS feed from rss_items
|
88
|
+
|
89
|
+
def build_rss
|
90
|
+
log 'Building RSS feed'
|
91
|
+
rss = Nokogiri::XML::Builder.new
|
92
|
+
|
93
|
+
rss_items = @rss_items.sort_by { |rss_item| rss_item.pub_date }
|
94
|
+
host = options[:Host]
|
95
|
+
|
96
|
+
copyover = []
|
97
|
+
|
98
|
+
if File.exist? @output then
|
99
|
+
doc = nil
|
100
|
+
|
101
|
+
open @output, 'rb' do |io| doc = Nokogiri::XML io end
|
102
|
+
|
103
|
+
copyover_count = 50 - rss_items.length
|
104
|
+
|
105
|
+
if copyover_count > 0 then
|
106
|
+
items = doc.xpath('//rss/channel/item')
|
107
|
+
|
108
|
+
index = [copyover_count, items.length].min
|
109
|
+
|
110
|
+
copyover = items.to_a[-index..-1]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
rss.rss :version => '2.0' do
|
115
|
+
rss.channel do
|
116
|
+
rss.title 'IMAP to RSS'
|
117
|
+
rss.description "An RSS feed built from your IMAP server"
|
118
|
+
rss.generator "imap_to_rss version #{IMAPToRSS::VERSION}"
|
119
|
+
rss.docs 'http://cyber.law.harvard.edu/rss/rss.html'
|
120
|
+
|
121
|
+
copyover.each do |item|
|
122
|
+
rss.send :insert, item
|
123
|
+
end
|
124
|
+
|
125
|
+
rss_items.each do |item|
|
126
|
+
rss.item do
|
127
|
+
rss.title item.title
|
128
|
+
rss.description item.description
|
129
|
+
rss.author item.author
|
130
|
+
rss.pubDate item.pub_date.rfc822
|
131
|
+
rss.link item.link if item.link
|
132
|
+
rss.guid item.guid, :isPermaLink => false if item.guid
|
133
|
+
rss.category item.category if item.category
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
open @output, 'w' do |io|
|
140
|
+
io.write rss.to_xml
|
141
|
+
end
|
142
|
+
|
143
|
+
log 'Saved RSS feed'
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Processes mailboxes with each handler then writes out the RSS file.
|
148
|
+
|
149
|
+
def run
|
150
|
+
handlers = IMAPToRSS::Handler.handlers
|
151
|
+
|
152
|
+
handlers.delete_if do |handler|
|
153
|
+
handler.name.split('::').last != options[:Handler]
|
154
|
+
end if options[:Handler]
|
155
|
+
|
156
|
+
handlers = handlers.map do |handler|
|
157
|
+
handler = handler.new
|
158
|
+
handler.setup self
|
159
|
+
handler
|
160
|
+
end
|
161
|
+
|
162
|
+
dest_mailbox = options[:MoveTo]
|
163
|
+
|
164
|
+
@boxes.each do |mailbox|
|
165
|
+
@imap.select mailbox
|
166
|
+
log "Selected mailbox #{mailbox}"
|
167
|
+
|
168
|
+
handlers.each do |handler|
|
169
|
+
log "Running handler #{handler.search.join ' '}"
|
170
|
+
messages = @imap.search [
|
171
|
+
'NOT', 'DELETED',
|
172
|
+
'NOT', 'KEYWORD', 'IMAP_TO_RSS',
|
173
|
+
*handler.search
|
174
|
+
]
|
175
|
+
|
176
|
+
next if messages.empty?
|
177
|
+
|
178
|
+
handled = handler.handle messages
|
179
|
+
|
180
|
+
@imap.store handled, '+FLAGS', %w[IMAP_TO_RSS]
|
181
|
+
|
182
|
+
if dest_mailbox then
|
183
|
+
@imap.copy handled, dest_mailbox
|
184
|
+
@imap.store handled, '+FLAGS', [:Deleted]
|
185
|
+
@imap.expunge
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
build_rss
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
plugins = Gem.find_files 'imap_to_rss/handler/*'
|
196
|
+
|
197
|
+
plugins.each do |plugin|
|
198
|
+
# strip path info to always require latest
|
199
|
+
require plugin.sub(/.*(imap_to_rss.handler.*)\.[^.]+$/, '\1')
|
200
|
+
end
|
201
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'imap_to_rss'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Base message handler class. Subclass this to define your own handlers, and
|
6
|
+
# override the #initialize and #handle methods.
|
7
|
+
#
|
8
|
+
# To have the handler automatically be picked up by IMAPToRSS, place it in the
|
9
|
+
# <tt>imap_to_rss/handler/</tt> directory.
|
10
|
+
|
11
|
+
class IMAPToRSS::Handler
|
12
|
+
|
13
|
+
##
|
14
|
+
# IMAP SEARCH command keywords to search
|
15
|
+
|
16
|
+
attr_accessor :search
|
17
|
+
|
18
|
+
@handlers = []
|
19
|
+
|
20
|
+
##
|
21
|
+
# Collect handler subclasses
|
22
|
+
|
23
|
+
def self.inherited(subclass)
|
24
|
+
@handlers << subclass
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# List of found handlers
|
29
|
+
|
30
|
+
def self.handlers
|
31
|
+
@handlers
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# :method: initialize
|
36
|
+
#
|
37
|
+
# Override and set <tt>@search</tt> to IMAP search terms for the messages
|
38
|
+
# you're interested in. (Usually something simple like 'FROM', 'amazon' is
|
39
|
+
# enough.) Deleted messages and previously handled messages will be
|
40
|
+
# automatically ignored.
|
41
|
+
#
|
42
|
+
# You can tell imap_to_rss to re-scan messages by clearing the IMAP_TO_RSS
|
43
|
+
# keyword with imap_keywords:
|
44
|
+
#
|
45
|
+
# imap_keywords --no-list --keywords IMAP_TO_RSS --delete
|
46
|
+
|
47
|
+
##
|
48
|
+
# Adds an item to the RSS feed with given parts
|
49
|
+
|
50
|
+
def add_item(title, description, author, pub_date, link = nil, guid = nil,
|
51
|
+
category = nil)
|
52
|
+
pub_date = case pub_date
|
53
|
+
when Time then
|
54
|
+
pub_date
|
55
|
+
when Date, DateTime then
|
56
|
+
Time.parse pub_date.to_s
|
57
|
+
else
|
58
|
+
Time.parse pub_date
|
59
|
+
end
|
60
|
+
|
61
|
+
item = IMAPToRSS::RSSItem.new title, description, author, pub_date, link,
|
62
|
+
guid, category
|
63
|
+
|
64
|
+
@itor.rss_items << item
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Delegates to IMAPToRSS#each_message
|
69
|
+
|
70
|
+
def each_message(*args, &block)
|
71
|
+
@itor.each_message(*args, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Guts of the handler, implement this yourself. It should call #add_item
|
76
|
+
# for each message found.
|
77
|
+
#
|
78
|
+
# See IMAPToRSS::Handler::UPS for a simple handler,
|
79
|
+
# IMAPToRSS::Handler::Amazon for a complex one.
|
80
|
+
|
81
|
+
def handle(uids)
|
82
|
+
raise NotImplementedError, 'write me'
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Delegates to IMAPToRSS
|
87
|
+
|
88
|
+
def log(*args)
|
89
|
+
@itor.log(*args)
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Sets up delegators to IMAPToRSS
|
94
|
+
|
95
|
+
def setup(imap_to_rss)
|
96
|
+
@itor = imap_to_rss
|
97
|
+
@imap = imap_to_rss.imap
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'imap_to_rss/handler'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Handles messages from Amazon
|
5
|
+
|
6
|
+
class IMAPToRSS::Handler::Amazon < IMAPToRSS::Handler
|
7
|
+
|
8
|
+
##
|
9
|
+
# Selects messages with amazon in the From header
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@search = 'FROM', 'amazon'
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Turns +uids+ into RSSItems
|
17
|
+
|
18
|
+
def handle(uids)
|
19
|
+
each_message uids, 'text/plain' do |uid, mail|
|
20
|
+
@mail = mail
|
21
|
+
|
22
|
+
case @mail.subject
|
23
|
+
when /^Your Order with Amazon.com/ then
|
24
|
+
if @mail.body =~ /Your purchase has been divided into/ then
|
25
|
+
@mail.body.split(/^Order #\d+/).each do |order|
|
26
|
+
add_order order
|
27
|
+
end
|
28
|
+
else
|
29
|
+
add_order @mail.body
|
30
|
+
end
|
31
|
+
when /has shipped!$/ then
|
32
|
+
order_shipped_bang
|
33
|
+
when /has shipped/ then
|
34
|
+
order_shipped
|
35
|
+
when /^Amazon.com - Order Revision \((.*?)\)/ then
|
36
|
+
order_revision $1
|
37
|
+
when /^Amazon.com - Your Cancellation/ then
|
38
|
+
order_cancellation
|
39
|
+
when /sent you an Amazon.com Gift Card!$/ then
|
40
|
+
gift_card
|
41
|
+
when /^Amazon Web Services Billing Statement Available/ then
|
42
|
+
aws_bill
|
43
|
+
when /^Your savings from Amazon.com/, # ignore
|
44
|
+
/^Your Amazon.com Purchase from/ then # dup of regular order email
|
45
|
+
next
|
46
|
+
else
|
47
|
+
log "Unknown Subject: %p" % @mail.subject
|
48
|
+
next
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Adds an RSS item with +subject+, +description+ and +url+
|
55
|
+
|
56
|
+
def add_item(subject, description, url)
|
57
|
+
super subject, description, @mail.from, @mail.date, url, @mail.message_id,
|
58
|
+
'Amazon'
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Adds an RSS item from order +order+
|
63
|
+
|
64
|
+
def add_order(order)
|
65
|
+
items = order.scan(/^(\d+) "(.*?)"/)
|
66
|
+
|
67
|
+
order =~ /Order number:\s+([\d-]+)/
|
68
|
+
return unless $1
|
69
|
+
order_number = $1
|
70
|
+
url = order_url order_number
|
71
|
+
|
72
|
+
order =~ /^Total for this Order:\s+(.*)/
|
73
|
+
total = $1.strip
|
74
|
+
|
75
|
+
subject = "Amazon order #{order_number}"
|
76
|
+
|
77
|
+
description = "<p>Total: #{total}\n"
|
78
|
+
|
79
|
+
description << order_table(items)
|
80
|
+
|
81
|
+
add_item subject, description, url
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Adds an RSS item for an AWS bill
|
86
|
+
|
87
|
+
def aws_bill
|
88
|
+
@mail.body =~ /^Total: (.*)/
|
89
|
+
total = $1
|
90
|
+
|
91
|
+
@mail.body =~ /^(http.*)/
|
92
|
+
|
93
|
+
add_item "Amazon Web Services Bill: #{total}", '', $1
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Adds an RSS item for a gift card
|
98
|
+
|
99
|
+
def gift_card
|
100
|
+
url = "https://www.amazon.com/gp/css/account/payment/view-gc-balance.html"
|
101
|
+
@mail.body =~ /^Amount: (.*)/
|
102
|
+
amount = $1
|
103
|
+
|
104
|
+
@mail.body =~ /^From: (.*)/
|
105
|
+
from = $1
|
106
|
+
|
107
|
+
@mail.body =~ /^Gift Message: (.*)/
|
108
|
+
message = $1
|
109
|
+
|
110
|
+
@mail.body =~ /^Claim code (.*)/
|
111
|
+
claim_code = $1
|
112
|
+
|
113
|
+
subject = "Amazon Gift Card from #{from} - #{amount}!"
|
114
|
+
|
115
|
+
description = "<p><strong>#{from} send you a #{amount} gift card!</strong>\n\n"
|
116
|
+
description << "<p>#{message}\n\n"
|
117
|
+
description << "<h2>#{claim_code}</h2>\n\n"
|
118
|
+
description << "<p><a href=\"#{url}\">Claim your gift card</a>"
|
119
|
+
|
120
|
+
add_item subject, description, url
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Adds an RSS item for an order cancellation
|
125
|
+
|
126
|
+
def order_cancellation
|
127
|
+
@mail.body =~ /order #(.*?) /
|
128
|
+
order_number = $1
|
129
|
+
|
130
|
+
items = @mail.body.scan(/(\d?) of (.*)/)
|
131
|
+
|
132
|
+
url = order_url order_number
|
133
|
+
subject = "Amazon order cancelation #{order_number}"
|
134
|
+
description = "<p>You canceled your order:\n\n"
|
135
|
+
|
136
|
+
description << order_table(items)
|
137
|
+
|
138
|
+
add_item subject, description, url
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Adds an RSS item for a shipped order
|
143
|
+
|
144
|
+
def order_shipped
|
145
|
+
@mail.body =~ /^(The following items .*?:\r\n.*?)(Shipping Carrier|Item Subtotal)/m
|
146
|
+
items = $1.map do |item|
|
147
|
+
next unless item =~ /\d+\s(.*?)\$[\d.]+\s+(\d+)/
|
148
|
+
[$2, $1.strip]
|
149
|
+
end.compact
|
150
|
+
|
151
|
+
carrier = $1.strip if @mail.body =~ /Shipping Carrier: (.*)/
|
152
|
+
date = $1.strip if @mail.body =~ /Ship Date: (.*)/
|
153
|
+
speed = $1.strip if @mail.body =~ /Shipping Speed: (.*)/
|
154
|
+
tracking_number = $1.strip if @mail.body =~ /Carrier Tracking ID: (.*)/
|
155
|
+
|
156
|
+
@mail.body =~ /Your shipping address:\r\n\r\n(.*?)\r\n\r\n/m
|
157
|
+
address = $1.split("\n").map { |line| line.strip }.join "<br />\n" if $1
|
158
|
+
|
159
|
+
if tracking_number then
|
160
|
+
url = case carrier
|
161
|
+
when 'USPS' then
|
162
|
+
"http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?strOrigTrackNum=#{tracking_number}"
|
163
|
+
when 'FedEx' then
|
164
|
+
"http://fedex.com/Tracking?tracknumbers=#{tracking_number}"
|
165
|
+
else
|
166
|
+
log "Unknown carrier: %p" % carrier
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
subject = @mail.subject
|
172
|
+
|
173
|
+
description = order_table items
|
174
|
+
if url then
|
175
|
+
description << "<p>Via <a href=\"#{url}\">#{carrier}</a>\n\n"
|
176
|
+
elsif carrier then
|
177
|
+
description << "<p>Via #{carrier} (no tracking number found)\n\n"
|
178
|
+
end
|
179
|
+
description << "<p>To:<br>\n#{address}" if address
|
180
|
+
|
181
|
+
add_item subject, description, url
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Adds an RSS item for a shipped order (alternate subject ending with !)
|
186
|
+
|
187
|
+
def order_shipped_bang
|
188
|
+
@mail.body =~ /this shipment:\r\n\r\n(.*?)\r\n\r\nShip/m
|
189
|
+
items = $1.scan(/(\d+) of (.*)/)
|
190
|
+
|
191
|
+
carrier = $1.strip if @mail.body =~ /Shipped via (.*?)\s/
|
192
|
+
tracking_number = $1.strip if @mail.body =~ /Tracking number: (.*?)\s/
|
193
|
+
|
194
|
+
@mail.body =~ /This shipment was sent to:\r\n\r\n(.*?)\r\n\r\n/m
|
195
|
+
address = $1.split("\n").map { |line| line.strip }.join "<br />\n" if $1
|
196
|
+
|
197
|
+
if tracking_number then
|
198
|
+
url = case carrier
|
199
|
+
when 'USPS' then
|
200
|
+
"http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?strOrigTrackNum=#{tracking_number}"
|
201
|
+
when 'FedEx' then
|
202
|
+
"http://fedex.com/Tracking?tracknumbers=#{tracking_number}"
|
203
|
+
else
|
204
|
+
log "Unknown carrier: %p" % carrier
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
subject = @mail.subject
|
210
|
+
|
211
|
+
description = order_table items
|
212
|
+
|
213
|
+
if url then
|
214
|
+
description << "<p>Via <a href=\"#{url}\">#{carrier}</a>\n\n"
|
215
|
+
elsif carrier then
|
216
|
+
description << "<p>Via #{carrier} (no tracking number found)\n\n"
|
217
|
+
end
|
218
|
+
description << "<p>To:<br>\n#{address}" if address
|
219
|
+
|
220
|
+
add_item subject, description, url
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Adds an RSS item for an order revision on +order_number+
|
225
|
+
|
226
|
+
def order_revision(order_number)
|
227
|
+
url = order_url order_number
|
228
|
+
subject = "Order Revision (#{order_number})"
|
229
|
+
|
230
|
+
@mail.body =~ /^Dear .*?,\r\n\r\n(.*?)\r\n\r\n/m
|
231
|
+
|
232
|
+
description = "<p>#{$1}"
|
233
|
+
|
234
|
+
add_item subject, description, url
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# Creates an HTML table for +items+
|
239
|
+
|
240
|
+
def order_table(items)
|
241
|
+
table = "<table>\n<tr><th>Quantity<th>Description\n"
|
242
|
+
|
243
|
+
items.each do |qty, desc|
|
244
|
+
table << "<tr><td>#{qty}<td>#{desc.strip}\n"
|
245
|
+
end
|
246
|
+
table << "</table>\n\n"
|
247
|
+
|
248
|
+
table
|
249
|
+
end
|
250
|
+
|
251
|
+
##
|
252
|
+
# Returns the link for order +order_number+
|
253
|
+
|
254
|
+
def order_url(order_number)
|
255
|
+
"https://www.amazon.com/gp/css/summary/edit.html?ie=UTF8&orderID=#{order_number}"
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'imap_to_rss/handler'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Handles messages from HSBC savings and HSBC credit cards
|
5
|
+
|
6
|
+
class IMAPToRSS::Handler::HSBC < IMAPToRSS::Handler
|
7
|
+
|
8
|
+
##
|
9
|
+
# Selects messages with hsbc in the From header
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@search = 'FROM', 'hsbc'
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Turns +uids+ into RSS items for bank-to-bank transfers and general
|
17
|
+
# announcements like CC payments due or interest rate changes.
|
18
|
+
|
19
|
+
def handle(uids)
|
20
|
+
each_message uids, 'text/plain' do |uid, mail|
|
21
|
+
url = nil
|
22
|
+
description = nil
|
23
|
+
|
24
|
+
case mail.from.first
|
25
|
+
when 'HSBC@email.hsbcusa.com' then
|
26
|
+
mail.body =~ /^Dear.*?,
|
27
|
+
([ \t]*\r?\n){2,}
|
28
|
+
(.*?)
|
29
|
+
([ \t]*\r?\n){2,}/mx
|
30
|
+
|
31
|
+
next unless $2
|
32
|
+
|
33
|
+
description = $2
|
34
|
+
when 'A2ATransfer@us.hsbc.com' then
|
35
|
+
mail.body =~ /^Dear.*?[,:]
|
36
|
+
([ \t]*\r?\n){2,}
|
37
|
+
(.*?)
|
38
|
+
([ \t]*\r?\n){2,}
|
39
|
+
Sincerely,/mx
|
40
|
+
|
41
|
+
body = $2
|
42
|
+
body.gsub!(/[*\r]/, '')
|
43
|
+
body.gsub!(/[ \t]*\n/, "\n")
|
44
|
+
body = body.split(/\n\n+/).map { |para| "<p>#{para}</p>" }
|
45
|
+
|
46
|
+
description = body.join "\n\n"
|
47
|
+
when 'alerts@email.hsbcusa.com' then
|
48
|
+
mail.body =~ /^(http:.*)/
|
49
|
+
|
50
|
+
url = $1
|
51
|
+
else
|
52
|
+
log "Unknown From: #{mail.from.join ', '}"
|
53
|
+
next
|
54
|
+
end
|
55
|
+
|
56
|
+
add_item mail.subject, description, mail.from, mail.date, url,
|
57
|
+
mail.message_id, 'HSBC'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'imap_to_rss/handler'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Turns messages from UPS into links to the tracking page
|
5
|
+
|
6
|
+
class IMAPToRSS::Handler::UPS < IMAPToRSS::Handler
|
7
|
+
|
8
|
+
##
|
9
|
+
# Selects messages with ups in the From header
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@search = 'FROM', 'ups'
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Scans +uids+ for UPS tracking numbers and turns them into RSS items
|
17
|
+
|
18
|
+
def handle(uids)
|
19
|
+
each_message uids, 'text/plain' do |uid, mail|
|
20
|
+
mail.body =~ /Tracking Number:\s+(\w+)/
|
21
|
+
|
22
|
+
tracking_number = $1
|
23
|
+
|
24
|
+
url = "http://wwwapps.ups.com/WebTracking/processRequest?tracknum=#{tracking_number}"
|
25
|
+
|
26
|
+
description = %{Package shipped: <a href="#{url}">#{tracking_number}</a>}
|
27
|
+
|
28
|
+
add_item mail.subject, description, mail.from, mail.date, url,
|
29
|
+
mail.message_id, 'UPS'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: imap_to_rss
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Hodel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
|
14
|
+
YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
|
15
|
+
ZXQwHhcNMDcxMjIxMDIwNDE0WhcNMDgxMjIwMDIwNDE0WjBBMRAwDgYDVQQDDAdk
|
16
|
+
cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
|
17
|
+
FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
|
18
|
+
LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
|
19
|
+
U5ddZCVywn5nnAQ+Ui7jMW54CYt5/H6f2US6U0hQOjJR6cpfiymgxGdfyTiVcvTm
|
20
|
+
Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
|
21
|
+
mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
|
22
|
+
g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
|
23
|
+
sCANiQ8BAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
24
|
+
BBS5k4Z75VSpdM0AclG2UvzFA/VW5DANBgkqhkiG9w0BAQUFAAOCAQEAHagT4lfX
|
25
|
+
kP/hDaiwGct7XPuVGbrOsKRVD59FF5kETBxEc9UQ1clKWngf8JoVuEoKD774dW19
|
26
|
+
bU0GOVWO+J6FMmT/Cp7nuFJ79egMf/gy4gfUfQMuvfcr6DvZUPIs9P/TlK59iMYF
|
27
|
+
DIOQ3DxdF3rMzztNUCizN4taVscEsjCcgW6WkUJnGdqlu3OHWpQxZBJkBTjPCoc6
|
28
|
+
UW6on70SFPmAy/5Cq0OJNGEWBfgD9q7rrs/X8GGwUWqXb85RXnUVi/P8Up75E0ag
|
29
|
+
14jEc90kN+C7oI/AGCBN0j6JnEtYIEJZibjjDJTSMWlUKKkj30kq7hlUC2CepJ4v
|
30
|
+
x52qPcexcYZR7w==
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
|
33
|
+
date: 2009-05-15 00:00:00 -07:00
|
34
|
+
default_executable:
|
35
|
+
dependencies:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: imap_processor
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "1.0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: nokogiri
|
48
|
+
type: :runtime
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "1.2"
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: tmail
|
58
|
+
type: :runtime
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ~>
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "1.2"
|
65
|
+
version:
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: hoe
|
68
|
+
type: :development
|
69
|
+
version_requirement:
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.12.1
|
75
|
+
version:
|
76
|
+
description: |-
|
77
|
+
IMAPToRSS turns messages on an IMAP server into RSS entries when the match a
|
78
|
+
handler. Included handlers work for email from Amazon, HSBC and UPS.
|
79
|
+
IMAPToRSS automatically loads handlers for any other mail.
|
80
|
+
email:
|
81
|
+
- drbrain@example.com
|
82
|
+
executables:
|
83
|
+
- imap_to_rss
|
84
|
+
extensions: []
|
85
|
+
|
86
|
+
extra_rdoc_files:
|
87
|
+
- History.txt
|
88
|
+
- Manifest.txt
|
89
|
+
- README.txt
|
90
|
+
files:
|
91
|
+
- .autotest
|
92
|
+
- History.txt
|
93
|
+
- Manifest.txt
|
94
|
+
- README.txt
|
95
|
+
- Rakefile
|
96
|
+
- bin/imap_to_rss
|
97
|
+
- lib/imap_to_rss.rb
|
98
|
+
- lib/imap_to_rss/handler.rb
|
99
|
+
- lib/imap_to_rss/handler/amazon.rb
|
100
|
+
- lib/imap_to_rss/handler/hsbc.rb
|
101
|
+
- lib/imap_to_rss/handler/ups.rb
|
102
|
+
has_rdoc: true
|
103
|
+
homepage: http://seattlerb.rubyforge.org/imap_to_rss
|
104
|
+
licenses: []
|
105
|
+
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options:
|
108
|
+
- --main
|
109
|
+
- README.txt
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: "0"
|
117
|
+
version:
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: "0"
|
123
|
+
version:
|
124
|
+
requirements: []
|
125
|
+
|
126
|
+
rubyforge_project: seattlerb
|
127
|
+
rubygems_version: 1.3.3
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: IMAPToRSS turns messages on an IMAP server into RSS entries when the match a handler
|
131
|
+
test_files: []
|
132
|
+
|
metadata.gz.sig
ADDED