将*单个* IMAP 消息标记为未读

问题描述 投票:0回答:2

在获取所有“未读”消息然后迭代它们并获取它们之后,我正在尝试操作单个消息上的 IMAP 标志以将其标记为未读。

我不完全确定如何在单条消息的基础上将消息标记为未读/未见。我得到的只是消息编号,并且我不确定如何正确存储 UID 以仅影响单个消息。

类似问题中的答案似乎不起作用,因为它将错误消息设置为“未读”。如何设置我再次获取为“未读”的单个邮件?


我被要求提供更多信息。虽然这里去掉了“秘密”的具体细节,但这是我尝试实现的现有运行时,以便它尝试根据代码规则处理消息,并存储消息编号等,并尝试将消息的 ID 和主题存储在 pickle 文件中后将每条消息设置为“未读”,因为运行期间“看到”的任何内容都会在服务器上自动标记为“已读”,而不是设置为“未读”状态:

def main():
    conn = imaplib.IMAP4('SERVER')
    conn.login('username', 'passphrase')

    conn.select('inbox')
    (status, nums) = conn.search(None, '(UNSEEN)')

    msgnums = map(int, nums[0].split())

    for i in msgnums:
        try:
            raw_msg = conn.fetch(i, '(RFC822)')

            raw_msg = conn.fetch(i, '(RFC822)')
            msg = email.message_from_string(raw_msg[1][0][1])
            body = "Date: %s\r\nSender: %s\r\nSubject: %s\r\n\r\n" % (msg['Date'], msg['From'], msg['Subject'])
            msg_date = re.sub('/', '-', msg['Date']).replace(":", ".")
            fdate = re.sub('\s+', '_', msg_date).replace(",", "")
            print "Checking message: %s" % msg['Subject']

            if not msg['Subject']:
                continue  # fname = "unknown_msg%d_%s" % (i,fdate)
            elif msg['Subject'].lower().rfind('foobar') != -1:
                print "Subject match 'foobar', processing: %s" % msg['Subject']
                # We should have from the pickle an "observed" set of data, both subjects and message numbers.
                if msg['Subject'] in PICKLED_MESSAGES['observed']['subjects']:
                    print "Already handled this message, moving on to next item."
                    # Since this was already observed we let it be removed so things don't rerun it later.
                    # noinspection PyBroadException
                    try:
                        PICKLED_MESSAGES['observed']['subjects'].remove(msg['Subject'])
                        PICKLED_MESSAGES['observed']['msgnums'].remove(i)
                    except:
                        pass
                    continue
            else:
                continue

            # Do stuff with the message to store it in a special way on the filesystem

            # Note that we've now looked at the message, so next-run we can see
            # what was handled on the last run.
            PICKLED_MESSAGES['observed']['msgnums'].append(i)
            PICKLED_MESSAGES['observed']['subjects'].append(msg['Subject'])
            print "PICKLED:\n%s" % PICKLED_MESSAGES['observed']
            conn.uid('STORE', str(i), '-FLAGS', '(\Seen)')
        except Exception:
            conn.uid('STORE', str(i), '-FLAGS', '(\Seen)')
            PICKLED_MESSAGES['observed']['msgnums'].remove(i)
            PICKLED_MESSAGES['observed']['subjects'].remove(msg['Subject'])
            print "PICKLED:\n%s\n" % PICKLED_MESSAGES
        finally:
            # Store the pickle file so we can use it next run.
            cPickle.dump(PICKLED_MESSAGES, open('observed_msgs.pkl', 'wb'))


if __name__ == "__main__":
    # pre-runtime checks - is IMAP up, etc. run first, then this:

    # Initialize the PICKLED_MESSAGES data with pickle data or an empty
    # structure for the pickle.
    # noinspection PyBroadException
    try:
        PICKLED_MESSAGES = cPickle.load(open('observed_msgs.pkl', 'rb'))
    except Exception as e:
        PICKLED_MESSAGES = {
            'observed': {
                'msgnums': [],
                'subjects': [],
            },
        }

    # If all checks satisfied, continue and process the main() directives.
    try:
        main()
    except Exception as e:
        print("CRITICAL    An unhandled error has occurred: %s" % str(e))
        exit()

但是,它将正确的消息设置为“未读;”当使用我在系统上看到的建议方法时。所以,我不完全确定我是否没有正确获取消息的 UID,或者我是否还缺少其他操作。

python python-2.7 email imaplib
2个回答
8
投票

嗯,我今天感觉很蠢。

显然,正在迭代的消息编号和

