plexpy/plexpy/newsletter_handler.py
2024-09-25 11:54:55 -07:00

230 lines
10 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of Tautulli.
#
# Tautulli is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Tautulli is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
from io import open
import os
import shlex
from apscheduler.triggers.cron import CronTrigger
import email.utils
import plexpy
from plexpy import database
from plexpy import helpers
from plexpy import logger
from plexpy import newsletters
NEWSLETTER_SCHED = None
def add_newsletter_each(newsletter_id=None, notify_action=None, **kwargs):
if not notify_action:
logger.debug("Tautulli NewsletterHandler :: Notify called but no action received.")
return
data = {'newsletter': True,
'newsletter_id': newsletter_id,
'notify_action': notify_action}
data.update(kwargs)
plexpy.NOTIFY_QUEUE.put(data)
def schedule_newsletters(newsletter_id=None):
newsletters_list = newsletters.get_newsletters(newsletter_id=newsletter_id)
for newsletter in newsletters_list:
newsletter_job_name = '{} (newsletter_id {})'.format(newsletter['agent_label'], newsletter['id'])
if newsletter['active']:
schedule_newsletter_job('newsletter-{}'.format(newsletter['id']), name=newsletter_job_name,
func=add_newsletter_each, args=[newsletter['id'], 'on_cron'], cron=newsletter['cron'])
else:
schedule_newsletter_job('newsletter-{}'.format(newsletter['id']), name=newsletter_job_name,
remove_job=True)
def schedule_newsletter_job(newsletter_job_id, name='', func=None, remove_job=False, args=None, cron=None):
if cron:
values = shlex.split(cron)
# apscheduler day_of_week uses 0-6 = mon-sun
values[4] = str((int(values[4]) - 1) % 7) if values[4].isdigit() else values[4]
if NEWSLETTER_SCHED.get_job(newsletter_job_id):
if remove_job:
NEWSLETTER_SCHED.remove_job(newsletter_job_id)
logger.info("Tautulli NewsletterHandler :: Removed scheduled newsletter: %s" % name)
else:
try:
NEWSLETTER_SCHED.reschedule_job(
newsletter_job_id, args=args, trigger=CronTrigger(
minute=values[0], hour=values[1], day=values[2], month=values[3], day_of_week=values[4]
)
)
logger.info("Tautulli NewsletterHandler :: Re-scheduled newsletter: %s" % name)
except ValueError as e:
logger.error("Tautulli NewsletterHandler :: Failed to re-schedule newsletter: %s" % e)
elif not remove_job:
try:
NEWSLETTER_SCHED.add_job(
func, args=args, id=newsletter_job_id, trigger=CronTrigger(
minute=values[0], hour=values[1], day=values[2], month=values[3], day_of_week=values[4]
),
misfire_grace_time=None
)
logger.info("Tautulli NewsletterHandler :: Scheduled newsletter: %s" % name)
except ValueError as e:
logger.error("Tautulli NewsletterHandler :: Failed to schedule newsletter: %s" % e)
def notify(newsletter_id=None, notify_action=None, **kwargs):
logger.info("Tautulli NewsletterHandler :: Preparing newsletter for newsletter_id %s." % newsletter_id)
newsletter_config = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
if not newsletter_config:
return
if notify_action in ('test', 'api'):
subject = kwargs.pop('subject', None) or newsletter_config['subject']
body = kwargs.pop('body', None) or newsletter_config['body']
message = kwargs.pop('message', None) or newsletter_config['message']
else:
subject = newsletter_config['subject']
body = newsletter_config['body']
message = newsletter_config['message']
email_msg_id = email.utils.make_msgid()
email_reply_msg_id = get_last_newsletter_email_msg_id(newsletter_id=newsletter_id, notify_action=notify_action)
newsletter_agent = newsletters.get_agent_class(newsletter_id=newsletter_id,
newsletter_id_name=newsletter_config['id_name'],
agent_id=newsletter_config['agent_id'],
config=newsletter_config['config'],
email_config=newsletter_config['email_config'],
subject=subject,
body=body,
message=message,
email_msg_id=email_msg_id,
email_reply_msg_id=email_reply_msg_id
)
# Set the newsletter state in the db
newsletter_log_id = set_notify_state(newsletter=newsletter_config,
notify_action=notify_action,
subject=newsletter_agent.subject_formatted,
body=newsletter_agent.body_formatted,
message=newsletter_agent.message_formatted,
filename=newsletter_agent.filename_formatted,
start_date=newsletter_agent.start_date.format('YYYY-MM-DD'),
end_date=newsletter_agent.end_date.format('YYYY-MM-DD'),
start_time=newsletter_agent.start_time,
end_time=newsletter_agent.end_time,
newsletter_uuid=newsletter_agent.uuid,
email_msg_id=email_msg_id)
# Send the notification
success = newsletter_agent.send()
if success:
set_notify_success(newsletter_log_id)
return True
def set_notify_state(newsletter, notify_action, subject, body, message, filename,
start_date, end_date, start_time, end_time, newsletter_uuid, email_msg_id):
if newsletter and notify_action:
db = database.MonitorDatabase()
keys = {'timestamp': helpers.timestamp(),
'uuid': newsletter_uuid}
values = {'newsletter_id': newsletter['id'],
'agent_id': newsletter['agent_id'],
'agent_name': newsletter['agent_name'],
'notify_action': notify_action,
'subject_text': subject,
'body_text': body,
'message_text': message,
'start_date': start_date,
'end_date': end_date,
'start_time': start_time,
'end_time': end_time,
'email_msg_id': email_msg_id,
'filename': filename}
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
return db.last_insert_id()
else:
logger.error("Tautulli NewsletterHandler :: Unable to set notify state.")
def set_notify_success(newsletter_log_id):
keys = {'id': newsletter_log_id}
values = {'success': 1}
db = database.MonitorDatabase()
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
def get_last_newsletter_email_msg_id(newsletter_id, notify_action):
db = database.MonitorDatabase()
result = db.select_single("SELECT email_msg_id FROM newsletter_log "
"WHERE newsletter_id = ? AND notify_action = ? AND success = 1 "
"ORDER BY timestamp DESC LIMIT 1", [newsletter_id, notify_action])
if result:
return result['email_msg_id']
def get_newsletter(newsletter_uuid=None, newsletter_id_name=None):
db = database.MonitorDatabase()
if newsletter_uuid:
result = db.select_single("SELECT start_date, end_date, uuid, filename FROM newsletter_log "
"WHERE uuid = ?", [newsletter_uuid])
elif newsletter_id_name:
result = db.select_single("SELECT start_date, end_date, uuid, filename FROM newsletter_log "
"JOIN newsletters ON newsletters.id = newsletter_log.newsletter_id "
"WHERE id_name = ? AND notify_action != 'test' "
"ORDER BY timestamp DESC LIMIT 1", [newsletter_id_name])
else:
result = None
if result:
newsletter_uuid = result['uuid']
start_date = result['start_date']
end_date = result['end_date']
newsletter_file = result['filename'] or 'newsletter_%s-%s_%s.html' % (start_date.replace('-', ''),
end_date.replace('-', ''),
newsletter_uuid)
newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR or os.path.join(plexpy.DATA_DIR, 'newsletters')
newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)
if newsletter_file in os.listdir(newsletter_folder):
try:
with open(newsletter_file_fp, 'r', encoding='utf-8') as n_file:
newsletter = n_file.read()
return newsletter
except OSError as e:
logger.error("Tautulli NewsletterHandler :: Failed to retrieve newsletter '%s': %s" % (newsletter_uuid, e))
else:
logger.warn("Tautulli NewsletterHandler :: Newsletter file '%s' is missing." % newsletter_file)