Я должен сделать парсинг ответа по электронной почте о проекте, над которым я сейчас работаю. В итоге я использовал сопоставление с образцом для идентификации части ответа, поэтому пользователям не нужно было беспокоиться о том, куда вставить свой ответ.
Хорошей новостью является то, что реализация на самом деле не так уж сложна. Сложная часть - просто протестировать все почтовые клиенты и сервисы, которые вы хотите поддерживать, и выяснить, как идентифицировать каждого из них. Как правило, вы можете использовать либо идентификатор сообщения, либо заголовок X-Mailer или Return-Path, чтобы определить, откуда пришло электронное письмо.
Вот метод, который берет объект TMail и извлекает ответную часть сообщения и возвращает его вместе с почтовым клиентом / сервисом, из которого оно было отправлено. Предполагается, что у вас есть исходное сообщение От: имя и адрес в константах FROM_NAME
и FROM_ADDRESS
.
def find_reply(email)
message_id = email.message_id('')
x_mailer = email.header_string('x-mailer')
# For optimization, this list could be sorted from most popular to least popular email client/service
rules = [
[ 'Gmail', lambda { message_id =~ /.+gmail\.com>\z/}, /^.*#{FROM_NAME}\s+<#{FROM_ADDRESS}>\s*wrote:.*$/ ],
[ 'Yahoo! Mail', lambda { message_id =~ /.+yahoo\.com>\z/}, /^_+\nFrom: #{FROM_NAME} <#{FROM_ADDRESS}>$/ ],
[ 'Microsoft Live Mail/Hotmail', lambda { email.header_string('return-path') =~ /<.+@(hotmail|live).com>/}, /^Date:.+\nSubject:.+\nFrom: #{FROM_ADDRESS}$/ ],
[ 'Outlook Express', lambda { x_mailer =~ /Microsoft Outlook Express/ }, /^----- Original Message -----$/ ],
[ 'Outlook', lambda { x_mailer =~ /Microsoft Office Outlook/ }, /^\s*_+\s*\nFrom: #{FROM_NAME}.*$/ ],
# TODO: other email clients/services
# Generic fallback
[ nil, lambda { true }, /^.*#{FROM_ADDRESS}.*$/ ]
]
# Default to using the whole body as the reply (maybe the user deleted the original message when they replied?)
notes = email.body
source = nil
# Try to detect which email service/client sent this message
rules.find do |r|
if r[1].call
# Try to extract the reply. If we find it, save it and cancel the search.
reply_match = email.body.match(r[2])
if reply_match
notes = email.body[0, reply_match.begin(0)]
source = r[0]
next true
end
end
end
[notes.strip, source]
end