diff --git a/data/interfaces/default/newsletter_config.html b/data/interfaces/default/newsletter_config.html index 36c02c4d..d558c3d1 100644 --- a/data/interfaces/default/newsletter_config.html +++ b/data/interfaces/default/newsletter_config.html @@ -439,7 +439,7 @@ }); function previewNewsletter() { - window.open('preview_newsletter?newsletter_id=${newsletter["id"]}'); + window.open('preview_newsletter?newsletter_id=' + $('#newsletter_id').val()); } function sendTestNewsletter() { @@ -447,8 +447,9 @@ $.ajax({ url: 'send_newsletter', data: { - newsletter_id: '${newsletter["id"]}', - test: true + newsletter_id: $('#newsletter_id').val(), + subject: $('#email_subject').val(), + notify_action: 'test' }, cache: false, async: true, diff --git a/data/interfaces/default/notifier_config.html b/data/interfaces/default/notifier_config.html index 24688b34..8514f9ea 100644 --- a/data/interfaces/default/notifier_config.html +++ b/data/interfaces/default/notifier_config.html @@ -755,7 +755,7 @@ $.ajax({ url: 'send_notification', data: { - notifier_id: '${notifier["id"]}', + notifier_id: $('#notifier_id').val(), subject: $('#test_subject').val(), body: $('#test_body').val(), script: $('#test_script').val(), diff --git a/plexpy/__init__.py b/plexpy/__init__.py index 680c5451..49a2e9f3 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -426,7 +426,7 @@ def initialize_scheduler(): logger.error(e) -def schedule_job(function, name, hours=0, minutes=0, seconds=0, args=None): +def schedule_job(func, name, hours=0, minutes=0, seconds=0, args=None): """ Start scheduled job if starting or restarting plexpy. Reschedule job if Interval Settings have changed. @@ -444,7 +444,7 @@ def schedule_job(function, name, hours=0, minutes=0, seconds=0, args=None): hours=hours, minutes=minutes, seconds=seconds), args=args) logger.info(u"Re-scheduled background task: %s", name) elif hours > 0 or minutes > 0 or seconds > 0: - SCHED.add_job(function, id=name, trigger=IntervalTrigger( + SCHED.add_job(func, id=name, trigger=IntervalTrigger( hours=hours, minutes=minutes, seconds=seconds), args=args) logger.info(u"Scheduled background task: %s", name) @@ -574,14 +574,6 @@ def dbcheck(): 'filter_music TEXT, filter_photos TEXT)' ) - # notify_log table :: This is a table which logs notifications sent - c_db.execute( - 'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, ' - 'session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, ' - 'user_id INTEGER, user TEXT, notifier_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, ' - 'subject_text TEXT, body_text TEXT, script_args TEXT, success INTEGER DEFAULT 0)' - ) - # library_sections table :: This table keeps record of the servers library sections c_db.execute( 'CREATE TABLE IF NOT EXISTS library_sections (id INTEGER PRIMARY KEY AUTOINCREMENT, ' @@ -620,6 +612,14 @@ def dbcheck(): 'custom_conditions TEXT, custom_conditions_logic TEXT)' ) + # notify_log table :: This is a table which logs notifications sent + c_db.execute( + 'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, ' + 'session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, ' + 'user_id INTEGER, user TEXT, notifier_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, ' + 'subject_text TEXT, body_text TEXT, script_args TEXT, success INTEGER DEFAULT 0)' + ) + # newsletters table :: This table keeps record of the newsletter settings c_db.execute( 'CREATE TABLE IF NOT EXISTS newsletters (id INTEGER PRIMARY KEY AUTOINCREMENT, ' @@ -628,6 +628,13 @@ def dbcheck(): 'cron TEXT NOT NULL DEFAULT "0 0 * * 0", active INTEGER DEFAULT 0)' ) + # newsletter_log table :: This is a table which logs newsletters sent + c_db.execute( + 'CREATE TABLE IF NOT EXISTS newsletter_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, ' + 'newsletter_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, ' + 'subject_text TEXT, success INTEGER DEFAULT 0)' + ) + # poster_urls table :: This table keeps record of the notification poster urls c_db.execute( 'CREATE TABLE IF NOT EXISTS poster_urls (id INTEGER PRIMARY KEY AUTOINCREMENT, ' diff --git a/plexpy/newsletter_handler.py b/plexpy/newsletter_handler.py new file mode 100644 index 00000000..d3031080 --- /dev/null +++ b/plexpy/newsletter_handler.py @@ -0,0 +1,102 @@ +# 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 . + +import time + +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger + +import plexpy +import database +import logger +import newsletters + + +NEWSLETTER_SCHED = BackgroundScheduler() + + +def schedule_newsletter(newsletter_id, func=None, remove_job=False, args=None, **kwargs): + if NEWSLETTER_SCHED.get_job(newsletter_id): + if remove_job: + NEWSLETTER_SCHED.remove_job(newsletter_id) + else: + NEWSLETTER_SCHED.reschedule_job( + newsletter_id, args=args, trigger=CronTrigger(**kwargs)) + elif not remove_job: + NEWSLETTER_SCHED.add_job( + func, args=args, id=newsletter_id, trigger=CronTrigger(**kwargs)) + + +def notify(newsletter_id=None, notify_action=None, **kwargs): + logger.info(u"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_string = kwargs.pop('subject', 'Tautulli Newsletter') + else: + # Get the subject string + subject_string = newsletter_config['subject'] + + newsletter_agent = newsletters.get_agent_class(agent_id=newsletter_config['agent_id'], + config=newsletter_config['config'], + email_config=newsletter_config['email_config']) + subject = newsletter_agent.format_subject(subject_string) + + # Set the newsletter state in the db + newsletter_log_id = set_notify_state(newsletter=newsletter_config, + notify_action=notify_action, + subject=subject) + + # Send the notification + success = newsletters.send_newsletter(newsletter_id=newsletter_config['id'], + subject=subject, + notify_action=notify_action, + newsletter_log_id=newsletter_log_id, + **kwargs) + + if success: + set_notify_success(newsletter_log_id) + return True + + +def set_notify_state(newsletter, notify_action, subject): + + if newsletter and notify_action: + monitor_db = database.MonitorDatabase() + + keys = {'timestamp': int(time.time()), + 'newsletter_id': newsletter['id'], + 'agent_id': newsletter['agent_id'], + 'notify_action': notify_action} + + values = {'agent_name': newsletter['agent_name'], + 'subject_text': subject} + + monitor_db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values) + return monitor_db.last_insert_id() + else: + logger.error(u"Tautulli NewsletterHandler :: Unable to set notify state.") + + +def set_notify_success(newsletter_log_id): + keys = {'id': newsletter_log_id} + values = {'success': 1} + + monitor_db = database.MonitorDatabase() + monitor_db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values) diff --git a/plexpy/newsletters.py b/plexpy/newsletters.py index 99ff0d39..c9bb9501 100644 --- a/plexpy/newsletters.py +++ b/plexpy/newsletters.py @@ -50,6 +50,19 @@ def available_newsletter_agents(): return agents +def available_notification_actions(): + actions = [{'label': 'Schedule', + 'name': 'on_cron', + 'description': 'Trigger a notification on a certain schedule.', + 'subject': 'Tautulli Newsletter', + 'icon': 'fa-calendar', + 'media_types': ('newsletter',) + } + ] + + return actions + + def get_agent_class(agent_id=None, config=None, email_config=None): if str(agent_id).isdigit(): agent_id = int(agent_id) @@ -213,13 +226,15 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs): return False -def send_newsletter(newsletter_id=None, newsletter_log_id=None, **kwargs): +def send_newsletter(newsletter_id=None, subject=None, notify_action='', newsletter_log_id=None, **kwargs): newsletter_config = get_newsletter_config(newsletter_id=newsletter_id) if newsletter_config: agent = get_agent_class(agent_id=newsletter_config['agent_id'], config=newsletter_config['config'], email_config=newsletter_config['email_config']) - return agent.send(newsletter_log_id=newsletter_log_id, **kwargs) + return agent.send(subject=subject, + action=notify_action.split('on_')[-1], + newsletter_log_id=newsletter_log_id, **kwargs) else: logger.debug(u"Tautulli Newsletters :: Notification requested but no newsletter_id received.") @@ -284,7 +299,7 @@ class Newsletter(object): **kwargs ) - def _format_subject(self, subject): + def format_subject(self, subject): subject = subject or self._DEFAULT_EMAIL_CONFIG['subject'] try: @@ -313,10 +328,10 @@ class Newsletter(object): self.retrieve_data() return self.generate_newsletter() - def send(self, **kwargs): + def send(self, subject='', **kwargs): self.retrieve_data() - subject = self._format_subject(self.email_config['subject']) + subject = self.format_subject(subject or self.email_config['subject']) newsletter = self.generate_newsletter() if self.email_config['notifier']: diff --git a/plexpy/webserve.py b/plexpy/webserve.py index fc3f16c8..9eb097f6 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -39,6 +39,7 @@ import http_handler import libraries import log_reader import logger +import newsletter_handler import newsletters import mobile_app import notification_handler @@ -5478,7 +5479,7 @@ class WebInterface(object): @cherrypy.expose @requireAuth(member_of("admin")) @addtoapi("notify_newsletter") - def send_newsletter(self, newsletter_id=None, test=False, **kwargs): + def send_newsletter(self, newsletter_id=None, subject='Tautulli Newsletter', notify_action='', **kwargs): """ Send a newsletter using Tautulli. ``` @@ -5494,14 +5495,17 @@ class WebInterface(object): """ cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" - test = 'test ' if test else '' + test = 'test ' if notify_action == 'test' else '' if newsletter_id: newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id) if newsletter: logger.debug(u"Sending %s%s newsletter." % (test, newsletter['agent_label'])) - if newsletters.send_newsletter(newsletter_id=newsletter_id): + if newsletter_handler.notify(newsletter_id=newsletter_id, + notify_action=notify_action, + subject=subject, + **kwargs): return "Newsletter sent." else: return "Newsletter failed." @@ -5518,7 +5522,10 @@ class WebInterface(object): if newsletter_id: newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id) newsletter_agent = newsletters.get_agent_class(agent_id=newsletter['agent_id'], config=newsletter['config']) + if newsletter_agent: return newsletter_agent.preview(master=master) - return + return "Invalid newsletter id %s" % newsletter_id + + return "Missing newsletter_id parameter"