mirror of
https://github.com/draga79/NotiMail.git
synced 2024-11-09 23:51:07 +01:00
Added multiple e-mail support - fixed documentation - version 0.9
This commit is contained in:
parent
2577a52c1a
commit
98b14dbf8f
3 changed files with 116 additions and 64 deletions
157
NotiMail.py
157
NotiMail.py
|
@ -1,16 +1,16 @@
|
||||||
"""
|
"""
|
||||||
NotiMail
|
NotiMail
|
||||||
Version: 0.8 - Alpha
|
Version: 0.9 - Alpha
|
||||||
Author: Stefano Marinelli <stefano@dragas.it>
|
Author: Stefano Marinelli <stefano@dragas.it>
|
||||||
License: BSD 3-Clause License
|
License: BSD 3-Clause License
|
||||||
|
|
||||||
NotiMail is a script designed to monitor an email inbox using the IMAP IDLE feature
|
NotiMail is a script designed to monitor one or more email inbox(es) using the IMAP IDLE feature
|
||||||
and send notifications via HTTP POST requests when a new email arrives. This version includes
|
and send notifications via HTTP POST requests when a new email arrives. This version includes
|
||||||
additional features to store processed email UIDs in a SQLite3 database and ensure they are not
|
additional features to store processed email UIDs in a SQLite3 database and ensure they are not
|
||||||
processed repeatedly.
|
processed repeatedly.
|
||||||
|
|
||||||
The script uses:
|
The script uses:
|
||||||
- IMAP to connect to an email server
|
- IMAP to connect to one or more email server(s)
|
||||||
- IDLE mode to wait for new emails
|
- IDLE mode to wait for new emails
|
||||||
- Sends a notification containing the sender and subject of the new email upon receipt
|
- Sends a notification containing the sender and subject of the new email upon receipt
|
||||||
- Maintains a SQLite database to keep track of processed emails
|
- Maintains a SQLite database to keep track of processed emails
|
||||||
|
@ -24,6 +24,7 @@ Python Dependencies:
|
||||||
- sqlite3: For database operations.
|
- sqlite3: For database operations.
|
||||||
- datetime: For date and time operations.
|
- datetime: For date and time operations.
|
||||||
- signal, sys: For handling script shutdown and signals.
|
- signal, sys: For handling script shutdown and signals.
|
||||||
|
- threading: to deal with multiple inboxes
|
||||||
- BytesParser from email.parser: For parsing raw email data.
|
- BytesParser from email.parser: For parsing raw email data.
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
|
@ -70,6 +71,7 @@ import datetime
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
from email import policy
|
from email import policy
|
||||||
from email.parser import BytesParser
|
from email.parser import BytesParser
|
||||||
|
|
||||||
|
@ -111,11 +113,13 @@ class DatabaseHandler:
|
||||||
def close(self):
|
def close(self):
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
|
|
||||||
|
# Create a global database handler
|
||||||
|
db_handler = DatabaseHandler()
|
||||||
|
|
||||||
class EmailProcessor:
|
class EmailProcessor:
|
||||||
def __init__(self, mail):
|
def __init__(self, mail):
|
||||||
self.mail = mail
|
self.mail = mail
|
||||||
self.db_handler = DatabaseHandler()
|
self.db_handler = db_handler # Use the global db_handler
|
||||||
|
|
||||||
def fetch_unseen_emails(self):
|
def fetch_unseen_emails(self):
|
||||||
status, messages = self.mail.uid('search', None, "UNSEEN")
|
status, messages = self.mail.uid('search', None, "UNSEEN")
|
||||||
|
@ -307,62 +311,27 @@ class IMAPHandler:
|
||||||
processor = EmailProcessor(self.mail)
|
processor = EmailProcessor(self.mail)
|
||||||
processor.process()
|
processor.process()
|
||||||
|
|
||||||
|
class MultiIMAPHandler:
|
||||||
|
def __init__(self, accounts):
|
||||||
|
self.accounts = accounts
|
||||||
|
self.handlers = [IMAPHandler(account['Host'], account['EmailUser'], account['EmailPass']) for account in accounts]
|
||||||
|
|
||||||
# Load configurations from config.ini
|
def run(self):
|
||||||
config = configparser.ConfigParser()
|
threads = []
|
||||||
config.read('config.ini')
|
for handler in self.handlers:
|
||||||
|
thread = threading.Thread(target=self.monitor_account, args=(handler,))
|
||||||
|
thread.daemon = True # Set thread as daemon
|
||||||
|
threads.append(thread)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
email_user = config['EMAIL']['EmailUser']
|
for thread in threads:
|
||||||
email_pass = config['EMAIL']['EmailPass']
|
thread.join()
|
||||||
host = config['EMAIL']['Host']
|
|
||||||
|
|
||||||
# Example: Creating notification providers based on configuration
|
|
||||||
providers = []
|
|
||||||
|
|
||||||
if 'NTFY' in config:
|
|
||||||
ntfy_urls = [config['NTFY'][url_key] for url_key in config['NTFY']]
|
|
||||||
providers.append(NTFYNotificationProvider(ntfy_urls))
|
|
||||||
|
|
||||||
if 'PUSHOVER' in config:
|
|
||||||
api_token = config['PUSHOVER']['ApiToken']
|
|
||||||
user_key = config['PUSHOVER']['UserKey']
|
|
||||||
providers.append(PushoverNotificationProvider(api_token, user_key))
|
|
||||||
|
|
||||||
if 'GOTIFY' in config:
|
|
||||||
gotify_url = config['GOTIFY']['Url']
|
|
||||||
gotify_token = config['GOTIFY']['Token']
|
|
||||||
providers.append(GotifyNotificationProvider(gotify_url, gotify_token))
|
|
||||||
|
|
||||||
# Initialize Notifier with providers
|
|
||||||
notifier = Notifier(providers)
|
|
||||||
|
|
||||||
|
|
||||||
# Set a global timeout for all socket operations
|
@staticmethod
|
||||||
socket.setdefaulttimeout(600) # e.g., 600 seconds or 10 minutes
|
def monitor_account(handler):
|
||||||
|
print(f"Monitoring {handler.email_user}")
|
||||||
def shutdown_handler(signum, frame):
|
logging.info(f"Monitoring {handler.email_user}")
|
||||||
print("Shutdown signal received. Cleaning up...")
|
|
||||||
logging.info(f"Shutdown signal received. Cleaning up...")
|
|
||||||
try:
|
|
||||||
handler.mail.logout()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
processor.db_handler.close()
|
|
||||||
print("Cleanup complete. Exiting.")
|
|
||||||
logging.info(f"Cleanup complete. Exiting.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Register the signal handlers
|
|
||||||
signal.signal(signal.SIGTERM, shutdown_handler)
|
|
||||||
signal.signal(signal.SIGINT, shutdown_handler)
|
|
||||||
|
|
||||||
print("Script started. Press Ctrl+C to stop it anytime.")
|
|
||||||
logging.info(f"Script started. Press Ctrl+C to stop it anytime.")
|
|
||||||
handler = IMAPHandler(host, email_user, email_pass)
|
|
||||||
processor = EmailProcessor(None) # Creating an instance for graceful shutdown handling
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
try:
|
try:
|
||||||
handler.connect()
|
handler.connect()
|
||||||
while True:
|
while True:
|
||||||
|
@ -370,17 +339,89 @@ try:
|
||||||
handler.process_emails()
|
handler.process_emails()
|
||||||
except ConnectionAbortedError as e:
|
except ConnectionAbortedError as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
time.sleep(30) # wait for 30 seconds before trying to reconnect
|
time.sleep(30)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"An unexpected error occurred: {str(e)}")
|
print(f"An unexpected error occurred: {str(e)}")
|
||||||
logging.error(f"An unexpected error occurred: {str(e)}")
|
logging.error(f"An unexpected error occurred: {str(e)}")
|
||||||
notifier.send_notification("Script Error", f"An unexpected error occurred: {str(e)}")
|
notifier.send_notification("Script Error", f"An unexpected error occurred: {str(e)}")
|
||||||
finally:
|
|
||||||
|
def shutdown_handler(signum, frame):
|
||||||
|
print("Shutdown signal received. Cleaning up...")
|
||||||
|
logging.info(f"Shutdown signal received. Cleaning up...")
|
||||||
|
try:
|
||||||
|
for handler in MultiIMAPHandler.handlers:
|
||||||
|
handler.mail.logout()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
db_handler.close() # Use the global db_handler
|
||||||
|
print("Cleanup complete. Exiting.")
|
||||||
|
logging.info(f"Cleanup complete. Exiting.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def multi_account_main():
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
|
accounts = []
|
||||||
|
|
||||||
|
# Check for the old format [EMAIL] section
|
||||||
|
if 'EMAIL' in config.sections():
|
||||||
|
old_account = {
|
||||||
|
'EmailUser': config['EMAIL']['EmailUser'],
|
||||||
|
'EmailPass': config['EMAIL']['EmailPass'],
|
||||||
|
'Host': config['EMAIL']['Host']
|
||||||
|
}
|
||||||
|
accounts.append(old_account)
|
||||||
|
|
||||||
|
# Check for new format [EMAIL:accountX]
|
||||||
|
for section in config.sections():
|
||||||
|
if section.startswith("EMAIL:"):
|
||||||
|
account = {
|
||||||
|
'EmailUser': config[section]['EmailUser'],
|
||||||
|
'EmailPass': config[section]['EmailPass'],
|
||||||
|
'Host': config[section]['Host']
|
||||||
|
}
|
||||||
|
accounts.append(account)
|
||||||
|
|
||||||
|
providers = []
|
||||||
|
|
||||||
|
if 'NTFY' in config:
|
||||||
|
ntfy_urls = [config['NTFY'][url_key] for url_key in config['NTFY']]
|
||||||
|
providers.append(NTFYNotificationProvider(ntfy_urls))
|
||||||
|
|
||||||
|
if 'PUSHOVER' in config:
|
||||||
|
api_token = config['PUSHOVER']['ApiToken']
|
||||||
|
user_key = config['PUSHOVER']['UserKey']
|
||||||
|
providers.append(PushoverNotificationProvider(api_token, user_key))
|
||||||
|
|
||||||
|
if 'GOTIFY' in config:
|
||||||
|
gotify_url = config['GOTIFY']['Url']
|
||||||
|
gotify_token = config['GOTIFY']['Token']
|
||||||
|
providers.append(GotifyNotificationProvider(gotify_url, gotify_token))
|
||||||
|
|
||||||
|
global notifier
|
||||||
|
notifier = Notifier(providers)
|
||||||
|
|
||||||
|
socket.setdefaulttimeout(600)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGTERM, shutdown_handler)
|
||||||
|
signal.signal(signal.SIGINT, shutdown_handler)
|
||||||
|
|
||||||
|
print("Script started. Press Ctrl+C to stop it anytime.")
|
||||||
|
logging.info(f"Script started. Press Ctrl+C to stop it anytime.")
|
||||||
|
|
||||||
|
multi_handler = MultiIMAPHandler(accounts)
|
||||||
|
multi_handler.run()
|
||||||
|
|
||||||
print("Logging out and closing the connection...")
|
print("Logging out and closing the connection...")
|
||||||
logging.info(f"Logging out and closing the connection...")
|
logging.info(f"Logging out and closing the connection...")
|
||||||
try:
|
try:
|
||||||
|
for handler in MultiIMAPHandler.handlers:
|
||||||
handler.mail.logout()
|
handler.mail.logout()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
processor.db_handler.close()
|
processor.db_handler.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
multi_account_main()
|
||||||
|
|
||||||
|
|
14
README.md
14
README.md
|
@ -1,14 +1,16 @@
|
||||||
# NotiMail 📧
|
# NotiMail 📧
|
||||||
|
|
||||||
|
**Version 0.9 is here, featuring support for monitoring multiple email accounts!**
|
||||||
|
|
||||||
**Development is ongoing, and the project is in the early alpha stage - things may break!**
|
**Development is ongoing, and the project is in the early alpha stage - things may break!**
|
||||||
|
|
||||||
Stay connected without the constant drain on your battery. Introducing **NotiMail** - the future of server-side email notifications supporting multiple push providers!
|
Stay connected without the constant drain on your battery. Introducing **NotiMail** - the future of server-side email notifications supporting multiple push providers and multiple email accounts!
|
||||||
|
|
||||||
Mobile devices often use IMAP IDLE, maintaining a persistent connection to ensure real-time email notifications. Such continuous connections rapidly consume battery power. The modern era demanded a smarter, more energy-efficient solution. Meet NotiMail.
|
Mobile devices often use IMAP IDLE, maintaining a persistent connection to ensure real-time email notifications. Such continuous connections rapidly consume battery power. The modern era demanded a smarter, more energy-efficient solution. Meet NotiMail.
|
||||||
|
|
||||||
## Features 🌟
|
## Features 🌟
|
||||||
|
|
||||||
- **Monitors Emails on the Server**: NotiMail checks for new, unseen emails without needing a constant client connection.
|
- **Monitors Multiple Emails on the Server**: With version 0.9, NotiMail can now monitor multiple email accounts. Ensure you never miss an email regardless of which account it's sent to.
|
||||||
|
|
||||||
- **Processes and Notifies**: Once a new email is detected, NotiMail swiftly processes its details.
|
- **Processes and Notifies**: Once a new email is detected, NotiMail swiftly processes its details.
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ Mobile devices often use IMAP IDLE, maintaining a persistent connection to ensur
|
||||||
|
|
||||||
- **Efficient Operation**: Crafted for server-side operations, NotiMail guarantees a smooth, lightweight experience.
|
- **Efficient Operation**: Crafted for server-side operations, NotiMail guarantees a smooth, lightweight experience.
|
||||||
|
|
||||||
- **Customizable Settings**: Through an intuitive `config.ini`, customize NotiMail to fit your needs.
|
- **Customizable Settings**: Through an intuitive `config.ini`, customize NotiMail to fit your needs. With the new 0.9 version, configure multiple email accounts for monitoring.
|
||||||
|
|
||||||
- **Dependable Error Handling**: With robust mechanisms, NotiMail ensures you're notified of any hitches or anomalies.
|
- **Dependable Error Handling**: With robust mechanisms, NotiMail ensures you're notified of any hitches or anomalies.
|
||||||
|
|
||||||
|
@ -111,7 +113,7 @@ bash
|
||||||
|
|
||||||
**5. Configure NotiMail:**
|
**5. Configure NotiMail:**
|
||||||
|
|
||||||
Open the `config.ini` file in a text editor. Update the `[EMAIL]` section with your email credentials and host, and the configuration of one (or more) of the supported push providers.
|
Open the `config.ini` file in a text editor. From version 0.9, you can configure multiple email accounts for monitoring by adding sections like `[EMAIL:account1]`, `[EMAIL:account2]`, etc. If you're upgrading from an earlier version, your old single `[EMAIL]` configuration is still supported. Also, update the configuration for one (or more) of the supported push providers.
|
||||||
|
|
||||||
**6. Run NotiMail:**
|
**6. Run NotiMail:**
|
||||||
|
|
||||||
|
@ -132,4 +134,8 @@ bash
|
||||||
|
|
||||||
With that, you should have NotiMail up and running on your system! Enjoy a more efficient email notification experience.
|
With that, you should have NotiMail up and running on your system! Enjoy a more efficient email notification experience.
|
||||||
|
|
||||||
|
### Changelog:
|
||||||
|
|
||||||
|
- **Version 0.9:**
|
||||||
|
- Introduced support for monitoring multiple email accounts. Configure multiple accounts in the `config.ini` using the format `[EMAIL:account1]`, `[EMAIL:account2]`, and so on.
|
||||||
|
- Maintained compatibility with the old single `[EMAIL]` configuration for a smooth upgrade path.
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
[EMAIL]
|
[EMAIL:account1]
|
||||||
EmailUser = your@address.com
|
EmailUser = your@address.com
|
||||||
EmailPass = YourPassword
|
EmailPass = YourPassword
|
||||||
Host = mail.server.com
|
Host = mail.server.com
|
||||||
|
|
||||||
|
#[EMAIL:account2]
|
||||||
|
#EmailUser = your@address.com
|
||||||
|
#EmailPass = YourPassword
|
||||||
|
#Host = mail.server.com
|
||||||
|
|
||||||
#If your provider is NTFY, uncomment the following lines and configure
|
#If your provider is NTFY, uncomment the following lines and configure
|
||||||
#[NTFY]
|
#[NTFY]
|
||||||
#Url1 = https://ntfy.sh/TOPIC1
|
#Url1 = https://ntfy.sh/TOPIC1
|
||||||
|
|
Loading…
Reference in a new issue