Search code examples
pythonflaskflask-mail

How to adapt flask-mail to support two SMTP accounts


I am using flask-mail to send from one account and it works fine. The unique information about multiple smtp accounts that I found in Google is this old comment.

What I have:

MAIL_USERNAME = config.get('mail_service', 'USER')
MAIL_PASSWORD = config.get('mail_service', 'PASSWD')
MAIL_SERVER = config.get('mail_service', 'MAIL_SERVER')
MAIL_PORT = 587
MAIL_USE_TLS = True

mail = Mail()

def create_app(config_name):
    app = Flask(__name__)
    mail.init_app(app)
    ...

from flask_mail import Message
@app.route("/")
def index():
    msg = Message("Hello", sender="[email protected]", recipients=["[email protected]"])

I am not sure what is the best approach. Probably when sending each mail specify one of both configured SMTP accounts?

Any idea how to implement that?


Solution

  • You can use a config.json file to store the config for both accounts. Then, a custom function can use that file to extract values whenever required. The following code shows a simple setup of two accounts. The key requirement is to update the app config before initializing the app with the mail object(mail.init_app()) in each route. Each smtp account has its "message send" operation in its own route.

    config.json:

    Config for two SMTP Gmail accounts

    {
        "MAIL_SERVER" : "smtp.gmail.com",
        "MAIL_PORT" : 587,    
        "MAIL_USE_TLS": "True", 
        "MAIL_USERNAME" : ["[email protected]", "[email protected]"],
        "MAIL_PASSWORD" : ["pwd_for_smtp1", "pwd_for_smtp2"]
    }
    

    Code:

    To test this code, I sent test emails from [email protected] to [email protected] and vice versa. You should get the respective message displayed for each route when you access the route on your localhost.

    Note: For security reasons, you should use separate app passwords for authentication which should be generated for each SMTP Gmail account. The app passwords should also be updated in config.json above for the MAIL_PASSWORD key for each account. More details here.

    from flask import Flask
    from flask_mail import Mail
    from flask_mail import Message
    import json
    
    def smtp_config(config_name, smtp=1):
        with open(config_name) as f:
                config_data = json.load(f)
        if smtp not in {1,2}:
            raise ValueError("smtp can only be 1 or 2")
        if smtp==2:
            MAIL_USERNAME = config_data['MAIL_USERNAME'][1]
            MAIL_PASSWORD = config_data['MAIL_PASSWORD'][1]
        else:
            MAIL_USERNAME = config_data['MAIL_USERNAME'][0]         
            MAIL_PASSWORD = config_data['MAIL_PASSWORD'][0]        
        MAIL_SERVER = config_data['MAIL_SERVER']
        MAIL_PORT = config_data['MAIL_PORT']    
        MAIL_USE_TLS = bool(config_data['MAIL_USE_TLS'])
        return [MAIL_USERNAME, MAIL_PASSWORD, MAIL_SERVER, MAIL_PORT, MAIL_USE_TLS]
    
    app = Flask(__name__)
    mail = Mail()
    
    @app.route("/")
    def index():    
        smtp_data = smtp_config('config.json', smtp=1)
        app.config.update(dict(
        MAIL_SERVER = smtp_data[2],
        MAIL_PORT = smtp_data[3],
        MAIL_USE_TLS = smtp_data[4],    
        MAIL_USERNAME = smtp_data[0],
        MAIL_PASSWORD = smtp_data[1],
        ))
        mail.init_app(app)   
        msg = Message("Hello", sender="[email protected]", recipients=["[email protected]"])    
        msg.body = "This message was sent from smtp1"
        mail.send(msg)
        return "The message was sent from smtp1"
    
    @app.route("/smtp2/")
    def smtp2():        
        smtp_data = smtp_config('config.json', smtp=2)
        app.config.update(dict(
        MAIL_SERVER = smtp_data[2],
        MAIL_PORT = smtp_data[3],
        MAIL_USE_TLS = smtp_data[4],    
        MAIL_USERNAME = smtp_data[0],
        MAIL_PASSWORD = smtp_data[1],
        ))
        mail.init_app(app)  
        msg = Message("Hello", sender="[email protected]", recipients=["[email protected]"])    
        msg.body = "This message was sent from smtp2"
        mail.send(msg)
        return "The message was sent from smtp2"
    
    if __name__=='__main__':    
        app.run(debug=True, port=5000, host='localhost')  
    

    The smtp_config() function accepts two args: config_name which is the path of the config.json file and smtp which has a default value of 1 for smtp1 account config. This parameter can either be 1 or 2. The function returns a list of values required for mail configuration for the particular smtp.

    Then, in each route, just update the app config with the values received from the above function and then initialize the mail settings from the application settings(mail.init_app()).

    To add more accounts, you can pass a list of smtp account names to smtp for unique identification(instead of numbers 1 & 2 above). Of course, you'd also have to modify config.json accordingly:

    def smtp_config(config_name, smtp=['[email protected]', '[email protected]', '[email protected]'....]):
        #<---code--->
        if x[0]:
            MAIL_USERNAME = '[email protected]'
            ....
        elif x[1]:
            MAIL_USERNAME = '[email protected]'
            ....