Скрипт для автоматического создания правила dovecot.sieve для электронных писем в подкаталоге «Входящие».

после просмотра/использования решений на этом замечательном сайте в течение некоторого времени, наконец, пришло время для меня принять участие.

У меня есть довольно четкое представление о том, чего я хочу, но я ищу лучший способ добиться этого.

Что мне нужно?

В течение некоторого времени я использую настройку почтового сервера на raspberry-pi, пока она отлично работает. Он состоит из сервера dovecot и некоторых ситовых фильтров, настроенных для сортировки моих многочисленных адресов электронной почты в отдельные подкаталоги входящих сообщений. Существует также спам-фильтр, который каждую ночь обучается разнице между хамом и спамом с помощью скрипта. (В основном его учат, что спам находится в папке нежелательной почты, а в любой другой папке есть ветчина)

Я хотел бы воспроизвести это поведение для специальной папки «Информационный бюллетень». Эта папка не содержит срочных сообщений, которые нужно немедленно просмотреть или сообщить.

План состоит в том, чтобы вручную помещать электронные письма в папку «новости» и запускать скрипт, сканирующий эту папку один раз в день. Если он находит электронное письмо с адреса, для которого нет правила фильтрации, он должен создать правило для автоматического помещения писем с этого адреса в папку «новости» по прибытии.

Шаги к реализации?:

  • Для этого скрипт должен будет просканировать существующий файл .dovecot.sieve, извлечь адреса из правила «папка новостей» в отдельный файл или объект для сравнения.

    /*Example of a sieve filter:*/
    
    require "fileinto";
    
     /* Global Spam Filter */
    if anyof (header :contains "subject" "*SPAM*",
              header :contains "X-Spam-Flag" "YES" ) {
      fileinto "Junk";
      stop;
    }
    
    /* LAN Emails Filter */
      elsif address :is "to" "[email protected]" {
      fileinto "INBOX.Lokal";
      stop;
    }
    
    /* Newsletter Filter */
      elsif anyof (address :is "from" "[email protected]",
                   address :is "from" "[email protected]",
                   address :is "from" "[email protected]",
                   address :is "from" "[email protected]") {
      fileinto "INBOX.Newsletter";
      stop;
    }
    
     /* gmail Account Filter */
      elsif address :is "to" "[email protected]" {
      fileinto "INBOX.gmail";
      stop;
    }
    
     /* Yahoo Account Filter */
      elsif address :is "to" "[email protected]" {
      fileinto "INBOX.yahoo";
      stop;
    }
    
      else {
      # The rest goes into INBOX
      # default is "implicit keep", we do it explicitly here
      keep;
    }
    
  • Затем ему нужно будет обработать все электронные письма в каталоге maildir папки «новости» и найти в электронных письмах поле «От:» и адрес электронной почты, заключенный в заостренные скобки.

    Date: Mon, 4 Nov 2013 16:38:30 +0100 (CET)
    From: Johannes Ebert - Redaktion c't <[email protected]> 
    To: [email protected]
    
  • сравните их с извлеченными адресами из файла sieve и, если адрес не имеет правила фильтрации
    (например, не найден в списке), создайте его для него (или просто добавьте его к извлеченным адресам)

  • после обработки всех электронных писем будет создан новый набор правил для папки "новости" с файлом
    extracted_email_addresses-file, а существующий файл dovecot.sieve будет заменен новым (старый
    будет скопирован ранее, на всякий случай)
  • может быть, после этого потребуется перезапустить голубятню, чтобы прочитать новые правила?

Прогресс на данный момент:

Я попытался заставить это работать, просто используя команды и утилиты bash. Это приблизило меня к моменту, когда я почти смог извлечь адреса электронной почты из файла dovecot.sieve, но, на мой вкус, это было довольно сложно и заняло некоторое время.

#!/bin/sh

cp /home/mailman/.dovecot.sieve /home/mailman/autosieve/dovecot.sieve_`date +backup_%d%m%Y`
#echo "" > search.txt

X=grep -n "Newsletter Filter" /home/mailman/.dovecot.sieve #get rule start line number, some magic needs to happen here to just apply the numbers and not the full output by grep
Y=grep -n "INBOX.Newsletter" /home/mailman/.dovecot.sieve #get rule end line number
$X++  #increment to go into the next line
$Y--  #decrement to go into the previous line
sed -n ‘$X,$Yp’ /home/mailman/.dovecot.sieve > /home/mailman/search.txt  #copy lines into separate search_file
less /home/mailman/search.txt | awk -F '"' '{ if ($2 != "") print $4 }' > /home/mailman/adressen.txt # filter addresses and export to separate file

Поэтому я подумал, не смогу ли я добраться туда проще, возможно, используя python. Я возился с ним в другом малиновом проекте, но у меня не было времени полностью погрузиться во вселенную Python.

