plexpy/plexpy/notifiers.py
2016-01-23 08:12:41 -08:00

2131 lines
86 KiB
Python

# This file is part of PlexPy.
#
# PlexPy 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.
#
# PlexPy 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 PlexPy. If not, see <http://www.gnu.org/licenses/>.
from urlparse import urlparse
import base64
import json
import cherrypy
from email.mime.text import MIMEText
import email.utils
from httplib import HTTPSConnection
import os
import shlex
import smtplib
import subprocess
from urllib import urlencode
import urllib
import urllib2
from urlparse import parse_qsl
from pynma import pynma
import gntp.notifier
import oauth2 as oauth
import pythontwitter as twitter
import pythonfacebook as facebook
import plexpy
from plexpy import logger, helpers, request
from plexpy.helpers import checked
AGENT_IDS = {"Growl": 0,
"Prowl": 1,
"XBMC": 2,
"Plex": 3,
"NMA": 4,
"Pushalot": 5,
"Pushbullet": 6,
"Pushover": 7,
"OSX Notify": 8,
"Boxcar2": 9,
"Email": 10,
"Twitter": 11,
"IFTTT": 12,
"Telegram": 13,
"Slack": 14,
"Scripts": 15,
"Facebook": 16}
def available_notification_agents():
agents = [{'name': 'Growl',
'id': AGENT_IDS['Growl'],
'config_prefix': 'growl',
'has_config': True,
'state': checked(plexpy.CONFIG.GROWL_ENABLED),
'on_play': plexpy.CONFIG.GROWL_ON_PLAY,
'on_stop': plexpy.CONFIG.GROWL_ON_STOP,
'on_pause': plexpy.CONFIG.GROWL_ON_PAUSE,
'on_resume': plexpy.CONFIG.GROWL_ON_RESUME,
'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED,
'on_created': plexpy.CONFIG.GROWL_ON_CREATED,
'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.GROWL_ON_EXTUP,
'on_intup': plexpy.CONFIG.GROWL_ON_INTUP
},
{'name': 'Prowl',
'id': AGENT_IDS['Prowl'],
'config_prefix': 'prowl',
'has_config': True,
'state': checked(plexpy.CONFIG.PROWL_ENABLED),
'on_play': plexpy.CONFIG.PROWL_ON_PLAY,
'on_stop': plexpy.CONFIG.PROWL_ON_STOP,
'on_pause': plexpy.CONFIG.PROWL_ON_PAUSE,
'on_resume': plexpy.CONFIG.PROWL_ON_RESUME,
'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED,
'on_created': plexpy.CONFIG.PROWL_ON_CREATED,
'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PROWL_ON_EXTUP,
'on_intup': plexpy.CONFIG.PROWL_ON_INTUP
},
{'name': 'XBMC',
'id': AGENT_IDS['XBMC'],
'config_prefix': 'xbmc',
'has_config': True,
'state': checked(plexpy.CONFIG.XBMC_ENABLED),
'on_play': plexpy.CONFIG.XBMC_ON_PLAY,
'on_stop': plexpy.CONFIG.XBMC_ON_STOP,
'on_pause': plexpy.CONFIG.XBMC_ON_PAUSE,
'on_resume': plexpy.CONFIG.XBMC_ON_RESUME,
'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER,
'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED,
'on_created': plexpy.CONFIG.XBMC_ON_CREATED,
'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN,
'on_extup': plexpy.CONFIG.XBMC_ON_EXTUP,
'on_intup': plexpy.CONFIG.XBMC_ON_INTUP
},
{'name': 'Plex',
'id': AGENT_IDS['Plex'],
'config_prefix': 'plex',
'has_config': True,
'state': checked(plexpy.CONFIG.PLEX_ENABLED),
'on_play': plexpy.CONFIG.PLEX_ON_PLAY,
'on_stop': plexpy.CONFIG.PLEX_ON_STOP,
'on_pause': plexpy.CONFIG.PLEX_ON_PAUSE,
'on_resume': plexpy.CONFIG.PLEX_ON_RESUME,
'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER,
'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED,
'on_created': plexpy.CONFIG.PLEX_ON_CREATED,
'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PLEX_ON_EXTUP,
'on_intup': plexpy.CONFIG.PLEX_ON_INTUP
},
{'name': 'NotifyMyAndroid',
'id': AGENT_IDS['NMA'],
'config_prefix': 'nma',
'has_config': True,
'state': checked(plexpy.CONFIG.NMA_ENABLED),
'on_play': plexpy.CONFIG.NMA_ON_PLAY,
'on_stop': plexpy.CONFIG.NMA_ON_STOP,
'on_pause': plexpy.CONFIG.NMA_ON_PAUSE,
'on_resume': plexpy.CONFIG.NMA_ON_RESUME,
'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER,
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED,
'on_created': plexpy.CONFIG.NMA_ON_CREATED,
'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN,
'on_extup': plexpy.CONFIG.NMA_ON_EXTUP,
'on_intup': plexpy.CONFIG.NMA_ON_INTUP
},
{'name': 'Pushalot',
'id': AGENT_IDS['Pushalot'],
'config_prefix': 'pushalot',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED),
'on_play': plexpy.CONFIG.PUSHALOT_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP,
'on_pause': plexpy.CONFIG.PUSHALOT_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED,
'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHALOT_ON_EXTUP,
'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP
},
{'name': 'Pushbullet',
'id': AGENT_IDS['Pushbullet'],
'config_prefix': 'pushbullet',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
'on_play': plexpy.CONFIG.PUSHBULLET_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP,
'on_pause': plexpy.CONFIG.PUSHBULLET_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED,
'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHBULLET_ON_EXTUP,
'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP
},
{'name': 'Pushover',
'id': AGENT_IDS['Pushover'],
'config_prefix': 'pushover',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED),
'on_play': plexpy.CONFIG.PUSHOVER_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHOVER_ON_STOP,
'on_pause': plexpy.CONFIG.PUSHOVER_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED,
'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHOVER_ON_EXTUP,
'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP
},
{'name': 'Boxcar2',
'id': AGENT_IDS['Boxcar2'],
'config_prefix': 'boxcar',
'has_config': True,
'state': checked(plexpy.CONFIG.BOXCAR_ENABLED),
'on_play': plexpy.CONFIG.BOXCAR_ON_PLAY,
'on_stop': plexpy.CONFIG.BOXCAR_ON_STOP,
'on_pause': plexpy.CONFIG.BOXCAR_ON_PAUSE,
'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME,
'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER,
'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED,
'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED,
'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN,
'on_extup': plexpy.CONFIG.BOXCAR_ON_EXTUP,
'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP
},
{'name': 'E-mail',
'id': AGENT_IDS['Email'],
'config_prefix': 'email',
'has_config': True,
'state': checked(plexpy.CONFIG.EMAIL_ENABLED),
'on_play': plexpy.CONFIG.EMAIL_ON_PLAY,
'on_stop': plexpy.CONFIG.EMAIL_ON_STOP,
'on_pause': plexpy.CONFIG.EMAIL_ON_PAUSE,
'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED,
'on_created': plexpy.CONFIG.EMAIL_ON_CREATED,
'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.EMAIL_ON_EXTUP,
'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP
},
{'name': 'Twitter',
'id': AGENT_IDS['Twitter'],
'config_prefix': 'twitter',
'has_config': True,
'state': checked(plexpy.CONFIG.TWITTER_ENABLED),
'on_play': plexpy.CONFIG.TWITTER_ON_PLAY,
'on_stop': plexpy.CONFIG.TWITTER_ON_STOP,
'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE,
'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME,
'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER,
'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED,
'on_created': plexpy.CONFIG.TWITTER_ON_CREATED,
'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN,
'on_extup': plexpy.CONFIG.TWITTER_ON_EXTUP,
'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP
},
{'name': 'IFTTT',
'id': AGENT_IDS['IFTTT'],
'config_prefix': 'ifttt',
'has_config': True,
'state': checked(plexpy.CONFIG.IFTTT_ENABLED),
'on_play': plexpy.CONFIG.IFTTT_ON_PLAY,
'on_stop': plexpy.CONFIG.IFTTT_ON_STOP,
'on_pause': plexpy.CONFIG.IFTTT_ON_PAUSE,
'on_resume': plexpy.CONFIG.IFTTT_ON_RESUME,
'on_buffer': plexpy.CONFIG.IFTTT_ON_BUFFER,
'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED,
'on_created': plexpy.CONFIG.IFTTT_ON_CREATED,
'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN,
'on_extup': plexpy.CONFIG.IFTTT_ON_EXTUP,
'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP
},
{'name': 'Telegram',
'id': AGENT_IDS['Telegram'],
'config_prefix': 'telegram',
'has_config': True,
'state': checked(plexpy.CONFIG.TELEGRAM_ENABLED),
'on_play': plexpy.CONFIG.TELEGRAM_ON_PLAY,
'on_stop': plexpy.CONFIG.TELEGRAM_ON_STOP,
'on_pause': plexpy.CONFIG.TELEGRAM_ON_PAUSE,
'on_resume': plexpy.CONFIG.TELEGRAM_ON_RESUME,
'on_buffer': plexpy.CONFIG.TELEGRAM_ON_BUFFER,
'on_watched': plexpy.CONFIG.TELEGRAM_ON_WATCHED,
'on_created': plexpy.CONFIG.TELEGRAM_ON_CREATED,
'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN,
'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP,
'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP
},
{'name': 'Slack',
'id': AGENT_IDS['Slack'],
'config_prefix': 'slack',
'has_config': True,
'state': checked(plexpy.CONFIG.SLACK_ENABLED),
'on_play': plexpy.CONFIG.SLACK_ON_PLAY,
'on_stop': plexpy.CONFIG.SLACK_ON_STOP,
'on_resume': plexpy.CONFIG.SLACK_ON_RESUME,
'on_pause': plexpy.CONFIG.SLACK_ON_PAUSE,
'on_buffer': plexpy.CONFIG.SLACK_ON_BUFFER,
'on_watched': plexpy.CONFIG.SLACK_ON_WATCHED,
'on_created': plexpy.CONFIG.SLACK_ON_CREATED,
'on_extdown': plexpy.CONFIG.SLACK_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.SLACK_ON_INTDOWN,
'on_extup': plexpy.CONFIG.SLACK_ON_EXTUP,
'on_intup': plexpy.CONFIG.SLACK_ON_INTUP
},
{'name': 'Scripts',
'id': AGENT_IDS['Scripts'],
'config_prefix': 'scripts',
'has_config': True,
'state': checked(plexpy.CONFIG.SCRIPTS_ENABLED),
'on_play': plexpy.CONFIG.SCRIPTS_ON_PLAY,
'on_stop': plexpy.CONFIG.SCRIPTS_ON_STOP,
'on_pause': plexpy.CONFIG.SCRIPTS_ON_PAUSE,
'on_resume': plexpy.CONFIG.SCRIPTS_ON_RESUME,
'on_buffer': plexpy.CONFIG.SCRIPTS_ON_BUFFER,
'on_watched': plexpy.CONFIG.SCRIPTS_ON_WATCHED,
'on_created': plexpy.CONFIG.SCRIPTS_ON_CREATED,
'on_extdown': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN,
'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP
},
{'name': 'Facebook',
'id': AGENT_IDS['Facebook'],
'config_prefix': 'facebook',
'has_config': True,
'state': checked(plexpy.CONFIG.FACEBOOK_ENABLED),
'on_play': plexpy.CONFIG.FACEBOOK_ON_PLAY,
'on_stop': plexpy.CONFIG.FACEBOOK_ON_STOP,
'on_pause': plexpy.CONFIG.FACEBOOK_ON_PAUSE,
'on_resume': plexpy.CONFIG.FACEBOOK_ON_RESUME,
'on_buffer': plexpy.CONFIG.FACEBOOK_ON_BUFFER,
'on_watched': plexpy.CONFIG.FACEBOOK_ON_WATCHED,
'on_created': plexpy.CONFIG.FACEBOOK_ON_CREATED,
'on_extdown': plexpy.CONFIG.FACEBOOK_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.FACEBOOK_ON_INTDOWN,
'on_extup': plexpy.CONFIG.FACEBOOK_ON_EXTUP,
'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP
}
]
# OSX Notifications should only be visible if it can be used
osx_notify = OSX_NOTIFY()
if osx_notify.validate():
agents.append({'name': 'OSX Notify',
'id': AGENT_IDS['OSX Notify'],
'config_prefix': 'osx_notify',
'has_config': True,
'state': checked(plexpy.CONFIG.OSX_NOTIFY_ENABLED),
'on_play': plexpy.CONFIG.OSX_NOTIFY_ON_PLAY,
'on_stop': plexpy.CONFIG.OSX_NOTIFY_ON_STOP,
'on_pause': plexpy.CONFIG.OSX_NOTIFY_ON_PAUSE,
'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME,
'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER,
'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED,
'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED,
'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN,
'on_extup': plexpy.CONFIG.OSX_NOTIFY_ON_EXTUP,
'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP
})
return agents
def get_notification_agent_config(config_id):
if str(config_id).isdigit():
config_id = int(config_id)
if config_id == 0:
growl = GROWL()
return growl.return_config_options()
elif config_id == 1:
prowl = PROWL()
return prowl.return_config_options()
elif config_id == 2:
xbmc = XBMC()
return xbmc.return_config_options()
elif config_id == 3:
plex = Plex()
return plex.return_config_options()
elif config_id == 4:
nma = NMA()
return nma.return_config_options()
elif config_id == 5:
pushalot = PUSHALOT()
return pushalot.return_config_options()
elif config_id == 6:
pushbullet = PUSHBULLET()
return pushbullet.return_config_options()
elif config_id == 7:
pushover = PUSHOVER()
return pushover.return_config_options()
elif config_id == 8:
osx_notify = OSX_NOTIFY()
return osx_notify.return_config_options()
elif config_id == 9:
boxcar = BOXCAR()
return boxcar.return_config_options()
elif config_id == 10:
email = Email()
return email.return_config_options()
elif config_id == 11:
tweet = TwitterNotifier()
return tweet.return_config_options()
elif config_id == 12:
iftttClient = IFTTT()
return iftttClient.return_config_options()
elif config_id == 13:
telegramClient = TELEGRAM()
return telegramClient.return_config_options()
elif config_id == 14:
slackClient = SLACK()
return slackClient.return_config_options()
elif config_id == 15:
script = Scripts()
return script.return_config_options()
elif config_id == 16:
facebook = FacebookNotifier()
return facebook.return_config_options()
else:
return []
else:
return []
def send_notification(config_id, subject, body, **kwargs):
if str(config_id).isdigit():
config_id = int(config_id)
if config_id == 0:
growl = GROWL()
growl.notify(message=body, event=subject)
elif config_id == 1:
prowl = PROWL()
prowl.notify(message=body, event=subject)
elif config_id == 2:
xbmc = XBMC()
xbmc.notify(subject=subject, message=body)
elif config_id == 3:
plex = Plex()
plex.notify(subject=subject, message=body)
elif config_id == 4:
nma = NMA()
nma.notify(subject=subject, message=body)
elif config_id == 5:
pushalot = PUSHALOT()
pushalot.notify(message=body, event=subject)
elif config_id == 6:
pushbullet = PUSHBULLET()
pushbullet.notify(message=body, subject=subject)
elif config_id == 7:
pushover = PUSHOVER()
pushover.notify(message=body, event=subject)
elif config_id == 8:
osx_notify = OSX_NOTIFY()
osx_notify.notify(title=subject, text=body)
elif config_id == 9:
boxcar = BOXCAR()
boxcar.notify(title=subject, message=body)
elif config_id == 10:
email = Email()
email.notify(subject=subject, message=body)
elif config_id == 11:
tweet = TwitterNotifier()
tweet.notify(subject=subject, message=body)
elif config_id == 12:
iftttClient = IFTTT()
iftttClient.notify(subject=subject, message=body)
elif config_id == 13:
telegramClient = TELEGRAM()
telegramClient.notify(message=body, event=subject)
elif config_id == 14:
slackClient = SLACK()
slackClient.notify(message=body, event=subject)
elif config_id == 15:
scripts = Scripts()
scripts.notify(message=body, subject=subject, **kwargs)
elif config_id == 16:
facebook = FacebookNotifier()
facebook.notify(subject=subject, message=body)
else:
logger.debug(u"PlexPy Notifiers :: Unknown agent id received.")
else:
logger.debug(u"PlexPy Notifiers :: Notification requested but no agent id received.")
class GROWL(object):
"""
Growl notifications, for OS X.
"""
def __init__(self):
self.enabled = plexpy.CONFIG.GROWL_ENABLED
self.host = plexpy.CONFIG.GROWL_HOST
self.password = plexpy.CONFIG.GROWL_PASSWORD
def conf(self, options):
return cherrypy.config['config'].get('Growl', options)
def notify(self, message, event):
if not message or not event:
return
# Split host and port
if self.host == "":
host, port = "localhost", 23053
if ":" in self.host:
host, port = self.host.split(':', 1)
port = int(port)
else:
host, port = self.host, 23053
# If password is empty, assume none
if self.password == "":
password = None
else:
password = self.password
# Register notification
growl = gntp.notifier.GrowlNotifier(
applicationName='PlexPy',
notifications=['New Event'],
defaultNotifications=['New Event'],
hostname=host,
port=port,
password=password
)
try:
growl.register()
except gntp.notifier.errors.NetworkError:
logger.warn(u"PlexPy Notifiers :: Growl notification failed: network error")
return
except gntp.notifier.errors.AuthError:
logger.warn(u"PlexPy Notifiers :: Growl notification failed: authentication error")
return
# Fix message
message = message.encode(plexpy.SYS_ENCODING, "replace")
# Send it, including an image
image_file = os.path.join(str(plexpy.PROG_DIR),
"data/interfaces/default/images/favicon.png")
with open(image_file, 'rb') as f:
image = f.read()
try:
growl.notify(
noteType='New Event',
title=event,
description=message,
icon=image
)
logger.info(u"PlexPy Notifiers :: Growl notification sent.")
except gntp.notifier.errors.NetworkError:
logger.warn(u"PlexPy Notifiers :: Growl notification failed: network error")
return
def updateLibrary(self):
# For uniformity reasons not removed
return
def test(self, host, password):
self.enabled = True
self.host = host
self.password = password
self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
def return_config_options(self):
config_option = [{'label': 'Growl Host',
'value': self.host,
'name': 'growl_host',
'description': 'Your Growl hostname.',
'input_type': 'text'
},
{'label': 'Growl Password',
'value': self.password,
'name': 'growl_password',
'description': 'Your Growl password.',
'input_type': 'password'
}
]
return config_option
class PROWL(object):
"""
Prowl notifications.
"""
def __init__(self):
self.enabled = plexpy.CONFIG.PROWL_ENABLED
self.keys = plexpy.CONFIG.PROWL_KEYS
self.priority = plexpy.CONFIG.PROWL_PRIORITY
def conf(self, options):
return cherrypy.config['config'].get('Prowl', options)
def notify(self, message, event):
if not message or not event:
return
http_handler = HTTPSConnection("api.prowlapp.com")
data = {'apikey': plexpy.CONFIG.PROWL_KEYS,
'application': 'PlexPy',
'event': event.encode("utf-8"),
'description': message.encode("utf-8"),
'priority': plexpy.CONFIG.PROWL_PRIORITY}
http_handler.request("POST",
"/publicapi/add",
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Prowl notification sent.")
return True
elif request_status == 401:
logger.warn(u"PlexPy Notifiers :: Prowl notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Prowl notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
return
def test(self, keys, priority):
self.enabled = True
self.keys = keys
self.priority = priority
self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
def return_config_options(self):
config_option = [{'label': 'Prowl API Key',
'value': self.keys,
'name': 'prowl_keys',
'description': 'Your Prowl API key.',
'input_type': 'text'
},
{'label': 'Priority',
'value': self.priority,
'name': 'prowl_priority',
'description': 'Set the priority.',
'input_type': 'select',
'select_options': {-2: -2, -1: -1, 0: 0, 1: 1, 2: 2}
}
]
return config_option
class XBMC(object):
"""
XBMC notifications
"""
def __init__(self):
self.hosts = plexpy.CONFIG.XBMC_HOST
self.username = plexpy.CONFIG.XBMC_USERNAME
self.password = plexpy.CONFIG.XBMC_PASSWORD
def _sendhttp(self, host, command):
url_command = urllib.urlencode(command)
url = host + '/xbmcCmds/xbmcHttp/?' + url_command
if self.password:
return request.request_content(url, auth=(self.username, self.password))
else:
return request.request_content(url)
def _sendjson(self, host, method, params={}):
data = [{'id': 0, 'jsonrpc': '2.0', 'method': method, 'params': params}]
headers = {'Content-Type': 'application/json'}
url = host + '/jsonrpc'
if self.password:
response = request.request_json(url, method="post", data=json.dumps(data), headers=headers, auth=(self.username, self.password))
else:
response = request.request_json(url, method="post", data=json.dumps(data), headers=headers)
if response:
return response[0]['result']
def notify(self, subject=None, message=None):
hosts = [x.strip() for x in self.hosts.split(',')]
header = subject
message = message
time = "3000" # in ms
for host in hosts:
logger.info(u"PlexPy Notifiers :: Sending notification command to XMBC @ " + host)
try:
version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major']
if version < 12: # Eden
notification = header + "," + message + "," + time
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
request = self._sendhttp(host, notifycommand)
else: # Frodo
params = {'title': header, 'message': message, 'displaytime': int(time)}
request = self._sendjson(host, 'GUI.ShowNotification', params)
if not request:
raise Exception
else:
logger.info(u"PlexPy Notifiers :: XBMC notification sent.")
except Exception:
logger.warn(u"PlexPy Notifiers :: XBMC notification filed.")
def return_config_options(self):
config_option = [{'label': 'XBMC Host:Port',
'value': self.hosts,
'name': 'xbmc_host',
'description': 'Host running XBMC (e.g. http://localhost:8080). Separate multiple hosts with commas (,).',
'input_type': 'text'
},
{'label': 'XBMC Username',
'value': self.username,
'name': 'xbmc_username',
'description': 'Your XBMC username.',
'input_type': 'text'
},
{'label': 'XBMC Password',
'value': self.password,
'name': 'xbmc_password',
'description': 'Your XMBC password.',
'input_type': 'password'
}
]
return config_option
class Plex(object):
def __init__(self):
self.client_hosts = plexpy.CONFIG.PLEX_CLIENT_HOST
self.username = plexpy.CONFIG.PLEX_USERNAME
self.password = plexpy.CONFIG.PLEX_PASSWORD
def _sendhttp(self, host, command):
username = self.username
password = self.password
url_command = urllib.urlencode(command)
url = host + '/xbmcCmds/xbmcHttp/?' + url_command
req = urllib2.Request(url)
if password:
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string)
# logger.info(u"PlexPy Notifiers :: Plex url: %s" % url)
try:
handle = urllib2.urlopen(req)
except Exception as e:
logger.error(u"PlexPy Notifiers :: Error opening Plex url: %s" % e)
return
response = handle.read().decode(plexpy.SYS_ENCODING)
return response
def notify(self, subject=None, message=None):
hosts = [x.strip() for x in self.client_hosts.split(',')]
header = subject
message = message
time = "3000" # in ms
for host in hosts:
logger.info(u"PlexPy Notifiers :: Sending notification command to Plex Media Server @ " + host)
try:
notification = header + "," + message + "," + time
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
request = self._sendhttp(host, notifycommand)
if not request:
raise Exception
else:
logger.info(u"PlexPy Notifiers :: Plex notification sent.")
except:
logger.warn(u"PlexPy Notifiers :: Plex notification failed.")
def return_config_options(self):
config_option = [{'label': 'Plex Client Host:Port',
'value': self.client_hosts,
'name': 'plex_client_host',
'description': 'Host running Plex Client (eg. http://192.168.1.100:3000).',
'input_type': 'text'
},
{'label': 'Plex Username',
'value': self.username,
'name': 'plex_username',
'description': 'Username of your Plex client API (blank for none).',
'input_type': 'text'
},
{'label': 'Plex Password',
'value': self.password,
'name': 'plex_password',
'description': 'Password of your Plex client API (blank for none).',
'input_type': 'password'
}
]
return config_option
class NMA(object):
def __init__(self):
self.api = plexpy.CONFIG.NMA_APIKEY
self.nma_priority = plexpy.CONFIG.NMA_PRIORITY
def notify(self, subject=None, message=None):
if not subject or not message:
return
title = 'PlexPy'
api = plexpy.CONFIG.NMA_APIKEY
nma_priority = plexpy.CONFIG.NMA_PRIORITY
# logger.debug(u"NMA title: " + title)
# logger.debug(u"NMA API: " + api)
# logger.debug(u"NMA Priority: " + str(nma_priority))
event = subject
# logger.debug(u"NMA event: " + event)
# logger.debug(u"NMA message: " + message)
batch = False
p = pynma.PyNMA()
keys = api.split(',')
p.addkey(keys)
if len(keys) > 1:
batch = True
response = p.push(title, event, message, priority=nma_priority, batch_mode=batch)
if not response[api][u'code'] == u'200':
logger.warn(u"PlexPy Notifiers :: NotifyMyAndroid notification failed.")
return False
else:
logger.info(u"PlexPy Notifiers :: NotifyMyAndroid notification sent.")
return True
def return_config_options(self):
config_option = [{'label': 'NotifyMyAndroid API Key',
'value': plexpy.CONFIG.NMA_APIKEY,
'name': 'nma_apikey',
'description': 'Your NotifyMyAndroid API key. Separate multiple api keys with commas.',
'input_type': 'text'
},
{'label': 'Priority',
'value': plexpy.CONFIG.NMA_PRIORITY,
'name': 'nma_priority',
'description': 'Set the priority.',
'input_type': 'select',
'select_options': {-2: -2, -1: -1, 0: 0, 1: 1, 2: 2}
}
]
return config_option
class PUSHBULLET(object):
def __init__(self):
self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY
self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID
self.channel_tag = plexpy.CONFIG.PUSHBULLET_CHANNEL_TAG
def conf(self, options):
return cherrypy.config['config'].get('PUSHBULLET', options)
def notify(self, message, subject):
if not message or not subject:
return
http_handler = HTTPSConnection("api.pushbullet.com")
data = {'type': "note",
'title': subject.encode("utf-8"),
'body': message.encode("utf-8")}
# Can only send to a device or channel, not both.
if self.deviceid:
data['device_iden'] = self.deviceid
elif self.channel_tag:
data['channel_tag'] = self.channel_tag
http_handler.request("POST",
"/v2/pushes",
headers={'Content-type': "application/json",
'Authorization': 'Basic %s' % base64.b64encode(plexpy.CONFIG.PUSHBULLET_APIKEY + ":")},
body=json.dumps(data))
response = http_handler.getresponse()
request_status = response.status
# logger.debug(u"PushBullet response status: %r" % request_status)
# logger.debug(u"PushBullet response headers: %r" % response.getheaders())
# logger.debug(u"PushBullet response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: PushBullet notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: PushBullet notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: PushBullet notification failed.")
return False
def test(self, apikey, deviceid):
self.enabled = True
self.apikey = apikey
self.deviceid = deviceid
self.notify('Main Screen Activate', 'Test Message')
def get_devices(self):
if plexpy.CONFIG.PUSHBULLET_APIKEY:
http_handler = HTTPSConnection("api.pushbullet.com")
http_handler.request("GET", "/v2/devices",
headers={'Content-type': "application/json",
'Authorization': 'Basic %s' % base64.b64encode(plexpy.CONFIG.PUSHBULLET_APIKEY + ":")})
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
data = json.loads(response.read())
devices = data.get('devices', [])
devices = {d['iden']: d['nickname'] for d in devices if d['active']}
devices.update({'': ''})
return devices
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Unable to retrieve Pushbullet devices list: %s" % response.reason)
return {'': ''}
else:
logger.warn(u"PlexPy Notifiers :: Unable to retrieve Pushbullet devices list.")
return {'': ''}
else:
return {'': ''}
def return_config_options(self):
config_option = [{'label': 'Pushbullet API Key',
'value': self.apikey,
'name': 'pushbullet_apikey',
'description': 'Your Pushbullet API key.',
'input_type': 'text'
},
{'label': 'Device',
'value': self.deviceid,
'name': 'pushbullet_deviceid',
'description': 'Set your Pushbullet device. If set, will override channel tag. ' \
'Leave blank to notify on all devices.',
'input_type': 'select',
'select_options': self.get_devices()
},
{'label': 'Channel',
'value': self.channel_tag,
'name': 'pushbullet_channel_tag',
'description': 'A channel tag (optional).',
'input_type': 'text'
}
]
return config_option
class PUSHALOT(object):
def __init__(self):
self.api_key = plexpy.CONFIG.PUSHALOT_APIKEY
def notify(self, message, event):
if not message or not event:
return
pushalot_authorizationtoken = plexpy.CONFIG.PUSHALOT_APIKEY
# logger.debug(u"Pushalot event: " + event)
# logger.debug(u"Pushalot message: " + message)
# logger.debug(u"Pushalot api: " + pushalot_authorizationtoken)
http_handler = HTTPSConnection("pushalot.com")
data = {'AuthorizationToken': pushalot_authorizationtoken,
'Title': event.encode('utf-8'),
'Body': message.encode("utf-8")}
http_handler.request("POST",
"/api/sendmessage",
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
response = http_handler.getresponse()
request_status = response.status
# logger.debug(u"Pushalot response status: %r" % request_status)
# logger.debug(u"Pushalot response headers: %r" % response.getheaders())
# logger.debug(u"Pushalot response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Pushalot notification sent.")
return True
elif request_status == 410:
logger.warn(u"PlexPy Notifiers :: Pushalot notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Pushalot notification failed.")
return False
def return_config_options(self):
config_option = [{'label': 'Pushalot API Key',
'value': plexpy.CONFIG.PUSHALOT_APIKEY,
'name': 'pushalot_apikey',
'description': 'Your Pushalot API key.',
'input_type': 'text'
}
]
return config_option
class PUSHOVER(object):
def __init__(self):
self.enabled = plexpy.CONFIG.PUSHOVER_ENABLED
self.application_token = plexpy.CONFIG.PUSHOVER_APITOKEN
self.keys = plexpy.CONFIG.PUSHOVER_KEYS
self.priority = plexpy.CONFIG.PUSHOVER_PRIORITY
self.sound = plexpy.CONFIG.PUSHOVER_SOUND
def conf(self, options):
return cherrypy.config['config'].get('Pushover', options)
def notify(self, message, event):
if not message or not event:
return
http_handler = HTTPSConnection("api.pushover.net")
data = {'token': self.application_token,
'user': plexpy.CONFIG.PUSHOVER_KEYS,
'title': event.encode("utf-8"),
'message': message.encode("utf-8"),
'sound': plexpy.CONFIG.PUSHOVER_SOUND,
'priority': plexpy.CONFIG.PUSHOVER_PRIORITY}
http_handler.request("POST",
"/1/messages.json",
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
response = http_handler.getresponse()
request_status = response.status
# logger.debug(u"Pushover response status: %r" % request_status)
# logger.debug(u"Pushover response headers: %r" % response.getheaders())
# logger.debug(u"Pushover response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Pushover notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Pushover notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Pushover notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
return
def test(self, keys, priority, sound):
self.enabled = True
self.keys = keys
self.priority = priority
self.sound = sound
self.notify('Main Screen Activate', 'Test Message')
def get_sounds(self):
if plexpy.CONFIG.PUSHOVER_APITOKEN:
http_handler = HTTPSConnection("api.pushover.net")
http_handler.request("GET", "/1/sounds.json?token=" + self.application_token)
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
data = json.loads(response.read())
sounds = data.get('sounds', {})
sounds.update({'': ''})
return sounds
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Unable to retrieve Pushover notification sounds list: %s" % response.reason)
return {'': ''}
else:
logger.warn(u"PlexPy Notifiers :: Unable to retrieve Pushover notification sounds list.")
return {'': ''}
else:
return {'': ''}
def return_config_options(self):
config_option = [{'label': 'Pushover API Token',
'value': plexpy.CONFIG.PUSHOVER_APITOKEN,
'name': 'pushover_apitoken',
'description': 'Your Pushover API token.',
'input_type': 'text'
},
{'label': 'Pushover User or Group Key',
'value': self.keys,
'name': 'pushover_keys',
'description': 'Your Pushover user or group key.',
'input_type': 'text'
},
{'label': 'Priority',
'value': self.priority,
'name': 'pushover_priority',
'description': 'Set the priority.',
'input_type': 'select',
'select_options': {-2: -2, -1: -1, 0: 0, 1: 1, 2: 2}
},
{'label': 'Sound',
'value': self.sound,
'name': 'pushover_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': self.get_sounds()
}
]
return config_option
class TwitterNotifier(object):
REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
def __init__(self):
self.consumer_key = "2LdJKXHDUwJtjYBsdwJisIOsh"
self.consumer_secret = "QWbUcZzAIiL4zbDCIhy2EdUkV8yEEav3qMdo5y3FugxCFelWrA"
def notify(self, subject, message):
if not subject or not message:
return
else:
self._send_tweet(subject + ': ' + message)
def test_notify(self):
return self._send_tweet("This is a test notification from PlexPy at " + helpers.now())
def _get_authorization(self):
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
oauth_client = oauth.Client(oauth_consumer)
logger.info("PlexPy Notifiers :: Requesting temp token from Twitter")
resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET')
if resp['status'] != '200':
logger.warn("PlexPy Notifiers :: Invalid respond from Twitter requesting temp token: %s" % resp['status'])
else:
request_token = dict(parse_qsl(content))
plexpy.CONFIG.TWITTER_USERNAME = request_token['oauth_token']
plexpy.CONFIG.TWITTER_PASSWORD = request_token['oauth_token_secret']
return self.AUTHORIZATION_URL + "?oauth_token=" + request_token['oauth_token']
def _get_credentials(self, key):
request_token = {}
request_token['oauth_token'] = plexpy.CONFIG.TWITTER_USERNAME
request_token['oauth_token_secret'] = plexpy.CONFIG.TWITTER_PASSWORD
request_token['oauth_callback_confirmed'] = 'true'
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
token.set_verifier(key)
# logger.debug(u"Generating and signing request for an access token using key " + key)
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
# logger.debug(u"oauth_consumer: " + str(oauth_consumer))
oauth_client = oauth.Client(oauth_consumer, token)
# logger.debug(u"oauth_client: " + str(oauth_client))
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
# logger.debug(u"resp, content: " + str(resp) + ',' + str(content))
access_token = dict(parse_qsl(content))
# logger.debug(u"access_token: " + str(access_token))
# logger.debug(u"resp[status] = " + str(resp['status']))
if resp['status'] != '200':
logger.error(u"PlexPy Notifiers :: The request for a Twitter token did not succeed: " + str(resp['status']), logger.ERROR)
return False
else:
# logger.info(u"PlexPy Notifiers :: Your Twitter Access Token key: %s" % access_token['oauth_token'])
# logger.info(u"PlexPy Notifiers :: Access Token secret: %s" % access_token['oauth_token_secret'])
plexpy.CONFIG.TWITTER_USERNAME = access_token['oauth_token']
plexpy.CONFIG.TWITTER_PASSWORD = access_token['oauth_token_secret']
plexpy.CONFIG.write()
return True
def _send_tweet(self, message=None):
username = self.consumer_key
password = self.consumer_secret
access_token_key = plexpy.CONFIG.TWITTER_USERNAME
access_token_secret = plexpy.CONFIG.TWITTER_PASSWORD
# logger.info(u"PlexPy Notifiers :: Sending tweet: " + message)
api = twitter.Api(username, password, access_token_key, access_token_secret)
try:
api.PostUpdate(message)
logger.info(u"PlexPy Notifiers :: Twitter notification sent.")
except Exception as e:
logger.warn(u"PlexPy Notifiers :: Twitter notification failed: %s" % e)
return False
return True
def return_config_options(self):
config_option = [{'label': 'Instructions',
'description': 'Step 1: Click the <strong>Request Authorization</strong> button below.<br>\
Step 2: Input the <strong>Authorization Key</strong> you received from Step 1 below.<br>\
Step 3: Click the <strong>Verify Key</strong> button below.',
'input_type': 'help'
},
{'label': 'Request Authorization',
'value': 'Request Authorization',
'name': 'twitterStep1',
'description': 'Request Twitter authorization. (Ensure you allow the browser pop-up).',
'input_type': 'button'
},
{'label': 'Authorization Key',
'value': '',
'name': 'twitter_key',
'description': 'Your Twitter authorization key.',
'input_type': 'text'
},
{'label': 'Verify Key',
'value': 'Verify Key',
'name': 'twitterStep2',
'description': 'Verify your Twitter authorization key.',
'input_type': 'button'
},
{'input_type': 'nosave'
}
]
return config_option
class OSX_NOTIFY(object):
def __init__(self):
try:
self.objc = __import__("objc")
self.AppKit = __import__("AppKit")
except:
# logger.error(u"PlexPy Notifiers :: Cannot load OSX Notifications agent.")
pass
def validate(self):
try:
self.objc = __import__("objc")
self.AppKit = __import__("AppKit")
return True
except:
return False
def swizzle(self, cls, SEL, func):
old_IMP = cls.instanceMethodForSelector_(SEL)
def wrapper(self, *args, **kwargs):
return func(self, old_IMP, *args, **kwargs)
new_IMP = self.objc.selector(wrapper, selector=old_IMP.selector,
signature=old_IMP.signature)
self.objc.classAddMethod(cls, SEL, new_IMP)
def notify(self, title, subtitle=None, text=None, sound=True, image=None):
try:
self.swizzle(self.objc.lookUpClass('NSBundle'),
b'bundleIdentifier',
self.swizzled_bundleIdentifier)
NSUserNotification = self.objc.lookUpClass('NSUserNotification')
NSUserNotificationCenter = self.objc.lookUpClass('NSUserNotificationCenter')
NSAutoreleasePool = self.objc.lookUpClass('NSAutoreleasePool')
if not NSUserNotification or not NSUserNotificationCenter:
return False
pool = NSAutoreleasePool.alloc().init()
notification = NSUserNotification.alloc().init()
notification.setTitle_(title)
if subtitle:
notification.setSubtitle_(subtitle)
if text:
notification.setInformativeText_(text)
if sound:
notification.setSoundName_("NSUserNotificationDefaultSoundName")
if image:
source_img = self.AppKit.NSImage.alloc().initByReferencingFile_(image)
notification.setContentImage_(source_img)
# notification.set_identityImage_(source_img)
notification.setHasActionButton_(False)
notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
notification_center.deliverNotification_(notification)
logger.info(u"PlexPy Notifiers :: OSX Notify notification sent.")
del pool
return True
except Exception as e:
logger.warn(u"PlexPy Notifiers :: OSX notification failed: %s" % e)
return False
def swizzled_bundleIdentifier(self, original, swizzled):
return 'ade.plexpy.osxnotify'
def return_config_options(self):
config_option = [{'label': 'Register Notify App',
'value': plexpy.CONFIG.OSX_NOTIFY_APP,
'name': 'osx_notify_app',
'description': 'Enter the path/application name to be registered with the '
'Notification Center, default is /Applications/PlexPy.',
'input_type': 'text'
}
]
return config_option
class BOXCAR(object):
def __init__(self):
self.url = 'https://new.boxcar.io/api/notifications'
self.token = plexpy.CONFIG.BOXCAR_TOKEN
self.sound = plexpy.CONFIG.BOXCAR_SOUND
def notify(self, title, message):
if not title or not message:
return
try:
data = urllib.urlencode({
'user_credentials': plexpy.CONFIG.BOXCAR_TOKEN,
'notification[title]': title.encode('utf-8'),
'notification[long_message]': message.encode('utf-8'),
'notification[sound]': plexpy.CONFIG.BOXCAR_SOUND
})
req = urllib2.Request(self.url)
handle = urllib2.urlopen(req, data)
handle.close()
logger.info(u"PlexPy Notifiers :: Boxcar2 notification sent.")
return True
except urllib2.URLError as e:
logger.warn(u"PlexPy Notifiers :: Boxcar2 notification failed: %s" % e)
return False
def get_sounds(self):
sounds = {'': '',
'beep-crisp': 'Beep (Crisp)',
'beep-soft': 'Beep (Soft)',
'bell-modern': 'Bell (Modern)',
'bell-one-tone': 'Bell (One Tone)',
'bell-simple': 'Bell (Simple)',
'bell-triple': 'Bell (Triple)',
'bird-1': 'Bird (1)',
'bird-2': 'Bird (2)',
'boing': 'Boing',
'cash': 'Cash',
'clanging': 'Clanging',
'detonator-charge': 'Detonator Charge',
'digital-alarm': 'Digital Alarm',
'done': 'Done',
'echo': 'Echo',
'flourish': 'Flourish',
'harp': 'Harp',
'light': 'Light',
'magic-chime':'Magic Chime',
'magic-coin': 'Magic Coin',
'no-sound': 'No Sound',
'notifier-1': 'Notifier (1)',
'notifier-2': 'Notifier (2)',
'notifier-3': 'Notifier (3)',
'orchestral-long': 'Orchestral (Long)',
'orchestral-short': 'Orchestral (Short)',
'score': 'Score',
'success': 'Success',
'up': 'Up'}
return sounds
def return_config_options(self):
config_option = [{'label': 'Boxcar Access Token',
'value': plexpy.CONFIG.BOXCAR_TOKEN,
'name': 'boxcar_token',
'description': 'Your Boxcar access token.',
'input_type': 'text'
},
{'label': 'Sound',
'value': self.sound,
'name': 'boxcar_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': self.get_sounds()
}
]
return config_option
class Email(object):
def __init__(self):
pass
def notify(self, subject, message):
if not subject or not message:
return
message = MIMEText(message, 'plain', "utf-8")
message['Subject'] = subject
message['From'] = email.utils.formataddr((plexpy.CONFIG.EMAIL_FROM_NAME, plexpy.CONFIG.EMAIL_FROM))
message['To'] = plexpy.CONFIG.EMAIL_TO
message['CC'] = plexpy.CONFIG.EMAIL_CC
recipients = [x.strip() for x in plexpy.CONFIG.EMAIL_TO.split(';')] \
+ [x.strip() for x in plexpy.CONFIG.EMAIL_CC.split(';')] \
+ [x.strip() for x in plexpy.CONFIG.EMAIL_BCC.split(';')]
recipients = filter(None, recipients)
try:
mailserver = smtplib.SMTP(plexpy.CONFIG.EMAIL_SMTP_SERVER, plexpy.CONFIG.EMAIL_SMTP_PORT)
if (plexpy.CONFIG.EMAIL_TLS):
mailserver.starttls()
mailserver.ehlo()
if plexpy.CONFIG.EMAIL_SMTP_USER:
mailserver.login(plexpy.CONFIG.EMAIL_SMTP_USER, plexpy.CONFIG.EMAIL_SMTP_PASSWORD)
mailserver.sendmail(plexpy.CONFIG.EMAIL_FROM, recipients, message.as_string())
mailserver.quit()
logger.info(u"PlexPy Notifiers :: Email notification sent.")
return True
except Exception as e:
logger.warn(u"PlexPy Notifiers :: Email notification failed: %s" % e)
return False
def return_config_options(self):
config_option = [{'label': 'From Name',
'value': plexpy.CONFIG.EMAIL_FROM_NAME,
'name': 'email_from_name',
'description': 'The name of the sender.',
'input_type': 'text'
},
{'label': 'From',
'value': plexpy.CONFIG.EMAIL_FROM,
'name': 'email_from',
'description': 'The email address of the sender.',
'input_type': 'text'
},
{'label': 'To',
'value': plexpy.CONFIG.EMAIL_TO,
'name': 'email_to',
'description': 'The email address(es) of the recipients, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'CC',
'value': plexpy.CONFIG.EMAIL_CC,
'name': 'email_cc',
'description': 'The email address(es) to CC, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'BCC',
'value': plexpy.CONFIG.EMAIL_BCC,
'name': 'email_bcc',
'description': 'The email address(es) to BCC, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'SMTP Server',
'value': plexpy.CONFIG.EMAIL_SMTP_SERVER,
'name': 'email_smtp_server',
'description': 'Host for the SMTP server.',
'input_type': 'text'
},
{'label': 'SMTP Port',
'value': plexpy.CONFIG.EMAIL_SMTP_PORT,
'name': 'email_smtp_port',
'description': 'Port for the SMTP server.',
'input_type': 'number'
},
{'label': 'SMTP User',
'value': plexpy.CONFIG.EMAIL_SMTP_USER,
'name': 'email_smtp_user',
'description': 'User for the SMTP server.',
'input_type': 'text'
},
{'label': 'SMTP Password',
'value': plexpy.CONFIG.EMAIL_SMTP_PASSWORD,
'name': 'email_smtp_password',
'description': 'Password for the SMTP server.',
'input_type': 'password'
},
{'label': 'TLS',
'value': plexpy.CONFIG.EMAIL_TLS,
'name': 'email_tls',
'description': 'Does the server use encryption.',
'input_type': 'checkbox'
}
]
return config_option
class IFTTT(object):
def __init__(self):
self.apikey = plexpy.CONFIG.IFTTT_KEY
self.event = plexpy.CONFIG.IFTTT_EVENT
def notify(self, message, subject):
if not message or not subject:
return
http_handler = HTTPSConnection("maker.ifttt.com")
data = {'value1': subject.encode("utf-8"),
'value2': message.encode("utf-8")}
# logger.debug(u"Ifttt SENDING: %s" % json.dumps(data))
http_handler.request("POST",
"/trigger/%s/with/key/%s" % (self.event, self.apikey),
headers={'Content-type': "application/json"},
body=json.dumps(data))
response = http_handler.getresponse()
request_status = response.status
# logger.debug(u"Ifttt response status: %r" % request_status)
# logger.debug(u"Ifttt response headers: %r" % response.getheaders())
# logger.debug(u"Ifttt response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Ifttt notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Ifttt notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Ifttt notification failed.")
return False
def test(self):
return self.notify('PlexPy', 'Test Message')
def return_config_options(self):
config_option = [{'label': 'Ifttt Maker Channel Key',
'value': self.apikey,
'name': 'ifttt_key',
'description': 'Your Ifttt key. You can get a key from <a href="https://ifttt.com/maker" target="_blank">here</a>.',
'input_type': 'text'
},
{'label': 'Ifttt Event',
'value': self.event,
'name': 'ifttt_event',
'description': 'The Ifttt maker event to fire. The notification subject and body will be sent'
' as value1 and value2 respectively.',
'input_type': 'text'
}
]
return config_option
class TELEGRAM(object):
def __init__(self):
self.enabled = plexpy.CONFIG.TELEGRAM_ENABLED
self.bot_token = plexpy.CONFIG.TELEGRAM_BOT_TOKEN
self.chat_id = plexpy.CONFIG.TELEGRAM_CHAT_ID
def conf(self, options):
return cherrypy.config['config'].get('Telegram', options)
def notify(self, message, event):
if not message or not event:
return
http_handler = HTTPSConnection("api.telegram.org")
data = {'chat_id': self.chat_id,
'text': event.encode('utf-8') + ': ' + message.encode("utf-8")}
http_handler.request("POST",
"/bot%s/%s" % (self.bot_token, "sendMessage"),
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Telegram notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Telegram notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Telegram notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
return
def test(self, bot_token, chat_id):
self.enabled = True
self.bot_token = bot_token
self.chat_id = chat_id
self.notify('Main Screen Activate', 'Test Message')
def return_config_options(self):
config_option = [{'label': 'Telegram Bot Token',
'value': self.bot_token,
'name': 'telegram_bot_token',
'description': 'Your Telegram bot token. Contact <a href="http://telegram.me/BotFather" target="_blank">@BotFather</a> on Telegram to get one.',
'input_type': 'text'
},
{'label': 'Telegram Chat ID',
'value': self.chat_id,
'name': 'telegram_chat_id',
'description': 'Your Telegram Chat ID, Group ID, or channel username. Contact <a href="http://telegram.me/myidbot" target="_blank">@myidbot</a> on Telegram to get an ID.',
'input_type': 'text'
}
]
return config_option
class SLACK(object):
"""
Slack Notifications
"""
def __init__(self):
self.enabled = plexpy.CONFIG.SLACK_ENABLED
self.slack_hook = plexpy.CONFIG.SLACK_HOOK
self.channel = plexpy.CONFIG.SLACK_CHANNEL
self.username = plexpy.CONFIG.SLACK_USERNAME
self.icon_emoji = plexpy.CONFIG.SLACK_ICON_EMOJI
def conf(self, options):
return cherrypy.config['config'].get('Slack', options)
def notify(self, message, event):
if not message or not event:
return
http_handler = HTTPSConnection("hooks.slack.com")
data = {'text': event.encode('utf-8') + ': ' + message.encode("utf-8")}
if self.channel != '': data['channel'] = self.channel
if self.username != '': data['username'] = self.username
if self.icon_emoji != '':
if urlparse(self.icon_emoji).scheme == '':
data['icon_emoji'] = self.icon_emoji
else:
data['icon_url'] = self.icon_url
url = urlparse(self.slack_hook).path
http_handler.request("POST",
url,
headers={'Content-type': "application/x-www-form-urlencoded"},
body=json.dumps(data))
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Slack notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.warn(u"PlexPy Notifiers :: Slack notification failed: %s" % response.reason)
return False
else:
logger.warn(u"PlexPy Notifiers :: Slack notification failed.")
return False
def updateLibrary(self):
#For uniformity reasons not removed
return
def test(self):
self.enabled = True
return self.notify('Main Screen Activate', 'Test Message')
def return_config_options(self):
config_option = [{'label': 'Slack Hook',
'value': self.slack_hook,
'name': 'slack_hook',
'description': 'Your Slack incoming webhook.',
'input_type': 'text'
},
{'label': 'Slack Channel',
'value': self.channel,
'name': 'slack_channel',
'description': 'Your Slack channel name (begin with \'#\'). Leave blank for webhook integration default.',
'input_type': 'text'
},
{'label': 'Slack Username',
'value': self.username,
'name': 'slack_username',
'description': 'The Slack username which will be shown. Leave blank for webhook integration default.',
'input_type': 'text'
},
{'label': 'Slack Icon',
'value': self.icon_emoji,
'description': 'The icon you wish to show, use Slack emoji or image url. Leave blank for webhook integration default.',
'name': 'slack_icon_emoji',
'input_type': 'text'
}
]
return config_option
class Scripts(object):
def __init__(self, **kwargs):
self.script_exts = ('.bat', '.cmd', '.exe', '.php', '.pl', '.py', '.pyw', '.rb', '.sh')
def conf(self, options):
return cherrypy.config['config'].get('Scripts', options)
def updateLibrary(self):
# For uniformity reasons not removed
return
def test(self, subject, message, *args, **kwargs):
self.notify(subject, message, *args, **kwargs)
return
def list_scripts(self):
scriptdir = plexpy.CONFIG.SCRIPTS_FOLDER
scripts = {'': ''}
if scriptdir and not os.path.exists(scriptdir):
return scripts
for root, dirs, files in os.walk(scriptdir):
for f in files:
name, ext = os.path.splitext(f)
if ext in self.script_exts:
rfp = os.path.join(os.path.relpath(root, scriptdir), f)
fp = os.path.join(root, f)
scripts[fp] = rfp
return scripts
def notify(self, subject='', message='', notify_action='', script_args=[], *args, **kwargs):
"""
Args:
subject(string, optional): Head text,
message(string, optional): Body text,
notify_action(string): 'play'
script_args(list): ["python2", '-p', '-zomg']
"""
logger.debug(u"PlexPy Notifiers :: Trying to run notify script, action: %s, arguments: %s" %
(notify_action if notify_action else None, script_args if script_args else None))
if not plexpy.CONFIG.SCRIPTS_FOLDER:
return
# Make sure we use the correct script..
if notify_action == 'play':
script = plexpy.CONFIG.SCRIPTS_ON_PLAY_SCRIPT
elif notify_action == 'stop':
script = plexpy.CONFIG.SCRIPTS_ON_STOP_SCRIPT
elif notify_action == 'pause':
script = plexpy.CONFIG.SCRIPTS_ON_PAUSE_SCRIPT
elif notify_action == 'resume':
script = plexpy.CONFIG.SCRIPTS_ON_RESUME_SCRIPT
elif notify_action == 'buffer':
script = plexpy.CONFIG.SCRIPTS_ON_BUFFER_SCRIPT
elif notify_action == 'extdown':
script = plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT
elif notify_action == 'extup':
script = plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT
elif notify_action == 'intdown':
script = plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT
elif notify_action == 'intup':
script = plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT
elif notify_action == 'created':
script = plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT
elif notify_action == 'watched':
script = plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT
else:
# For manual scripts
script = kwargs.get('script', '')
# Don't try to run the script if the action does not have one
if notify_action and not script:
logger.debug(u"PlexPy Notifiers :: No script selected for action %s, exiting..." % notify_action)
return
elif not script:
logger.debug(u"PlexPy Notifiers :: No script selected, exiting...")
return
name, ext = os.path.splitext(script)
if ext == '.py':
prefix = 'python'
elif ext == '.pyw':
prefix = 'pythonw'
elif ext == '.php':
prefix = 'php'
elif ext == '.pl':
prefix = 'perl'
elif ext == '.rb':
prefix = 'ruby'
else:
prefix = ''
if os.name == 'nt':
script = script.encode(plexpy.SYS_ENCODING, 'ignore')
if prefix:
script = [prefix, script]
else:
script = [script]
# For manual notifications
if script_args and isinstance(script_args, basestring):
# attemps for format it for the user
script_args = shlex.split(script_args)
# Windows handles unicode very badly.
# https://bugs.python.org/issue19264
if script_args and os.name == 'nt':
script_args = [s.encode(plexpy.SYS_ENCODING, 'ignore') for s in script_args]
# Allow overrides for shitty systems
if prefix and script_args:
if script_args[0] in ['python2', 'python', 'pythonw', 'php', 'ruby', 'perl']:
script[0] = script_args[0]
del script_args[0]
script.extend(script_args)
logger.debug(u"PlexPy Notifiers :: Full script is: %s" % script)
try:
p = subprocess.Popen(script, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=plexpy.CONFIG.SCRIPTS_FOLDER)
out, error = p.communicate()
status = p.returncode
if out and status:
out = out.strip()
logger.debug(u"PlexPy Notifiers :: Script returned: %s" % out)
if error:
error = error.strip()
logger.error(u"PlexPy Notifiers :: Script error: %s" % error)
else:
logger.info(u"PlexPy Notifiers :: Script notification sent.")
except OSError as e:
logger.error(u"PlexPy Notifiers :: Failed to run script: %s" % e)
def return_config_options(self):
config_option = [{'label': 'Warning',
'description': '<strong>Script notifications are currently experimental!</strong><br><br>\
Supported file types: ' + ', '.join(self.script_exts),
'input_type': 'help'
},
{'label': 'Script folder',
'value': plexpy.CONFIG.SCRIPTS_FOLDER,
'name': 'scripts_folder',
'description': 'Add your script folder.',
'input_type': 'text',
},
{'label': 'Playback Start',
'value': plexpy.CONFIG.SCRIPTS_ON_PLAY_SCRIPT,
'name': 'scripts_on_play_script',
'description': 'Choose the script for on play.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Playback Stop',
'value': plexpy.CONFIG.SCRIPTS_ON_STOP_SCRIPT,
'name': 'scripts_on_stop_script',
'description': 'Choose the script for on stop.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Playback Pause',
'value': plexpy.CONFIG.SCRIPTS_ON_PAUSE_SCRIPT,
'name': 'scripts_on_pause_script',
'description': 'Choose the script for on pause.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Playback Resume',
'value': plexpy.CONFIG.SCRIPTS_ON_RESUME_SCRIPT,
'name': 'scripts_on_resume_script',
'description': 'Choose the script for on resume.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Watched',
'value': plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT,
'name': 'scripts_on_watched_script',
'description': 'Choose the script for on watched.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Buffer Warnings',
'value': plexpy.CONFIG.SCRIPTS_ON_BUFFER_SCRIPT,
'name': 'scripts_on_buffer_script',
'description': 'Choose the script for buffer warnings.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Recently Added',
'value': plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT,
'name': 'scripts_on_created_script',
'description': 'Choose the script for recently added.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Plex Remote Access Down',
'value': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
'name': 'scripts_on_extdown_script',
'description': 'Choose the script for Plex remote access down.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Plex Server Down',
'value': plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT,
'name': 'scripts_on_intdown_script',
'description': 'Choose the script for Plex server down.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Plex Remote Access Back Up',
'value': plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT,
'name': 'scripts_on_extup_script',
'description': 'Choose the script for Plex remote access back up.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Plex Server Back Up',
'value': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
'name': 'scripts_on_intup_script',
'description': 'Choose the script for Plex server back up.',
'input_type': 'select',
'select_options': self.list_scripts()
}
]
return config_option
class FacebookNotifier(object):
def __init__(self):
self.redirect_uri = plexpy.CONFIG.FACEBOOK_REDIRECT_URI
self.app_id = plexpy.CONFIG.FACEBOOK_APP_ID
self.app_secret = plexpy.CONFIG.FACEBOOK_APP_SECRET
self.group_id = plexpy.CONFIG.FACEBOOK_GROUP
def notify(self, subject, message):
if not subject or not message:
return
else:
self._post_facebook(subject + ': ' + message)
def test_notify(self):
return self._post_facebook(u"PlexPy Notifiers :: This is a test notification from PlexPy at " + helpers.now())
def _get_authorization(self):
return facebook.auth_url(app_id=self.app_id,
canvas_url=self.redirect_uri + '/facebookStep2',
perms=['user_managed_groups','publish_actions'])
def _get_credentials(self, code):
logger.info(u"PlexPy Notifiers :: Requesting access token from Facebook")
try:
# Request user access token
api = facebook.GraphAPI(version='2.5')
response = api.get_access_token_from_code(code=code,
redirect_uri=self.redirect_uri + '/facebookStep2',
app_id=self.app_id,
app_secret=self.app_secret)
access_token = response['access_token']
# Request extended user access token
api = facebook.GraphAPI(access_token=access_token, version='2.5')
response = api.extend_access_token(app_id=self.app_id,
app_secret=self.app_secret)
access_token = response['access_token']
plexpy.CONFIG.FACEBOOK_TOKEN = access_token
plexpy.CONFIG.write()
except Exception as e:
logger.error(u"PlexPy Notifiers :: Error requesting Facebook access token: %s" % e)
return False
return True
def _post_facebook(self, message=None):
access_token = plexpy.CONFIG.FACEBOOK_TOKEN
group_id = plexpy.CONFIG.FACEBOOK_GROUP
if group_id:
api = facebook.GraphAPI(access_token=access_token, version='2.5')
try:
api.put_wall_post(profile_id=group_id, message=message)
logger.info(u"PlexPy Notifiers :: Facebook notification sent.")
except Exception as e:
logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: %s" % e)
return False
return True
else:
logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: No Facebook Group ID provided.")
return False
def return_config_options(self):
config_option = [{'label': 'Instructions',
'description': '<strong>Facebook notifications are currently experimental!</strong><br><br> \
Step 1: Visit <a href="https://developers.facebook.com/apps/" target="_blank"> \
Facebook Developers</a> to create a new app using <strong>advanced setup</strong>.<br>\
Step 2: Go to <strong>Settings > Advanced</strong> and fill in \
<strong>Valid OAuth redirect URIs</strong> with your PlexPy URL (i.e. http://localhost:8181).<br>\
Step 3: Fill in the <strong>PlexPy URL</strong> below with the exact same URL from Step 2.<br>\
Step 4: Fill in the <strong>App ID</strong> and <strong>App Secret</strong> below.<br>\
Step 5: Click the <strong>Request Authorization</strong> button below.',
'input_type': 'help'
},
{'label': 'PlexPy URL',
'value': self.redirect_uri,
'name': 'facebook_redirect_uri',
'description': 'Your PlexPy URL. This will tell Facebook where to redirect you after authorization.',
'input_type': 'text'
},
{'label': 'Facebook App ID',
'value': self.app_id,
'name': 'facebook_app_id',
'description': 'Your Facebook app ID.',
'input_type': 'text'
},
{'label': 'Facebook App Secret',
'value': self.app_secret,
'name': 'facebook_app_secret',
'description': 'Your Facebook app secret.',
'input_type': 'text'
},
{'label': 'Request Authorization',
'value': 'Request Authorization',
'name': 'facebookStep1',
'description': 'Request Facebook authorization. (Ensure you allow the browser pop-up).',
'input_type': 'button'
},
{'label': 'Facebook Group ID',
'value': self.group_id,
'name': 'facebook_group',
'description': 'Your Facebook Group ID.',
'input_type': 'text'
}
]
return config_option