mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-09-29 20:00:10 +00:00
Harvey Tindall
e8ad3f98d6
The admin page now has the option to send an invite to an email address. Since there are now two email types (invites and pw resets), the new sections have been added to config.ini, and email_template and email_plaintext have been renamed to email_html and email_text respectively.
167 lines
7.0 KiB
Python
167 lines
7.0 KiB
Python
import datetime
|
|
import pytz
|
|
import requests
|
|
import smtplib
|
|
import ssl
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
from pathlib import Path
|
|
from dateutil import parser as date_parser
|
|
from jinja2 import Environment, FileSystemLoader
|
|
from __main__ import config
|
|
from __main__ import email_log as log
|
|
|
|
|
|
class Email():
|
|
def __init__(self, address):
|
|
self.address = address
|
|
log.debug(f'{self.address}: Creating email')
|
|
self.content = {}
|
|
self.from_address = config['email']['address']
|
|
self.from_name = config['email']['from']
|
|
log.debug((
|
|
f'{self.address}: Sending from {self.from_address} ' +
|
|
f'({self.from_name})'))
|
|
|
|
def pretty_time(self, expiry):
|
|
current_time = datetime.datetime.now()
|
|
date = expiry.strftime(config['email']['date_format'])
|
|
if config.getboolean('email', 'use_24h'):
|
|
log.debug(f'{self.address}: Using 24h time')
|
|
time = expiry.strftime('%H:%M')
|
|
else:
|
|
log.debug(f'{self.address}: Using 12h time')
|
|
time = expiry.strftime('%-I:%M %p')
|
|
expiry_delta = (expiry - current_time).seconds
|
|
expires_in = {'hours': expiry_delta//3600,
|
|
'minutes': (expiry_delta//60) % 60}
|
|
if expires_in['hours'] == 0:
|
|
expires_in = f'{str(expires_in["minutes"])}m'
|
|
else:
|
|
expires_in = (f'{str(expires_in["hours"])}h ' +
|
|
f'{str(expires_in["minutes"])}m')
|
|
log.debug(f'{self.address}: Expires in {expires_in}')
|
|
return {'date': date, 'time': time, 'expires_in': expires_in}
|
|
|
|
def construct_invite(self, invite):
|
|
self.subject = config['invite_emails']['subject']
|
|
log.debug(f'{self.address}: Using subject {self.subject}')
|
|
log.debug(f'{self.address}: Constructing email content')
|
|
expiry = invite['expiry']
|
|
expiry.replace(tzinfo=None)
|
|
pretty = self.pretty_time(expiry)
|
|
email_message = config['email']['message']
|
|
invite_link = config['invite_emails']['url_base']
|
|
invite_link += '/' + invite['code']
|
|
for key in ['text', 'html']:
|
|
sp = Path(config['invite_emails']['email_' + key]) / '..'
|
|
sp = str(sp.resolve()) + '/'
|
|
template_loader = FileSystemLoader(searchpath=sp)
|
|
template_env = Environment(loader=template_loader)
|
|
fname = Path(config['invite_emails']['email_' + key]).name
|
|
template = template_env.get_template(fname)
|
|
c = template.render(expiry_date=pretty['date'],
|
|
expiry_time=pretty['time'],
|
|
expires_in=pretty['expires_in'],
|
|
invite_link=invite_link,
|
|
message=email_message)
|
|
self.content[key] = c
|
|
log.info(f'{self.address}: {key} constructed')
|
|
|
|
def construct_reset(self, reset):
|
|
self.subject = config['password_resets']['subject']
|
|
log.debug(f'{self.address}: Using subject {self.subject}')
|
|
log.debug(f'{self.address}: Constructing email content')
|
|
try:
|
|
expiry = date_parser.parse(reset['ExpirationDate'])
|
|
expiry = expiry.replace(tzinfo=None)
|
|
except:
|
|
log.error(f"{self.address}: Couldn't parse expiry time")
|
|
return False
|
|
current_time = datetime.datetime.now()
|
|
if expiry >= current_time:
|
|
log.debug(f'{self.address}: Invite valid')
|
|
pretty = self.pretty_time(expiry)
|
|
email_message = config['email']['message']
|
|
for key in ['text', 'html']:
|
|
sp = Path(config['password_resets']['email_' + key]) / '..'
|
|
sp = str(sp.resolve()) + '/'
|
|
template_loader = FileSystemLoader(searchpath=sp)
|
|
template_env = Environment(loader=template_loader)
|
|
fname = Path(config['password_resets']['email_' + key]).name
|
|
template = template_env.get_template(fname)
|
|
c = template.render(username=reset['UserName'],
|
|
expiry_date=pretty['date'],
|
|
expiry_time=pretty['time'],
|
|
expires_in=pretty['expires_in'],
|
|
pin=reset['Pin'],
|
|
message=email_message)
|
|
self.content[key] = c
|
|
log.info(f'{self.address}: {key} constructed')
|
|
return True
|
|
else:
|
|
err = ((f"{self.address}: " +
|
|
"Reset has reportedly already expired. " +
|
|
"Ensure timezones are correctly configured."))
|
|
log.error(err)
|
|
return False
|
|
|
|
|
|
class Mailgun(Email):
|
|
def __init__(self, address):
|
|
super().__init__(address)
|
|
self.api_url = config['mailgun']['api_url']
|
|
self.api_key = config['mailgun']['api_key']
|
|
self.from_mg = f'{self.from_name} <{self.from_address}>'
|
|
|
|
def send(self):
|
|
response = requests.post(self.api_url,
|
|
auth=("api", self.api_key),
|
|
data={"from": self.from_mg,
|
|
"to": [self.address],
|
|
"subject": self.subject,
|
|
"text": self.content['text'],
|
|
"html": self.content['html']})
|
|
if response.ok:
|
|
log.info(f'{self.address}: Sent via mailgun.')
|
|
return True
|
|
log.debug(f'{self.address}: Mailgun: {response.status_code}')
|
|
return response
|
|
|
|
|
|
class Smtp(Email):
|
|
def __init__(self, address):
|
|
super().__init__(address)
|
|
self.server = config['smtp']['server']
|
|
self.password = config['smtp']['password']
|
|
try:
|
|
self.port = int(config['smtp']['port'])
|
|
except ValueError:
|
|
self.port = 465
|
|
log.debug(f'{self.address}: Defaulting to port {self.port}')
|
|
self.context = ssl.create_default_context()
|
|
|
|
def send(self):
|
|
message = MIMEMultipart("alternative")
|
|
message["Subject"] = self.subject
|
|
message["From"] = self.from_address
|
|
message["To"] = self.address
|
|
text = MIMEText(self.content['text'], 'plain')
|
|
html = MIMEText(self.content['html'], 'html')
|
|
message.attach(text)
|
|
message.attach(html)
|
|
try:
|
|
with smtplib.SMTP_SSL(self.server,
|
|
self.port,
|
|
context=self.context) as server:
|
|
server.login(self.from_address, self.password)
|
|
server.sendmail(self.from_address,
|
|
self.address,
|
|
message.as_string())
|
|
log.info(f'{self.address}: Sent via smtp')
|
|
return True
|
|
except Exception as e:
|
|
err = f'{self.address}: Failed to send via smtp: '
|
|
err += type(e).__name__
|
|
log.error(err)
|