Как читать электронную почту с помощью Lua?

#lua #smtp #luasocket

#lua #smtp #luasocket

Вопрос:

Я пишу скрипт, который может отправлять электронные письма с помощью LuaSocket SMTP. Теперь я хотел бы, чтобы мой скрипт также мог читать мои электронные письма.

У меня есть учетная запись электронной почты в службе, которая поддерживает IMAP.

 IMAP host and port: example.com:143
My E-Mail: doesnotexist@example.com
The password: notsaying
  

Как я могу заставить свой скрипт получать электронную почту из этой службы, чтобы я мог отображать ее содержимое?

Редактировать:

Я пробовал imap4 с https://github.com/vrld/imap4.lua но, похоже, это еще не конец.

Я попробовал этот пример:

 require 'luarocks.require'

local imap4 = require 'imap4'

local username = "doesnotexist@example.com"
local password = "notsaying"

local connection = imap4('example.com', 143)

print(table.concat(connection:capability(), ', '))
assert(connection:isCapable('IMAP4rev1'))

connection:login(username, password)

for mb, info in pairs(connection:lsub()) do
    local stat = connection:status(mb, {'MESSAGES', 'RECENT', 'UNSEEN'})
    print(mb, stat.MESSAGES, stat.RECENT, stat.UNSEEN)
end

local info = connection:examine('INBOX')
print(info.exist, info.recent)

for _,v in pairs(connection:fetch('UID BODY.PEEK[HEADER.FIELDS (From Date Subject)]', (info.exist-3)..':*')) do
    print(v.id, v.UID, v.BODY.value)
end

connection:logout()
  

Я только добавил имя пользователя и пароль и изменил сервер и порт из исходного примера и изменил самую последнюю выборку с 4 на 3. Я также удалил комментарии, так как их много. Кроме тех, которые я больше ничего не изменял, это в основном тот же пример.

Кажется, что он правильно входит в систему, используя мои учетные данные и правильный сервер и порт, но у него есть какая-то проблема, которую я не могу отладить.

Приведенный выше пример выводит:

 IMAP4rev1, CHILDREN, NAMESPACE
INBOX.Sent  3   0   0
INBOX   4   0   0
4   0
lua: example.lua:24: attempt to index field 'BODY' (a nil value)
stack traceback:
    example.lua:24: in main chunk
    [C]: ?
  

Это показывает, что у меня есть 4 электронных письма в моем почтовом ящике и 3 в моем отправленном, что правильно.

Комментарии:

1. Вы пробовали что-нибудь? Вы читали документацию? Вы пробовали какой-то код и потерпели неудачу?

2. Я обновил свой вопрос.

3. В комментарии в исходном примере упоминается «Информация о списке 4 самых последних писем», когда он ссылается на (info.exist-4)..':*' . Поскольку в вашем почтовом ящике всего четыре письма, возможно, вам следует использовать меньшее число, чем 4…

4. Вы правы, обновили мой вопрос. Я изменил -4 на -3. Теперь возникла новая проблема, он не находит поле «ТЕЛО». Я проверил, что все 4 сообщения электронной почты имеют тело.

5. может быть, вы можете использовать только pop3 дополнительно с tls ( github.com/moteus/lua-pop3 )? Я использую эту библиотеку в службе, которая опрашивает почтовый сервер. Также вы можете recv rfc822 через imap и проанализировать его с помощью pop3.message class . я могу опубликовать пример завтра

Ответ №1:

В этом примере используется библиотека imap4 для получения сообщения и pop3.message для анализа.

 local imap4   = require "imap4"
local Message = require "pop3.message"

local connection = imap4('imap.qip.ru', 143)

assert(connection:isCapable('IMAP4rev1'))

connection:login('****', '****')

-- Select INBOX with read only permissions.
local info = connection:examine('INBOX')
print(info.exist, info.recent)

-- List info on the 4 most recent mails.
for _,v in pairs(connection:fetch('RFC822', (info.exist-4)..':*')) do
    print("-------------------------")
    local msg = Message(v.RFC822)
    print("ID:         ", msg:id())
    print("subject:    ", msg:subject())
    print("to:         ", msg:to())
    print("from:       ", msg:from())
    print("from addr:  ", msg:from_address())
    print("reply:      ", msg:reply_to())
    print("reply addr: ", msg:reply_address())
    print("trunc:      ", msg:is_truncated())
    for i,v in ipairs(msg:full_content()) do
        if v.text then  print("  ", i , "TEXT: ", v.type, #v.text)
        else print("  ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
    end
end

-- close connection
connection:logout()
  

Комментарии:

1. Спасибо, я приму этот ответ. Я не очень разбираюсь в IMAP, этот ответ помог мне заставить его работать с (по-видимому) нестандартно совместимым сервером IMAP, или, по крайней мере, мне это кажется нестандартным. Я опубликую ответ ниже, объясняющий, почему example

Ответ №2:

Сначала попробуйте решение moeus. Мой пример беспокоит только в том случае, если сервер, который вам нужен, не поддерживает RFC822 или вообще не соответствует стандарту.

В этом ответе я кратко объясню, почему исходный пример не сработал, а также приведу пример без RFC822, поскольку сервер, с которым мне приходится работать, не понимает RFC822 и многие другие ключевые слова, которые он должен понимать в соответствии со стандартом IMAP 4

В примере мне пришлось изменить:

 connection:fetch('UID BODY.PEEK[HEADER.FIELDS (From Date Subject)]', (info.exist-3)..':*'))
  

Для:

 connection:fetch('(UID BODY.PEEK[HEADER.FIELDS (From Date Subject)])', (info.exist-3)..':*'))
  

Обратите внимание на круглые скобки, вот почему пример не сработал.

Ответ Moteus с RFC822 у меня не работает, потому что сервер, с которым мне приходится работать, нестандартный. Вот как я заставил это работать:

 local imap4   = require "imap4"
local Message = require "pop3.message"

local connection = imap4('imap.qip.ru', 143)

assert(connection:isCapable('IMAP4rev1'))

connection:login('****', '****')

-- Select INBOX with read only permissions.
local info = connection:examine('INBOX')
print(info.exist, info.recent)

-- List info on the 4 most recent mails.
for _,v in pairs(connection:fetch('(UID BODY.PEEK[HEADER.FIELDS (Subject To From Date)])', (info.exist-4)..':*')) do
    print("-------------------------")
    local msg = Message(v.BODY.value)
    print("ID:         ", msg:id())
    print("subject:    ", msg:subject())
    print("to:         ", msg:to())
    print("from:       ", msg:from())
    print("from addr:  ", msg:from_address())
    print("reply:      ", msg:reply_to())
    print("reply addr: ", msg:reply_address())
    print("trunc:      ", msg:is_truncated())
    for i,v in ipairs(msg:full_content()) do
        if v.text then  print("  ", i , "TEXT: ", v.type, #v.text)
        else print("  ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
    end
end

-- close connection
connection:logout()
  

В отличие от примера Moteus, вам придется извлекать текст отдельно.

Вы можете получить текст письма следующим образом:

 connection:fetch('(BODY.PEEK[TEXT])', id)