ishapi 0.1.8.301 → 0.1.8.303
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.
- checksums.yaml +4 -4
- data/app/jobs/ishapi/email_message_intake_job.rb +154 -214
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: deb042effc76a3bac981e7d95c5ccbac9c1fe067b5afb6799f273a2fb061ba67
|
4
|
+
data.tar.gz: a497b8b725895391c965abe2680adb51d739958ca36172615263aa3fd6b83703
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 206d685ea05a79c226a49d888e1ba2ced49e5ff32343999fe1a00e42c65eb8f139e17cc1dcb2bd6d0e7acbb83c257ef55205d75f318631c353a3aef22b6db143
|
7
|
+
data.tar.gz: ceebf0b9f54feb97eeac59cc655d65c12fc10957431d4821bf472e18822d97d80be31b0407f914a2a94e63119c62a5cf8a743e9f96556f088c481e90fbad3eff
|
@@ -9,243 +9,183 @@ class Ishapi::EmailMessageIntakeJob < Ishapi::ApplicationJob
|
|
9
9
|
|
10
10
|
queue_as :default
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
## Content Types:
|
17
|
-
# "application/pdf; name=\"Securities Forward Agreement -- HaulHub Inc -- Victor Pudeyev -- 2021-10-26.docx.pdf\""
|
18
|
-
# "image/jpeg; name=TX_DL_2.jpg"
|
19
|
-
# "image/png; name=image005.png"
|
20
|
-
# "multipart/alternative; boundary=_000_BL0PR10MB2913C560ADE059F0AB3A6D11829A9BL0PR10MB2913namp_",
|
21
|
-
# "text/html; charset=utf-8"
|
22
|
-
# "text/plain; charset=UTF-8"
|
23
|
-
# "text/calendar; charset=utf-8; method=REQUEST"
|
24
|
-
def churn_subpart message, part
|
25
|
-
if part.content_disposition&.include?('attachment')
|
26
|
-
## @TODO: attachments !
|
27
|
-
;
|
28
|
-
else
|
29
|
-
if part.content_type.include?("multipart/related") ||
|
30
|
-
part.content_type.include?("multipart/alternative")
|
31
|
-
|
32
|
-
part.parts.each do |subpart|
|
33
|
-
churn_subpart( message, subpart )
|
34
|
-
end
|
35
|
-
else
|
36
|
-
attachment = Office::EmailAttachment.new({
|
37
|
-
content: part.decoded,
|
38
|
-
content_type: part.content_type,
|
39
|
-
email_message: message,
|
40
|
-
})
|
41
|
-
attachment.save
|
12
|
+
=begin
|
13
|
+
object_key = 'bg57j6u0j38b5ts86635fkqtjlucn5tvrm2hea81'
|
14
|
+
MsgStub.where({ object_key: object_key }).delete
|
42
15
|
|
43
|
-
|
44
|
-
|
16
|
+
stub = MsgStub.create({ object_key: object_key })
|
17
|
+
id = stub.id
|
18
|
+
=end
|
19
|
+
def perform id
|
20
|
+
stub = ::Office::EmailMessageStub.find id
|
21
|
+
if !Rails.env.test?
|
22
|
+
puts "Performing EmailMessageIntakeJob for object_key #{stub.object_key}"
|
23
|
+
end
|
24
|
+
if stub.state != ::Office::EmailMessageStub::STATE_PENDING
|
25
|
+
raise "This stub has already been processed: #{stub.id.to_s}."
|
26
|
+
return
|
27
|
+
end
|
28
|
+
client = Aws::S3::Client.new({
|
29
|
+
region: ::S3_CREDENTIALS[:region_ses],
|
30
|
+
access_key_id: ::S3_CREDENTIALS[:access_key_id_ses],
|
31
|
+
secret_access_key: ::S3_CREDENTIALS[:secret_access_key_ses],
|
32
|
+
})
|
45
33
|
|
46
|
-
|
47
|
-
|
34
|
+
_mail = client.get_object( bucket: ::S3_CREDENTIALS[:bucket_ses], key: stub.object_key ).body.read
|
35
|
+
the_mail = Mail.new(_mail)
|
36
|
+
message_id = the_mail.header['message-id'].decoded
|
37
|
+
in_reply_to_id = the_mail.header['in-reply-to']&.to_s
|
38
|
+
email_inbox_tag_id = WpTag.emailtag(WpTag::INBOX).id
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
;
|
53
|
-
elsif part.content_type.include?("image/jpeg")
|
54
|
-
;
|
55
|
-
elsif part.content_type.include?("image/png")
|
56
|
-
;
|
40
|
+
if !the_mail.to
|
41
|
+
the_mail.to = [ 'NO-RECIPIENT' ]
|
42
|
+
end
|
57
43
|
|
58
|
-
else
|
59
|
-
puts! part.content_type, '444 No action for a part with this content_type'
|
60
44
|
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
45
|
+
subject = ::Msg.strip_emoji the_mail.subject
|
46
|
+
subject ||= '(wco no subject)'
|
65
47
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
clean = ""
|
48
|
+
@message = ::Office::EmailMessage.where( message_id: message_id ).first
|
49
|
+
@message ||= ::Office::EmailMessage.create({
|
50
|
+
raw: _mail,
|
70
51
|
|
71
|
-
|
72
|
-
|
73
|
-
clean = text.gsub regex, ""
|
52
|
+
message_id: message_id,
|
53
|
+
in_reply_to_id: in_reply_to_id,
|
74
54
|
|
75
|
-
|
76
|
-
|
77
|
-
clean = clean.gsub regex, ""
|
55
|
+
object_key: stub.object_key,
|
56
|
+
# object_path: stub.object_path,
|
78
57
|
|
79
|
-
|
80
|
-
|
81
|
-
clean = clean.gsub regex, ""
|
58
|
+
subject: subject,
|
59
|
+
date: the_mail.date,
|
82
60
|
|
83
|
-
|
84
|
-
|
85
|
-
clean = clean.gsub regex, ""
|
86
|
-
end
|
61
|
+
from: the_mail.from ? the_mail.from[0] : "nobody@unknown-doma.in",
|
62
|
+
froms: the_mail.from,
|
87
63
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
return
|
96
|
-
end
|
97
|
-
client = Aws::S3::Client.new({
|
98
|
-
region: ::S3_CREDENTIALS[:region_ses],
|
99
|
-
access_key_id: ::S3_CREDENTIALS[:access_key_id_ses],
|
100
|
-
secret_access_key: ::S3_CREDENTIALS[:secret_access_key_ses],
|
101
|
-
})
|
102
|
-
|
103
|
-
_mail = client.get_object( bucket: ::S3_CREDENTIALS[:bucket_ses], key: stub.object_key ).body.read
|
104
|
-
the_mail = Mail.new(_mail)
|
105
|
-
message_id = the_mail.header['message-id'].decoded
|
106
|
-
in_reply_to_id = the_mail.header['in-reply-to']&.to_s
|
107
|
-
email_inbox_tag_id = WpTag.emailtag(WpTag::INBOX).id
|
108
|
-
|
109
|
-
if !the_mail.to
|
110
|
-
the_mail.to = [ 'NO-RECIPIENT' ]
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
subject = strip_emoji the_mail.subject
|
115
|
-
subject ||= '(wco no subject)'
|
116
|
-
|
117
|
-
@message = ::Office::EmailMessage.where( message_id: message_id ).first
|
118
|
-
@message ||= ::Office::EmailMessage.create({
|
119
|
-
raw: _mail,
|
120
|
-
|
121
|
-
message_id: message_id,
|
122
|
-
in_reply_to_id: in_reply_to_id,
|
123
|
-
|
124
|
-
object_key: stub.object_key,
|
125
|
-
# object_path: stub.object_path,
|
126
|
-
|
127
|
-
subject: subject,
|
128
|
-
date: the_mail.date,
|
129
|
-
|
130
|
-
from: the_mail.from ? the_mail.from[0] : "nobody@unknown-doma.in",
|
131
|
-
froms: the_mail.from,
|
132
|
-
|
133
|
-
to: the_mail.to ? the_mail.to[0] : nil,
|
134
|
-
tos: the_mail.to,
|
135
|
-
|
136
|
-
cc: the_mail.cc ? the_mail.cc[0] : nil,
|
137
|
-
ccs: the_mail.cc,
|
138
|
-
|
139
|
-
# bccs: the_mail.bcc,
|
140
|
-
})
|
141
|
-
if !@message.persisted?
|
142
|
-
puts! @message.errors.full_messages, "Could not create email_message"
|
143
|
-
end
|
144
|
-
if the_mail.body.preamble.present?
|
145
|
-
@message.preamble = the_mail.body.preamble
|
146
|
-
end
|
147
|
-
if the_mail.body.epilogue.present?
|
148
|
-
@message.epilogue = the_mail.body.epilogue
|
149
|
-
end
|
150
|
-
|
151
|
-
## Parts
|
152
|
-
the_mail.parts.each do |part|
|
153
|
-
churn_subpart( @message, part )
|
154
|
-
end
|
155
|
-
|
156
|
-
if the_mail.parts.length == 0
|
157
|
-
body = the_mail.body.decoded.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
158
|
-
if the_mail.content_type&.include?('text/html')
|
159
|
-
@message.part_html = body
|
160
|
-
elsif the_mail.content_type&.include?('text/plain')
|
161
|
-
@message.part_txt = body
|
162
|
-
elsif the_mail.content_type.blank?
|
163
|
-
@message.part_txt = body
|
164
|
-
else
|
165
|
-
throw "mail body of unknown type: #{the_mail.content_type}"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
## Attachments
|
170
|
-
the_mail.attachments.each do |att|
|
171
|
-
photo = Photo.new({
|
172
|
-
content_type: att.content_type.split(';')[0],
|
173
|
-
original_filename: att.content_type_parameters[:name],
|
174
|
-
image_data: att.body.encoded,
|
175
|
-
email_message_id: @message.id,
|
64
|
+
to: the_mail.to ? the_mail.to[0] : nil,
|
65
|
+
tos: the_mail.to,
|
66
|
+
|
67
|
+
cc: the_mail.cc ? the_mail.cc[0] : nil,
|
68
|
+
ccs: the_mail.cc,
|
69
|
+
|
70
|
+
# bccs: the_mail.bcc,
|
176
71
|
})
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
72
|
+
if !@message.persisted?
|
73
|
+
puts! @message.errors.full_messages, "Could not create email_message"
|
74
|
+
end
|
75
|
+
if the_mail.body.preamble.present?
|
76
|
+
@message.preamble = the_mail.body.preamble
|
77
|
+
end
|
78
|
+
if the_mail.body.epilogue.present?
|
79
|
+
@message.epilogue = the_mail.body.epilogue
|
80
|
+
end
|
81
|
+
|
82
|
+
## Parts
|
83
|
+
the_mail.parts.each do |part|
|
84
|
+
@message.churn_subpart( part )
|
85
|
+
end
|
86
|
+
|
87
|
+
if the_mail.parts.length == 0
|
88
|
+
body = the_mail.body.decoded.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
89
|
+
if the_mail.content_type&.include?('text/html')
|
90
|
+
@message.part_html = body
|
91
|
+
elsif the_mail.content_type&.include?('text/plain')
|
92
|
+
@message.part_txt = body
|
93
|
+
elsif the_mail.content_type.blank?
|
94
|
+
@message.part_txt = body
|
95
|
+
else
|
96
|
+
throw "mail body of unknown type: #{the_mail.content_type}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
## Attachments
|
101
|
+
the_mail.attachments.each do |att|
|
102
|
+
photo = Photo.new({
|
103
|
+
content_type: att.content_type.split(';')[0],
|
104
|
+
original_filename: att.content_type_parameters[:name],
|
105
|
+
image_data: att.body.encoded,
|
106
|
+
email_message_id: @message.id,
|
107
|
+
})
|
108
|
+
photo.decode_base64_image
|
109
|
+
if photo.save
|
110
|
+
;
|
111
|
+
else
|
112
|
+
attachment = Office::EmailAttachment.new({
|
113
|
+
content: att.decoded,
|
114
|
+
content_type: att.content_type,
|
115
|
+
email_message: @message,
|
116
|
+
filename: att.filename,
|
117
|
+
})
|
118
|
+
attachment.save
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
## Leadset, Lead
|
123
|
+
domain = @message.from.split('@')[1] rescue 'unknown.domain'
|
187
124
|
leadset = Leadset.find_or_create_by( company_url: domain )
|
188
|
-
Lead.find_or_create_by( email:
|
189
|
-
|
125
|
+
lead = Lead.find_or_create_by( email: @message.from, m3_leadset_id: leadset.id )
|
126
|
+
the_mail.cc&.each do |cc|
|
127
|
+
domain = cc.split('@')[1] rescue 'unknown.domain'
|
128
|
+
leadset = Leadset.find_or_create_by( company_url: domain )
|
129
|
+
Lead.find_or_create_by( email: cc, m3_leadset_id: leadset.id )
|
130
|
+
end
|
190
131
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
132
|
+
## Conversation
|
133
|
+
if in_reply_to_id
|
134
|
+
in_reply_to_msg = ::Office::EmailMessage.where({ message_id: in_reply_to_id }).first
|
135
|
+
if !in_reply_to_msg
|
136
|
+
conv = ::Office::EmailConversation.find_or_create_by({
|
137
|
+
subject: @message.subject,
|
138
|
+
})
|
139
|
+
in_reply_to_msg = ::Office::EmailMessage.find_or_create_by({
|
140
|
+
message_id: in_reply_to_id,
|
141
|
+
email_conversation_id: conv.id,
|
142
|
+
})
|
143
|
+
end
|
144
|
+
conv = in_reply_to_msg.email_conversation
|
145
|
+
else
|
195
146
|
conv = ::Office::EmailConversation.find_or_create_by({
|
196
147
|
subject: @message.subject,
|
197
148
|
})
|
198
|
-
in_reply_to_msg = ::Office::EmailMessage.find_or_create_by({
|
199
|
-
message_id: in_reply_to_id,
|
200
|
-
email_conversation_id: conv.id,
|
201
|
-
})
|
202
149
|
end
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
150
|
+
@message.update_attributes({ email_conversation_id: conv.id })
|
151
|
+
conv.update_attributes({
|
152
|
+
state: Conv::STATE_UNREAD,
|
153
|
+
latest_at: the_mail.date || Time.now.to_datetime,
|
154
|
+
from_emails: ( conv.from_emails + the_mail.from ).uniq,
|
207
155
|
})
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
# || MiaTagger.analyze( @message.part_html, :is_spammy_recruite ).score > .5
|
232
|
-
|
233
|
-
puts! "applying filter #{filter} to conv #{conv}" if DEBUG
|
234
|
-
|
235
|
-
@message.apply_filter( filter )
|
156
|
+
conv.add_tag( ::WpTag::INBOX )
|
157
|
+
conv_lead_tie = Office::EmailConversationLead.find_or_create_by({
|
158
|
+
lead_id: lead.id,
|
159
|
+
email_conversation_id: conv.id,
|
160
|
+
})
|
161
|
+
|
162
|
+
|
163
|
+
## Actions & Filters
|
164
|
+
email_filters = Office::EmailFilter.active
|
165
|
+
email_filters.each do |filter|
|
166
|
+
if ( filter.from_regex.blank? || @message.from.match( filter.from_regex ) ) &&
|
167
|
+
( filter.from_exact.blank? || @message.from.downcase.include?( filter.from_exact&.downcase ) ) &&
|
168
|
+
( filter.body_exact.blank? || @message.part_html&.include?( filter.body_exact ) ) &&
|
169
|
+
( filter.subject_regex.blank? || @message.subject.match( filter.subject_regex ) ) &&
|
170
|
+
( filter.subject_exact.blank? || @message.subject.downcase.include?( filter.subject_exact&.downcase ) )
|
171
|
+
|
172
|
+
# || MiaTagger.analyze( @message.part_html, :is_spammy_recruite ).score > .5
|
173
|
+
|
174
|
+
puts! "applying filter #{filter} to conv #{conv}" if DEBUG
|
175
|
+
|
176
|
+
@message.apply_filter( filter )
|
177
|
+
end
|
236
178
|
end
|
237
|
-
end
|
238
179
|
|
239
|
-
|
180
|
+
stub.update_attributes({ state: ::Office::EmailMessageStub::STATE_PROCESSED })
|
240
181
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
182
|
+
## Notification
|
183
|
+
conv = Conv.find( conv.id )
|
184
|
+
if conv.in_emailtag? WpTag::INBOX
|
185
|
+
out = ::Ishapi::ApplicationMailer.forwarder_notify( @message.id.to_s )
|
186
|
+
Rails.env.production? ? out.deliver_later : out.deliver_now
|
187
|
+
end
|
247
188
|
|
248
189
|
end
|
249
|
-
|
250
190
|
end
|
251
191
|
EIJ = Ishapi::EmailMessageIntakeJob
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ishapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.8.
|
4
|
+
version: 0.1.8.303
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- piousbox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|