Так что я был бы рад небольшой помощи/совету/указанию в правильном направлении здесь.

До сих пор я нашел несколько решений для аналогичной проблемы (для первой части), где было необходимо извлечение, но я не смог его полностью адаптировать или сделал некоторые ошибки, так как не мог выполнить скрипт.

#!/usr/bin/python

file = open("dovecot.sieve", "r")

rule = {}
current_rule = None

for line in file:
    line = line.split()

    if (line[2] == "INBOX.Newsletter"):
        break
    if (line[1] == "/* Newsletter Filter */"):
        current_rule = rule.setdefault('Newsletter', [])
        continue
    if (line[5] == "from"):
        current_rule.append(line[6])
        continue
    if (line[3] == "from"):
        current_rule.append(line[4])
        continue


file.close()

# Now print out all the data
import pprint
print "whole array"
print "=============================="
pprint.pprint(rule)
print 
print "addresses found"
print "=========================="
pprint.pprint(rule['Newsletter'])

Может ли кто-нибудь также порекомендовать IDE для python с отладчиком и т. Д.? Eclipse пришел бы мне на ум, или есть что-то еще (может быть, не такое ресурсоемкое)?


person DocBrown    schedule 17.10.2014    source источник


Ответы (1)


Итак, у меня есть свободное время, чтобы решить свой вопрос. Немного покопался, прочитал несколько фрагментов кода и протестировал его в Eclipse с помощью Pydev.

Теперь я запускаю этот скрипт как задание cron ночью.

Что он делает?

Он собирает все адреса электронной почты в файле dovecot.sieve (то есть в наборе правил «Информационный бюллетень»). Затем ищет в папке INBOX.Newsletter любые незарегистрированные адреса электронной почты, сравнивая их с собранными адресами. Если он находит новый адрес, он сохраняет копию старого файла sieve, а затем перезаписывает существующий файл. Новые адреса электронной почты вставляются в набор правил «Информационный бюллетень», поэтому эти электронные письма перенаправляются в указанную папку «Информационный бюллетень».

#!/usr/bin/python2.7

import os, sys
#Get the already configured email senders...
addresses = {}
current_addresses = None

with open("/home/postman/.dovecot.sieve", "r") as sieveconf:
    for line in sieveconf:
        if "INBOX.Newsletter" in line:
            break

        if "Newsletter Filter" in line:
            current_addresses = addresses.setdefault('found', [])
            continue

        if "from" in line and current_addresses != None:
            line = line.split('"')

            if (len(line) > 4) and (line[1] == "from"):
                current_addresses.append(line[3])

                continue

#save the count for later
addr_num = 0
addr_num = len(addresses['found'])

#iterate all files in all sub-directories of INBOX.Newsletter
for root, _,files in os.walk("/home/postman/Mails/.INBOX.Newsletter"):
    #for each file in the current directory
    for emaildir in files:
        #open the file
        with open(os.path.join(root, emaildir), "r") as mail:
            #scan line by line
            for line in mail:
                if "From: " in line:
                    #arm boolean value for adding to list
                    found_sw = False
                    #extract substring from line
                    found = ((line.split('<'))[1].split('>')[0])
                    #compare found address with already existing addresses in dictionary
                    for m_addr in addresses['found']:
                        if m_addr == found:
                            #remember if the address is already in the dictionary
                            found_sw = True
                            break

                    if not found_sw:
                        #if the address is not included in the dictionary put it there
                        current_addresses.append(found)
                    break


# Now print out all the data
#import pprint
#print "addresses found:"
#print "=========================="
#pprint.pprint(addresses['found'])
#print
#print "orig_nmbr_of_addresses:" , addr_num
#print "found_nmbr_of_addresses:", len(addresses['found'])
#print "not_recorded_addresses:", (len(addresses['found']) - (addr_num))

#Compare if the address count has changed
if addr_num == len(addresses['found']):
    #exit the script since no new addresses have been found
    sys.exit
else:
    #copy original sieve file for backup
    import datetime
    from shutil import copyfile
    backupfilename = '.backup_%s.sieve'% datetime.date.today()
    copyfile('dovecot.sieve', backupfilename)

    #edit the existing sieve file and add the new entries
    import fileinput
    #open file for in place editing
    for line in fileinput.input('dovecot.sieve', inplace=1):
        #if the line before the last entry is reached
        if addresses['found'][(addr_num - 2)] in line:
            #print the line
            print line,
            #put new rules before the last line (just to avoid extra handling for last line, since the lines before are rather identical)
            for x in range (addr_num, (len(addresses['found']))):
                print '               address :is "from" "%s",'% addresses['found'][x]
        else:
            #print all other lines
            print line,
person DocBrown    schedule 07.11.2014