conn.uid(...)
期望的消息的 UID 不一定是相同的编号。我发现必须获取 UID 并进行一些获取后处理才能仅传递 UID。


原始方法

我能够在上面的

for
循环中使用以下命令获取 UID:

for i in msgnums:
    # ...
    msg_uid = conn.fetch(i, 'UID')[1][0].split()[2].strip('()')
    # ...

这给了我消息的 UID,这是

conn.uid
所期望的,而不是简单的消息编号。我觉得自己没有意识到这一点有点愚蠢,但这似乎解决了问题。


更新方法#1(感谢评论中的@Max

我用 UID 等效项替换了所有搜索/获取/存储命令。

  • conn.search(None, '(UNSEEN)')
    变成
    conn.uid('SEARCH', None, '(UNSEEN)')
  • conn.fetch(i, '(RFC822)')
    变成
    conn.uid('FETCH', i, '(RFC822)')
  • conn.store(i, '-FLAGS', '(\Seen)')
    变成
    conn.uid('STORE', i, '-FLAGS', '(\Seen)')

更新方法#2(受#1启发,但更进一步)

我基本上厌倦了编写 UID 命令,但还需要在另一个使用类似 IMAP 接口和命令的程序中应用类似的基于 UID 的功能。鉴于此,我决定编写一个

imaplib_extension.py
模块,用于“扩展”
imaplib
IMAP4
IMAP4_SSL
功能,并使用
uid
覆盖“搜索”、“获取”和“存储”命令变体,但以其他方式保留
imaplib
中的“搜索”、“获取”和“存储”命令,但返回基于 UID 函数的不同结果集。

这就是我的

imaplib_extension.py
文件中的内容,我只需从此模块导入
IMAP4
IMAP4_SSL
而不是直接从
imaplib
导入,并用
imaplib.IMAP4
替换任何
imaplib.IMAP4_SSL
IMAP4
调用或
IMAP4_SSL
稍后致电。因此,不需要
import imaplib
,只需
from imaplib import IMAP4
(或相应的
IMAP4_SSL
):

import imaplib

class IMAP4(imaplib.IMAP4):
    def search(self, charset, *criteria):
        # conn.uid('SEARCH', charset, criteria)
        return self.uid('SEARCH', charset, " ".join(criteria))


    def fetch(self, message_set, message_parts):
        # conn.uid('FETCH', msgset, parts)
        return self.uid('FETCH', message_set, message_parts)

    def store(self, message_set, command, flags):
        # conn.uid('STORE', msg_uid, '-FLAGS', '(\Seen)')
        return self.uid('STORE', message_set, command, flags)


# noinspection PyPep8Naming
class IMAP4_SSL(imaplib.IMAP4_SSL):
    def search(self, charset, *criteria):
        # conn.uid('SEARCH', charset, criteria)
        return self.uid('SEARCH', charset, " ".join(criteria))

    def fetch(self, message_set, message_parts):
        # conn.uid('FETCH', msgset, parts)
        return self.uid('FETCH', message_set, message_parts)

    def store(self, message_set, command, flags):
        # conn.uid('STORE', msg_uid, '-FLAGS', '(\Seen)')
        return self.uid('STORE', message_set, command, flags)

我更喜欢使用

imaplib
的扩展,因为命令结构与现有命令保持相同,但可以正确使用 UID,而不是可能不是 UID 的“消息编号”。


更新方法#3

在意识到我在 other Python 应用程序中需要这个之后,我立即在 PyPI 上发布了

imaplibext,这基本上是上面方法 #2 的改进和充实版本。然而,它确实具有更好的错误处理能力,并且能够实际指定 IMAP 连接套接字的超时。这是一项改进,因为您无法直接对 
imaplib.IMAP4
imaplib.IMAP4_SSL
执行此操作,而且本质上是
imaplib
的直接替代品(尽管其核心仍然使用
imaplib
)。

此代码也存在于 GitHub,用于一般用途、改进建议和问题报告。


0
投票

我将留下有效的内容,它是其他线程中留下的几个答案的组合。看来APPEND中的flags不太好理解。

import imaplib

mail.select("Inbox", readonly=False)
status, messages = mail.uid('SEARCH', None, '(UNSEEN)')
message_ids = messages[0].split()

for message_id in message_ids:
    status, message_data = mail.uid('FETCH', message_id, '(RFC822)')
    email_from = message.get("From")
    email_subject = message.get("Subject")

    mail.uid('STORE', message_id, '-FLAGS', '\\SEEN')

© www.soinside.com 2019 - 2024. All rights reserved.