Add IMAP support and refactor environment configuration
- Introduced `imap_connect.py` for handling IMAP email interactions. - Created `run.py` to manage execution based on the selected mode (IMAP or EWS). - Updated Dockerfile and .env.dev to include necessary environment variables. - Enhanced logging and email processing in `exchange_connect.py`. - Added `weasyprint` and `imapclient` to requirements.
This commit is contained in:
parent
e19edee916
commit
5127caed03
@ -1,5 +1,6 @@
|
||||
FROM python:3.10-bullseye
|
||||
|
||||
ENV mode="EWS"
|
||||
ENV username=
|
||||
ENV password=
|
||||
ENV server="exchange.sankt-wendel.de"
|
||||
@ -61,4 +62,4 @@ COPY ./app .
|
||||
COPY *.crt /usr/local/share/ca-certificates/
|
||||
RUN update-ca-certificates
|
||||
|
||||
CMD [ "sh","-c","/etc/init.d/cups start && python3 /usr/src/app/exchange_connect.py" ]
|
||||
CMD [ "sh","-c","/etc/init.d/cups start && python3 /usr/src/app/run.py" ]
|
||||
|
@ -21,6 +21,7 @@ Message.register("alarmfax_parser_verarbeitet", alarmfax_parser_verarbeitet)
|
||||
Message.register("alarmfax_parser_id", alarmfax_parser_id)
|
||||
#print([f.name for f in Message.FIELDS if f.is_searchable])
|
||||
|
||||
def run():
|
||||
threads = {}
|
||||
|
||||
format = "%(asctime)s|%(threadName)s: %(message)s"
|
||||
@ -79,7 +80,6 @@ def folder_event_subscriber(folder: Folder):
|
||||
t = Thread(target=eventHandler, args=(event.ELEMENT_NAME,event.item_id.id,event.item_id.changekey,),name="eventHandler: {} ({})".format(event.ELEMENT_NAME, event.item_id.id))
|
||||
t.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
username = os.environ.get('username')
|
||||
password = os.environ.get('password')
|
||||
|
124
app/imap_connect.py
Normal file
124
app/imap_connect.py
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
import os
|
||||
import ssl
|
||||
import email
|
||||
import logging
|
||||
from imapclient import IMAPClient
|
||||
from threading import Thread
|
||||
from securecad_parser import parse_securecad_message
|
||||
from hooks import webhook, alarminator_api, cups_print
|
||||
import re
|
||||
|
||||
def run():
|
||||
threads = {}
|
||||
|
||||
format = "%(asctime)s|%(threadName)s: %(message)s"
|
||||
logging.basicConfig(format=format, level=logging.INFO,
|
||||
datefmt="%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def eventHandler(ELEMENT_NAME, uid, message_data, _server: IMAPClient = None):
|
||||
email_message = email.message_from_bytes(message_data[b'RFC822'])
|
||||
email_from: list[str] = re.findall(r'([\w\.-]+@[\w\.-]+)', email_message.get('From'))
|
||||
flags = _server.get_flags(uid)
|
||||
logging.info(ELEMENT_NAME + " - get Mail")
|
||||
if 'Processed_{}'.format(parser_id).encode() in flags[uid]:
|
||||
logging.info("Mail {} bereits verarbeitet.. ignoriere".format(uid))
|
||||
if not IS_DEV:
|
||||
return
|
||||
else:
|
||||
_server.set_flags(uid, ['\\SEEN','Processed_{}'.format(parser_id)])
|
||||
logging.info("got Mail {} von {}".format(email_message.get('Subject'), email_from))
|
||||
if any(mail in filter_from for mail in email_from):
|
||||
# Get HTML body
|
||||
html_body = ""
|
||||
if email_message.is_multipart():
|
||||
for part in email_message.walk():
|
||||
if part.get_content_type() == 'text/html':
|
||||
html_body = part.get_payload(decode=True).decode(part.get_content_charset() or 'utf-8', errors='replace')
|
||||
break
|
||||
else:
|
||||
if email_message.get_content_type() == 'text/html':
|
||||
html_body = email_message.get_payload(decode=True).decode(email_message.get_content_charset() or 'utf-8', errors='replace')
|
||||
|
||||
|
||||
parsed_body = parse_securecad_message(html_body)
|
||||
logging.debug(parsed_body)
|
||||
if parsed_body != None:
|
||||
if 'ALARMDEPESCHE' in parsed_body:
|
||||
logging.info("Alarm für: {}".format(parsed_body['ALARMDEPESCHE']))
|
||||
webhook(parsed_body)
|
||||
alarminator_api(parsed_body)
|
||||
cups_print(parsed_body,html_body)
|
||||
pass
|
||||
|
||||
def folder_event_subscriber(folder: str):
|
||||
logging.info('folder_event_subscriber startet for Folder: {}'.format(folder))
|
||||
with IMAPClient(server, ssl_context=ssl_context) as _server:
|
||||
_server.login(username, password)
|
||||
while True:
|
||||
# filtern des ordners nach mails der letzten 24h, die nicht verarbeitet wurden
|
||||
now = datetime.now()
|
||||
_server.select_folder(folder, readonly=False)
|
||||
q = ['SENTSINCE', now - timedelta(days=1),'NOT','KEYWORD', 'Processed_{}'.format(parser_id)]
|
||||
if IS_DEV:
|
||||
q = ['SENTSINCE', now - timedelta(days=1),'UNSEEN']
|
||||
q = ['UNSEEN']
|
||||
messages = _server.search(q)
|
||||
cnt = messages.__len__()
|
||||
if cnt > 0:
|
||||
logging.info("{} Mails nicht verarbeitet in den letzten 2 Tagen in ordner: {}".format(cnt, folder))
|
||||
for uid, message_data in _server.fetch(messages, 'RFC822').items():
|
||||
# IMAPClient ist nicht thread-safe, daher wird hier der _server übergeben und kein Thread verwendet.
|
||||
eventHandler('SearchFolderEvent', uid, message_data, _server)
|
||||
# t = Thread(target=eventHandler, args=('SearchFolderEvent',uid,message_data,),name="eventHandler: SearchFolderEvent ({})".format(uid))
|
||||
# t.start()
|
||||
# aktives warten auf streaming_events. maximal eine minute lang, dann wird nochmal der ordner durchsucht, falls mails angekommen sind während eines timeout/cooldown.
|
||||
_server.idle()
|
||||
try:
|
||||
logging.debug("Idle check for folder: {}".format(folder))
|
||||
messages = _server.idle_check(timeout=60) # Timeout after 60 seconds
|
||||
# if not messages:
|
||||
# logging.info("No new messages in folder: {}".format(folder))
|
||||
# continue
|
||||
for item in messages:
|
||||
if item[1] in (b'EXISTS'):
|
||||
logging.info("New messages in folder: {}".format(folder))
|
||||
for uid, message_data in _server.fetch([item[0]], 'RFC822').items():
|
||||
if uid:
|
||||
eventHandler('NewMailEvent', uid, message_data, _server)
|
||||
except Exception as e:
|
||||
logging.error("Error during idle check: {}".format(e))
|
||||
_server.idle_done()
|
||||
|
||||
username = os.environ.get('username')
|
||||
password = os.environ.get('password')
|
||||
server = os.environ.get('server')
|
||||
folders = os.environ.get('folders',"")
|
||||
parser_id = os.environ.get('alarmfax_parser_id',"")
|
||||
primary_smtp_address = os.environ.get('primary_smtp_address')
|
||||
filter_from = os.environ.get('filter_from').split(";") if os.environ.get('filter_from') else []
|
||||
IS_DEV = True if os.environ.get('IS_DEV') and os.environ.get('IS_DEV') == "True" else False
|
||||
if IS_DEV:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
ssl_context = ssl.create_default_context()
|
||||
|
||||
|
||||
with IMAPClient(server, ssl_context=ssl_context) as _server:
|
||||
_server.login(username, password)
|
||||
_server.logout()
|
||||
folders_to_subscribe = []
|
||||
for f in folders.split(";"):
|
||||
if f == "":
|
||||
folders_to_subscribe.append('INBOX')
|
||||
else:
|
||||
folders_to_subscribe.append(f)
|
||||
|
||||
while True:
|
||||
for f in folders_to_subscribe:
|
||||
if not f in threads or not threads[f].is_alive():
|
||||
logging.info("folder_event_subscriber for folder \"{}\" not alive, starting".format(f))
|
||||
t = Thread(target=folder_event_subscriber, args=(f,), daemon=True, name="folder_event_subscriber {}".format(f))
|
||||
threads[f] = t
|
||||
t.start()
|
10
app/run.py
Normal file
10
app/run.py
Normal file
@ -0,0 +1,10 @@
|
||||
import os
|
||||
from imap_connect import run as run_imap
|
||||
from exchange_connect import run as run_ews
|
||||
mode = os.environ.get('mode')
|
||||
if mode == 'IMAP':
|
||||
run_imap()
|
||||
elif mode == 'EWS':
|
||||
run_ews()
|
||||
else:
|
||||
raise ValueError("Invalid mode specified. Use 'IMAP' or 'EWS'.")
|
@ -6,6 +6,7 @@ services:
|
||||
restart: always
|
||||
privileged: true
|
||||
environment:
|
||||
- mode=${mode}
|
||||
- username=${username}
|
||||
- password=${password}
|
||||
- server=${server}
|
||||
|
@ -29,3 +29,4 @@ xmltodict==0.12.0
|
||||
xmltojson==2.0.1
|
||||
pycups==2.0.1
|
||||
weasyprint
|
||||
imapclient==3.0.1
|
Loading…
x
Reference in New Issue
Block a user