mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 13:41:15 -07:00
Add Facebook notification agent
This commit is contained in:
parent
754df5bea7
commit
7befbef6ec
8 changed files with 713 additions and 29 deletions
|
@ -235,8 +235,8 @@ fieldset[disabled] .btn-bright:active,
|
||||||
.btn-bright.disabled.active,
|
.btn-bright.disabled.active,
|
||||||
.btn-bright[disabled].active,
|
.btn-bright[disabled].active,
|
||||||
fieldset[disabled] .btn-bright.active {
|
fieldset[disabled] .btn-bright.active {
|
||||||
background-color: #5cb85c;
|
background-color: #c9302c;
|
||||||
border-color: #4cae4c;
|
border-color: #ac2925;
|
||||||
}
|
}
|
||||||
.btn-bright .badge {
|
.btn-bright .badge {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -14,7 +14,12 @@ from plexpy import helpers
|
||||||
<form action="set_notification_config" method="post" class="form" id="set_notification_config" data-parsley-validate>
|
<form action="set_notification_config" method="post" class="form" id="set_notification_config" data-parsley-validate>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
% for item in data:
|
% for item in data:
|
||||||
% if item['input_type'] == 'text' or item['input_type'] == 'number' or item['input_type'] == 'password':
|
% if item['input_type'] == 'help':
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${item['label']}</label>
|
||||||
|
<p class="help-block">${item['description'] | n}</p>
|
||||||
|
</div>
|
||||||
|
% elif item['input_type'] == 'text' or item['input_type'] == 'number' or item['input_type'] == 'password':
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="${item['name']}">${item['label']}</label>
|
<label for="${item['name']}">${item['label']}</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -29,6 +34,7 @@ from plexpy import helpers
|
||||||
</div>
|
</div>
|
||||||
% elif item['input_type'] == 'button':
|
% elif item['input_type'] == 'button':
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label for="${item['name']}">${item['label']}</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<input type="${item['input_type']}" class="btn btn-bright" id="${item['name']}" name="${item['name']}" value="${item['value']}">
|
<input type="${item['input_type']}" class="btn btn-bright" id="${item['name']}" name="${item['name']}" value="${item['value']}">
|
||||||
|
@ -146,8 +152,7 @@ from plexpy import helpers
|
||||||
|
|
||||||
$('#osxnotifyregister').click(function () {
|
$('#osxnotifyregister').click(function () {
|
||||||
var osx_notify_app = $("#osx_notify_app").val();
|
var osx_notify_app = $("#osx_notify_app").val();
|
||||||
$.get("/osxnotifyregister", { 'app': osx_notify_app }, function (data) { $('#ajaxMsg').html("<i class='fa fa-check'></i> " + data); });
|
$.get("/osxnotifyregister", { 'app': osx_notify_app }, function (data) { showMsg("<i class='fa fa-check'></i> " + data, false, true, 3000); });
|
||||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#save-notification-item').click(function () {
|
$('#save-notification-item').click(function () {
|
||||||
|
@ -157,15 +162,37 @@ from plexpy import helpers
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function disableTwitterVerify() {
|
||||||
|
if ($('#twitter_key').val() != '') { $('#twitterStep2').prop('disabled', false); }
|
||||||
|
else { $('#twitterStep2').prop('disabled', true); }
|
||||||
|
}
|
||||||
|
disableTwitterVerify();
|
||||||
|
$('#twitter_key').on('change', function () {
|
||||||
|
disableTwitterVerify()
|
||||||
|
});
|
||||||
|
|
||||||
$('#twitterStep1').click(function () {
|
$('#twitterStep1').click(function () {
|
||||||
$.get("/twitterStep1", function (data) {window.open(data); })
|
$.get("/twitterStep1", function (data) {window.open(data); })
|
||||||
.done(function () { $('#ajaxMsg').html("<i class='fa fa-check'></i> Confirm Authorization. Check pop-up blocker if no response."); });
|
.done(function () { showMsg("<i class='fa fa-check'></i> Confirm Authorization. Check pop-up blocker if no response.", false, true, 3000); });
|
||||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
|
||||||
});
|
});
|
||||||
$('#twitterStep2').click(function () {
|
$('#twitterStep2').click(function () {
|
||||||
var twitter_key = $("#twitter_key").val();
|
var twitter_key = $("#twitter_key").val();
|
||||||
$.get("/twitterStep2", { 'key': twitter_key }, function (data) { $('#ajaxMsg').html("<i class='fa fa-check'></i> " + data); });
|
$.get("/twitterStep2", { 'key': twitter_key }, function (data) { showMsg("<i class='fa fa-check'></i> " + data, false, true, 3000); });
|
||||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
});
|
||||||
|
|
||||||
|
function disableFacebookRequest() {
|
||||||
|
if ($('#facebook_app_id').val() != '' && $('#facebook_app_secret').val() != '') { $('#facebookStep1').prop('disabled', false); }
|
||||||
|
else { $('#facebookStep1').prop('disabled', true); }
|
||||||
|
}
|
||||||
|
disableFacebookRequest();
|
||||||
|
$('#facebook_app_id, #facebook_app_secret').on('change', function () {
|
||||||
|
disableFacebookRequest()
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#facebookStep1').click(function () {
|
||||||
|
doAjaxCall('set_notification_config', $(this), 'tabs', true);
|
||||||
|
$.get("/facebookStep1", function (data) {window.open(data); })
|
||||||
|
.done(function () { showMsg("<i class='fa fa-check'></i> Confirm Authorization. Check pop-up blocker if no response.", false, true, 3000); });
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#test_notifier').click(function () {
|
$('#test_notifier').click(function () {
|
||||||
|
|
|
@ -1563,8 +1563,7 @@ $(document).ready(function() {
|
||||||
|
|
||||||
$('#osxnotifyregister').click(function () {
|
$('#osxnotifyregister').click(function () {
|
||||||
var osx_notify_app = $("#osx_notify_reg").val();
|
var osx_notify_app = $("#osx_notify_reg").val();
|
||||||
$.get("/osxnotifyregister", {'app': osx_notify_app}, function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); });
|
$.get("/osxnotifyregister", { 'app': osx_notify_app }, function (data) { showMsg("<div class='msg'><span class='ui-icon ui-icon-check'></span>" + data + "</div>", false, true, 3000); });
|
||||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
457
lib/pythonfacebook/__init__.py
Normal file
457
lib/pythonfacebook/__init__.py
Normal file
|
@ -0,0 +1,457 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright 2010 Facebook
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Python client library for the Facebook Platform.
|
||||||
|
|
||||||
|
This client library is designed to support the Graph API and the
|
||||||
|
official Facebook JavaScript SDK, which is the canonical way to
|
||||||
|
implement Facebook authentication. Read more about the Graph API at
|
||||||
|
http://developers.facebook.com/docs/api. You can download the Facebook
|
||||||
|
JavaScript SDK at http://github.com/facebook/connect-js/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import binascii
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib.parse import parse_qs, urlencode
|
||||||
|
except ImportError:
|
||||||
|
from urlparse import parse_qs
|
||||||
|
from urllib import urlencode
|
||||||
|
|
||||||
|
from . import version
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = version.__version__
|
||||||
|
|
||||||
|
|
||||||
|
VALID_API_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5"]
|
||||||
|
|
||||||
|
|
||||||
|
class GraphAPI(object):
|
||||||
|
"""A client for the Facebook Graph API.
|
||||||
|
|
||||||
|
See http://developers.facebook.com/docs/api for complete
|
||||||
|
documentation for the API.
|
||||||
|
|
||||||
|
The Graph API is made up of the objects in Facebook (e.g., people,
|
||||||
|
pages, events, photos) and the connections between them (e.g.,
|
||||||
|
friends, photo tags, and event RSVPs). This client provides access
|
||||||
|
to those primitive types in a generic way. For example, given an
|
||||||
|
OAuth access token, this will fetch the profile of the active user
|
||||||
|
and the list of the user's friends:
|
||||||
|
|
||||||
|
graph = facebook.GraphAPI(access_token)
|
||||||
|
user = graph.get_object("me")
|
||||||
|
friends = graph.get_connections(user["id"], "friends")
|
||||||
|
|
||||||
|
You can see a list of all of the objects and connections supported
|
||||||
|
by the API at http://developers.facebook.com/docs/reference/api/.
|
||||||
|
|
||||||
|
You can obtain an access token via OAuth or by using the Facebook
|
||||||
|
JavaScript SDK. See
|
||||||
|
http://developers.facebook.com/docs/authentication/ for details.
|
||||||
|
|
||||||
|
If you are using the JavaScript SDK, you can use the
|
||||||
|
get_user_from_cookie() method below to get the OAuth access token
|
||||||
|
for the active user from the cookie saved by the SDK.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, access_token=None, timeout=None, version=None,
|
||||||
|
proxies=None):
|
||||||
|
# The default version is only used if the version kwarg does not exist.
|
||||||
|
default_version = "2.0"
|
||||||
|
|
||||||
|
self.access_token = access_token
|
||||||
|
self.timeout = timeout
|
||||||
|
self.proxies = proxies
|
||||||
|
|
||||||
|
if version:
|
||||||
|
version_regex = re.compile("^\d\.\d$")
|
||||||
|
match = version_regex.search(str(version))
|
||||||
|
if match is not None:
|
||||||
|
if str(version) not in VALID_API_VERSIONS:
|
||||||
|
raise GraphAPIError("Valid API versions are " +
|
||||||
|
str(VALID_API_VERSIONS).strip('[]'))
|
||||||
|
else:
|
||||||
|
self.version = "v" + str(version)
|
||||||
|
else:
|
||||||
|
raise GraphAPIError("Version number should be in the"
|
||||||
|
" following format: #.# (e.g. 2.0).")
|
||||||
|
else:
|
||||||
|
self.version = "v" + default_version
|
||||||
|
|
||||||
|
def get_object(self, id, **args):
|
||||||
|
"""Fetches the given object from the graph."""
|
||||||
|
return self.request(self.version + "/" + id, args)
|
||||||
|
|
||||||
|
def get_objects(self, ids, **args):
|
||||||
|
"""Fetches all of the given object from the graph.
|
||||||
|
|
||||||
|
We return a map from ID to object. If any of the IDs are
|
||||||
|
invalid, we raise an exception.
|
||||||
|
"""
|
||||||
|
args["ids"] = ",".join(ids)
|
||||||
|
return self.request(self.version + "/", args)
|
||||||
|
|
||||||
|
def get_connections(self, id, connection_name, **args):
|
||||||
|
"""Fetches the connections for given object."""
|
||||||
|
return self.request(
|
||||||
|
"%s/%s/%s" % (self.version, id, connection_name), args)
|
||||||
|
|
||||||
|
def put_object(self, parent_object, connection_name, **data):
|
||||||
|
"""Writes the given object to the graph, connected to the given parent.
|
||||||
|
|
||||||
|
For example,
|
||||||
|
|
||||||
|
graph.put_object("me", "feed", message="Hello, world")
|
||||||
|
|
||||||
|
writes "Hello, world" to the active user's wall. Likewise, this
|
||||||
|
will comment on the first post of the active user's feed:
|
||||||
|
|
||||||
|
feed = graph.get_connections("me", "feed")
|
||||||
|
post = feed["data"][0]
|
||||||
|
graph.put_object(post["id"], "comments", message="First!")
|
||||||
|
|
||||||
|
See http://developers.facebook.com/docs/api#publishing for all
|
||||||
|
of the supported writeable objects.
|
||||||
|
|
||||||
|
Certain write operations require extended permissions. For
|
||||||
|
example, publishing to a user's feed requires the
|
||||||
|
"publish_actions" permission. See
|
||||||
|
http://developers.facebook.com/docs/publishing/ for details
|
||||||
|
about publishing permissions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
assert self.access_token, "Write operations require an access token"
|
||||||
|
return self.request(
|
||||||
|
self.version + "/" + parent_object + "/" + connection_name,
|
||||||
|
post_args=data,
|
||||||
|
method="POST")
|
||||||
|
|
||||||
|
def put_wall_post(self, message, attachment={}, profile_id="me"):
|
||||||
|
"""Writes a wall post to the given profile's wall.
|
||||||
|
|
||||||
|
We default to writing to the authenticated user's wall if no
|
||||||
|
profile_id is specified.
|
||||||
|
|
||||||
|
attachment adds a structured attachment to the status message
|
||||||
|
being posted to the Wall. It should be a dictionary of the form:
|
||||||
|
|
||||||
|
{"name": "Link name"
|
||||||
|
"link": "http://www.example.com/",
|
||||||
|
"caption": "{*actor*} posted a new review",
|
||||||
|
"description": "This is a longer description of the attachment",
|
||||||
|
"picture": "http://www.example.com/thumbnail.jpg"}
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.put_object(profile_id, "feed", message=message,
|
||||||
|
**attachment)
|
||||||
|
|
||||||
|
def put_comment(self, object_id, message):
|
||||||
|
"""Writes the given comment on the given post."""
|
||||||
|
return self.put_object(object_id, "comments", message=message)
|
||||||
|
|
||||||
|
def put_like(self, object_id):
|
||||||
|
"""Likes the given post."""
|
||||||
|
return self.put_object(object_id, "likes")
|
||||||
|
|
||||||
|
def delete_object(self, id):
|
||||||
|
"""Deletes the object with the given ID from the graph."""
|
||||||
|
self.request(self.version + "/" + id, method="DELETE")
|
||||||
|
|
||||||
|
def delete_request(self, user_id, request_id):
|
||||||
|
"""Deletes the Request with the given ID for the given user."""
|
||||||
|
self.request("%s_%s" % (request_id, user_id), method="DELETE")
|
||||||
|
|
||||||
|
def put_photo(self, image, album_path="me/photos", **kwargs):
|
||||||
|
"""
|
||||||
|
Upload an image using multipart/form-data.
|
||||||
|
|
||||||
|
image - A file object representing the image to be uploaded.
|
||||||
|
album_path - A path representing where the image should be uploaded.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.request(
|
||||||
|
self.version + "/" + album_path,
|
||||||
|
post_args=kwargs,
|
||||||
|
files={"source": image},
|
||||||
|
method="POST")
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
"""Fetches the current version number of the Graph API being used."""
|
||||||
|
args = {"access_token": self.access_token}
|
||||||
|
try:
|
||||||
|
response = requests.request("GET",
|
||||||
|
"https://graph.facebook.com/" +
|
||||||
|
self.version + "/me",
|
||||||
|
params=args,
|
||||||
|
timeout=self.timeout,
|
||||||
|
proxies=self.proxies)
|
||||||
|
except requests.HTTPError as e:
|
||||||
|
response = json.loads(e.read())
|
||||||
|
raise GraphAPIError(response)
|
||||||
|
|
||||||
|
try:
|
||||||
|
headers = response.headers
|
||||||
|
version = headers["facebook-api-version"].replace("v", "")
|
||||||
|
return float(version)
|
||||||
|
except Exception:
|
||||||
|
raise GraphAPIError("API version number not available")
|
||||||
|
|
||||||
|
def request(
|
||||||
|
self, path, args=None, post_args=None, files=None, method=None):
|
||||||
|
"""Fetches the given path in the Graph API.
|
||||||
|
|
||||||
|
We translate args to a valid query string. If post_args is
|
||||||
|
given, we send a POST request to the given path with the given
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
"""
|
||||||
|
args = args or {}
|
||||||
|
|
||||||
|
if post_args is not None:
|
||||||
|
method = "POST"
|
||||||
|
|
||||||
|
if self.access_token:
|
||||||
|
if post_args is not None:
|
||||||
|
post_args["access_token"] = self.access_token
|
||||||
|
else:
|
||||||
|
args["access_token"] = self.access_token
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.request(method or "GET",
|
||||||
|
"https://graph.facebook.com/" +
|
||||||
|
path,
|
||||||
|
timeout=self.timeout,
|
||||||
|
params=args,
|
||||||
|
data=post_args,
|
||||||
|
proxies=self.proxies,
|
||||||
|
files=files)
|
||||||
|
except requests.HTTPError as e:
|
||||||
|
response = json.loads(e.read())
|
||||||
|
raise GraphAPIError(response)
|
||||||
|
|
||||||
|
headers = response.headers
|
||||||
|
if 'json' in headers['content-type']:
|
||||||
|
result = response.json()
|
||||||
|
elif 'image/' in headers['content-type']:
|
||||||
|
mimetype = headers['content-type']
|
||||||
|
result = {"data": response.content,
|
||||||
|
"mime-type": mimetype,
|
||||||
|
"url": response.url}
|
||||||
|
elif "access_token" in parse_qs(response.text):
|
||||||
|
query_str = parse_qs(response.text)
|
||||||
|
if "access_token" in query_str:
|
||||||
|
result = {"access_token": query_str["access_token"][0]}
|
||||||
|
if "expires" in query_str:
|
||||||
|
result["expires"] = query_str["expires"][0]
|
||||||
|
else:
|
||||||
|
raise GraphAPIError(response.json())
|
||||||
|
else:
|
||||||
|
raise GraphAPIError('Maintype was not text, image, or querystring')
|
||||||
|
|
||||||
|
if result and isinstance(result, dict) and result.get("error"):
|
||||||
|
raise GraphAPIError(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def fql(self, query):
|
||||||
|
"""FQL query.
|
||||||
|
|
||||||
|
Example query: "SELECT affiliations FROM user WHERE uid = me()"
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.request(self.version + "/" + "fql", {"q": query})
|
||||||
|
|
||||||
|
def get_app_access_token(self, app_id, app_secret):
|
||||||
|
"""Get the application's access token as a string."""
|
||||||
|
args = {'grant_type': 'client_credentials',
|
||||||
|
'client_id': app_id,
|
||||||
|
'client_secret': app_secret}
|
||||||
|
|
||||||
|
return self.request("oauth/access_token", args=args)["access_token"]
|
||||||
|
|
||||||
|
def get_access_token_from_code(
|
||||||
|
self, code, redirect_uri, app_id, app_secret):
|
||||||
|
"""Get an access token from the "code" returned from an OAuth dialog.
|
||||||
|
|
||||||
|
Returns a dict containing the user-specific access token and its
|
||||||
|
expiration date (if applicable).
|
||||||
|
|
||||||
|
"""
|
||||||
|
args = {
|
||||||
|
"code": code,
|
||||||
|
"redirect_uri": redirect_uri,
|
||||||
|
"client_id": app_id,
|
||||||
|
"client_secret": app_secret}
|
||||||
|
|
||||||
|
return self.request("oauth/access_token", args)
|
||||||
|
|
||||||
|
def extend_access_token(self, app_id, app_secret):
|
||||||
|
"""
|
||||||
|
Extends the expiration time of a valid OAuth access token. See
|
||||||
|
<https://developers.facebook.com/roadmap/offline-access-removal/
|
||||||
|
#extend_token>
|
||||||
|
|
||||||
|
"""
|
||||||
|
args = {
|
||||||
|
"client_id": app_id,
|
||||||
|
"client_secret": app_secret,
|
||||||
|
"grant_type": "fb_exchange_token",
|
||||||
|
"fb_exchange_token": self.access_token,
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.request("oauth/access_token", args=args)
|
||||||
|
|
||||||
|
def debug_access_token(self, token, app_id, app_secret):
|
||||||
|
"""
|
||||||
|
Gets information about a user access token issued by an app. See
|
||||||
|
<https://developers.facebook.com/docs/facebook-login/access-tokens
|
||||||
|
#debug>
|
||||||
|
|
||||||
|
We can generate the app access token by concatenating the app
|
||||||
|
id and secret: <https://developers.facebook.com/docs/
|
||||||
|
facebook-login/access-tokens#apptokens>
|
||||||
|
|
||||||
|
"""
|
||||||
|
args = {
|
||||||
|
"input_token": token,
|
||||||
|
"access_token": "%s|%s" % (app_id, app_secret)
|
||||||
|
}
|
||||||
|
return self.request("/debug_token", args=args)
|
||||||
|
|
||||||
|
|
||||||
|
class GraphAPIError(Exception):
|
||||||
|
def __init__(self, result):
|
||||||
|
self.result = result
|
||||||
|
self.code = None
|
||||||
|
try:
|
||||||
|
self.type = result["error_code"]
|
||||||
|
except:
|
||||||
|
self.type = ""
|
||||||
|
|
||||||
|
# OAuth 2.0 Draft 10
|
||||||
|
try:
|
||||||
|
self.message = result["error_description"]
|
||||||
|
except:
|
||||||
|
# OAuth 2.0 Draft 00
|
||||||
|
try:
|
||||||
|
self.message = result["error"]["message"]
|
||||||
|
self.code = result["error"].get("code")
|
||||||
|
if not self.type:
|
||||||
|
self.type = result["error"].get("type", "")
|
||||||
|
except:
|
||||||
|
# REST server style
|
||||||
|
try:
|
||||||
|
self.message = result["error_msg"]
|
||||||
|
except:
|
||||||
|
self.message = result
|
||||||
|
|
||||||
|
Exception.__init__(self, self.message)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_from_cookie(cookies, app_id, app_secret):
|
||||||
|
"""Parses the cookie set by the official Facebook JavaScript SDK.
|
||||||
|
|
||||||
|
cookies should be a dictionary-like object mapping cookie names to
|
||||||
|
cookie values.
|
||||||
|
|
||||||
|
If the user is logged in via Facebook, we return a dictionary with
|
||||||
|
the keys "uid" and "access_token". The former is the user's
|
||||||
|
Facebook ID, and the latter can be used to make authenticated
|
||||||
|
requests to the Graph API. If the user is not logged in, we
|
||||||
|
return None.
|
||||||
|
|
||||||
|
Download the official Facebook JavaScript SDK at
|
||||||
|
http://github.com/facebook/connect-js/. Read more about Facebook
|
||||||
|
authentication at
|
||||||
|
http://developers.facebook.com/docs/authentication/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
cookie = cookies.get("fbsr_" + app_id, "")
|
||||||
|
if not cookie:
|
||||||
|
return None
|
||||||
|
parsed_request = parse_signed_request(cookie, app_secret)
|
||||||
|
if not parsed_request:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
result = GraphAPI().get_access_token_from_code(
|
||||||
|
parsed_request["code"], "", app_id, app_secret)
|
||||||
|
except GraphAPIError:
|
||||||
|
return None
|
||||||
|
result["uid"] = parsed_request["user_id"]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def parse_signed_request(signed_request, app_secret):
|
||||||
|
""" Return dictionary with signed request data.
|
||||||
|
|
||||||
|
We return a dictionary containing the information in the
|
||||||
|
signed_request. This includes a user_id if the user has authorised
|
||||||
|
your application, as well as any information requested.
|
||||||
|
|
||||||
|
If the signed_request is malformed or corrupted, False is returned.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
encoded_sig, payload = map(str, signed_request.split('.', 1))
|
||||||
|
|
||||||
|
sig = base64.urlsafe_b64decode(encoded_sig + "=" *
|
||||||
|
((4 - len(encoded_sig) % 4) % 4))
|
||||||
|
data = base64.urlsafe_b64decode(payload + "=" *
|
||||||
|
((4 - len(payload) % 4) % 4))
|
||||||
|
except IndexError:
|
||||||
|
# Signed request was malformed.
|
||||||
|
return False
|
||||||
|
except TypeError:
|
||||||
|
# Signed request had a corrupted payload.
|
||||||
|
return False
|
||||||
|
except binascii.Error:
|
||||||
|
# Signed request had a corrupted payload.
|
||||||
|
return False
|
||||||
|
|
||||||
|
data = json.loads(data.decode('ascii'))
|
||||||
|
if data.get('algorithm', '').upper() != 'HMAC-SHA256':
|
||||||
|
return False
|
||||||
|
|
||||||
|
# HMAC can only handle ascii (byte) strings
|
||||||
|
# http://bugs.python.org/issue5285
|
||||||
|
app_secret = app_secret.encode('ascii')
|
||||||
|
payload = payload.encode('ascii')
|
||||||
|
|
||||||
|
expected_sig = hmac.new(app_secret,
|
||||||
|
msg=payload,
|
||||||
|
digestmod=hashlib.sha256).digest()
|
||||||
|
if sig != expected_sig:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def auth_url(app_id, canvas_url, perms=None, **kwargs):
|
||||||
|
url = "https://www.facebook.com/dialog/oauth?"
|
||||||
|
kvps = {'client_id': app_id, 'redirect_uri': canvas_url}
|
||||||
|
if perms:
|
||||||
|
kvps['scope'] = ",".join(perms)
|
||||||
|
kvps.update(kwargs)
|
||||||
|
return url + urlencode(kvps)
|
17
lib/pythonfacebook/version.py
Normal file
17
lib/pythonfacebook/version.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright 2014 Martey Dodoo
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
__version__ = "1.0.0-alpha"
|
|
@ -79,6 +79,22 @@ _CONFIG_DEFINITIONS = {
|
||||||
'EMAIL_ON_EXTUP': (int, 'Email', 0),
|
'EMAIL_ON_EXTUP': (int, 'Email', 0),
|
||||||
'EMAIL_ON_INTUP': (int, 'Email', 0),
|
'EMAIL_ON_INTUP': (int, 'Email', 0),
|
||||||
'ENABLE_HTTPS': (int, 'General', 0),
|
'ENABLE_HTTPS': (int, 'General', 0),
|
||||||
|
'FACEBOOK_ENABLED': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_APP_ID': (str, 'Facebook', ''),
|
||||||
|
'FACEBOOK_APP_SECRET': (str, 'Facebook', ''),
|
||||||
|
'FACEBOOK_TOKEN': (str, 'Facebook', ''),
|
||||||
|
'FACEBOOK_GROUP': (str, 'Facebook', ''),
|
||||||
|
'FACEBOOK_ON_PLAY': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_STOP': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_PAUSE': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_RESUME': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_BUFFER': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_WATCHED': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_CREATED': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_EXTDOWN': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_INTDOWN': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_EXTUP': (int, 'Facebook', 0),
|
||||||
|
'FACEBOOK_ON_INTUP': (int, 'Facebook', 0),
|
||||||
'FIRST_RUN_COMPLETE': (int, 'General', 0),
|
'FIRST_RUN_COMPLETE': (int, 'General', 0),
|
||||||
'FREEZE_DB': (int, 'General', 0),
|
'FREEZE_DB': (int, 'General', 0),
|
||||||
'GIT_BRANCH': (str, 'General', 'master'),
|
'GIT_BRANCH': (str, 'General', 'master'),
|
||||||
|
|
|
@ -34,6 +34,7 @@ from pynma import pynma
|
||||||
import gntp.notifier
|
import gntp.notifier
|
||||||
import oauth2 as oauth
|
import oauth2 as oauth
|
||||||
import pythontwitter as twitter
|
import pythontwitter as twitter
|
||||||
|
import pythonfacebook as facebook
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
from plexpy import logger, helpers, request
|
from plexpy import logger, helpers, request
|
||||||
|
@ -54,9 +55,10 @@ AGENT_IDS = {"Growl": 0,
|
||||||
"IFTTT": 12,
|
"IFTTT": 12,
|
||||||
"Telegram": 13,
|
"Telegram": 13,
|
||||||
"Slack": 14,
|
"Slack": 14,
|
||||||
"Scripts": 15}
|
"Scripts": 15,
|
||||||
|
"Facebook": 16}
|
||||||
|
|
||||||
|
|
||||||
def available_notification_agents():
|
def available_notification_agents():
|
||||||
agents = [{'name': 'Growl',
|
agents = [{'name': 'Growl',
|
||||||
'id': AGENT_IDS['Growl'],
|
'id': AGENT_IDS['Growl'],
|
||||||
|
@ -312,8 +314,24 @@ def available_notification_agents():
|
||||||
'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
|
'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
|
||||||
'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
|
'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
|
||||||
'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP
|
'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 Notifications should only be visible if it can be used
|
||||||
|
@ -341,7 +359,7 @@ def available_notification_agents():
|
||||||
|
|
||||||
|
|
||||||
def get_notification_agent_config(config_id):
|
def get_notification_agent_config(config_id):
|
||||||
if config_id:
|
if str(config_id).isdigit():
|
||||||
config_id = int(config_id)
|
config_id = int(config_id)
|
||||||
|
|
||||||
if config_id == 0:
|
if config_id == 0:
|
||||||
|
@ -392,6 +410,9 @@ def get_notification_agent_config(config_id):
|
||||||
elif config_id == 15:
|
elif config_id == 15:
|
||||||
script = Scripts()
|
script = Scripts()
|
||||||
return script.return_config_options()
|
return script.return_config_options()
|
||||||
|
elif config_id == 16:
|
||||||
|
facebook = FacebookNotifier()
|
||||||
|
return facebook.return_config_options()
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
|
@ -450,11 +471,15 @@ def send_notification(config_id, subject, body, **kwargs):
|
||||||
elif config_id == 15:
|
elif config_id == 15:
|
||||||
scripts = Scripts()
|
scripts = Scripts()
|
||||||
scripts.notify(message=body, subject=subject, **kwargs)
|
scripts.notify(message=body, subject=subject, **kwargs)
|
||||||
|
elif config_id == 16:
|
||||||
|
facebook = FacebookNotifier()
|
||||||
|
facebook.notify(subject=subject, message=body)
|
||||||
else:
|
else:
|
||||||
logger.debug(u"PlexPy Notifier :: Unknown agent id received.")
|
logger.debug(u"PlexPy Notifier :: Unknown agent id received.")
|
||||||
else:
|
else:
|
||||||
logger.debug(u"PlexPy Notifier :: Notification requested but no agent id received.")
|
logger.debug(u"PlexPy Notifier :: Notification requested but no agent id received.")
|
||||||
|
|
||||||
|
|
||||||
class GROWL(object):
|
class GROWL(object):
|
||||||
"""
|
"""
|
||||||
Growl notifications, for OS X.
|
Growl notifications, for OS X.
|
||||||
|
@ -1181,14 +1206,14 @@ class TwitterNotifier(object):
|
||||||
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
|
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
|
||||||
# logger.debug('oauth_consumer: ' + str(oauth_consumer))
|
# logger.debug('oauth_consumer: ' + str(oauth_consumer))
|
||||||
oauth_client = oauth.Client(oauth_consumer, token)
|
oauth_client = oauth.Client(oauth_consumer, token)
|
||||||
logger.info('oauth_client: ' + str(oauth_client))
|
# logger.info('oauth_client: ' + str(oauth_client))
|
||||||
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
|
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
|
||||||
logger.info('resp, content: ' + str(resp) + ',' + str(content))
|
# logger.info('resp, content: ' + str(resp) + ',' + str(content))
|
||||||
|
|
||||||
access_token = dict(parse_qsl(content))
|
access_token = dict(parse_qsl(content))
|
||||||
logger.info('access_token: ' + str(access_token))
|
# logger.info('access_token: ' + str(access_token))
|
||||||
|
|
||||||
logger.info('resp[status] = ' + str(resp['status']))
|
# logger.info('resp[status] = ' + str(resp['status']))
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
logger.info('The request for a token with did not succeed: ' + str(resp['status']), logger.ERROR)
|
logger.info('The request for a token with did not succeed: ' + str(resp['status']), logger.ERROR)
|
||||||
return False
|
return False
|
||||||
|
@ -1197,6 +1222,7 @@ class TwitterNotifier(object):
|
||||||
logger.info('Access Token secret: %s' % access_token['oauth_token_secret'])
|
logger.info('Access Token secret: %s' % access_token['oauth_token_secret'])
|
||||||
plexpy.CONFIG.TWITTER_USERNAME = access_token['oauth_token']
|
plexpy.CONFIG.TWITTER_USERNAME = access_token['oauth_token']
|
||||||
plexpy.CONFIG.TWITTER_PASSWORD = access_token['oauth_token_secret']
|
plexpy.CONFIG.TWITTER_PASSWORD = access_token['oauth_token_secret']
|
||||||
|
plexpy.CONFIG.write()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _send_tweet(self, message=None):
|
def _send_tweet(self, message=None):
|
||||||
|
@ -1205,35 +1231,42 @@ class TwitterNotifier(object):
|
||||||
access_token_key = plexpy.CONFIG.TWITTER_USERNAME
|
access_token_key = plexpy.CONFIG.TWITTER_USERNAME
|
||||||
access_token_secret = plexpy.CONFIG.TWITTER_PASSWORD
|
access_token_secret = plexpy.CONFIG.TWITTER_PASSWORD
|
||||||
|
|
||||||
logger.info(u"Sending tweet: " + message)
|
# logger.info(u"Sending tweet: " + message)
|
||||||
|
|
||||||
api = twitter.Api(username, password, access_token_key, access_token_secret)
|
api = twitter.Api(username, password, access_token_key, access_token_secret)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api.PostUpdate(message)
|
api.PostUpdate(message)
|
||||||
|
logger.info(u"Twitter notifications sent.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(u"Error Sending Tweet: %s" % e)
|
logger.info(u"Error sending Tweet: %s" % e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
config_option = [{'label': 'Request Authorisation',
|
config_option = [{'label': 'Instructions',
|
||||||
'value': 'Request Authorisation',
|
'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',
|
'name': 'twitterStep1',
|
||||||
'description': 'Step 1: Click Request button above. (Ensure you allow the browser pop-up).',
|
'description': 'Request Twitter authorization. (Ensure you allow the browser pop-up).',
|
||||||
'input_type': 'button'
|
'input_type': 'button'
|
||||||
},
|
},
|
||||||
{'label': 'Authorisation Key',
|
{'label': 'Authorization Key',
|
||||||
'value': '',
|
'value': '',
|
||||||
'name': 'twitter_key',
|
'name': 'twitter_key',
|
||||||
'description': 'Step 2: Input the authorisation key you received from Step 1.',
|
'description': 'Your Twitter authorization key.',
|
||||||
'input_type': 'text'
|
'input_type': 'text'
|
||||||
},
|
},
|
||||||
{'label': 'Verify Key',
|
{'label': 'Verify Key',
|
||||||
'value': 'Verify Key',
|
'value': 'Verify Key',
|
||||||
'name': 'twitterStep2',
|
'name': 'twitterStep2',
|
||||||
'description': 'Step 3: Verify the key.',
|
'description': 'Verify your Twitter authorization key.',
|
||||||
'input_type': 'button'
|
'input_type': 'button'
|
||||||
},
|
},
|
||||||
{'input_type': 'nosave'
|
{'input_type': 'nosave'
|
||||||
|
@ -1635,6 +1668,7 @@ class TELEGRAM(object):
|
||||||
|
|
||||||
return config_option
|
return config_option
|
||||||
|
|
||||||
|
|
||||||
class SLACK(object):
|
class SLACK(object):
|
||||||
"""
|
"""
|
||||||
Slack Notifications
|
Slack Notifications
|
||||||
|
@ -1958,3 +1992,120 @@ class Scripts(object):
|
||||||
]
|
]
|
||||||
|
|
||||||
return config_option
|
return config_option
|
||||||
|
|
||||||
|
|
||||||
|
class FacebookNotifier(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.app_id = plexpy.CONFIG.FACEBOOK_APP_ID
|
||||||
|
self.app_secret = plexpy.CONFIG.FACEBOOK_APP_SECRET
|
||||||
|
self.group_id = plexpy.CONFIG.FACEBOOK_GROUP
|
||||||
|
|
||||||
|
if plexpy.CONFIG.ENABLE_HTTPS:
|
||||||
|
protocol = 'https'
|
||||||
|
else:
|
||||||
|
protocol = 'http'
|
||||||
|
|
||||||
|
if plexpy.CONFIG.HTTP_HOST == '0.0.0.0':
|
||||||
|
host = 'localhost'
|
||||||
|
else:
|
||||||
|
host = plexpy.CONFIG.HTTP_HOST
|
||||||
|
|
||||||
|
self.redirect_url = '%s://%s:%i/facebookStep2' % (protocol, host, plexpy.CONFIG.HTTP_PORT)
|
||||||
|
|
||||||
|
|
||||||
|
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("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_url,
|
||||||
|
perms=['user_managed_groups','publish_actions'])
|
||||||
|
|
||||||
|
def _get_credentials(self, code):
|
||||||
|
logger.info('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_url,
|
||||||
|
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.info(u"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"Facebook notifications sent.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(u"Error sending Facebook post: %s" % e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.info('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 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>App ID</strong> and <strong>App Secret</strong> below.<br>\
|
||||||
|
Step 4: Click the <strong>Request Authorization</strong> button below.',
|
||||||
|
'input_type': 'help'
|
||||||
|
},
|
||||||
|
{'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
|
||||||
|
|
|
@ -695,12 +695,29 @@ class WebInterface(object):
|
||||||
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
tweet = notifiers.TwitterNotifier()
|
tweet = notifiers.TwitterNotifier()
|
||||||
result = tweet._get_credentials(key)
|
result = tweet._get_credentials(key)
|
||||||
logger.info(u"result: " + str(result))
|
# logger.info(u"result: " + str(result))
|
||||||
if result:
|
if result:
|
||||||
return "Key verification successful"
|
return "Key verification successful"
|
||||||
else:
|
else:
|
||||||
return "Unable to verify key"
|
return "Unable to verify key"
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
def facebookStep1(self):
|
||||||
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
|
facebook = notifiers.FacebookNotifier()
|
||||||
|
return facebook._get_authorization()
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
def facebookStep2(self, code):
|
||||||
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
|
facebook = notifiers.FacebookNotifier()
|
||||||
|
result = facebook._get_credentials(code)
|
||||||
|
# logger.info(u"result: " + str(result))
|
||||||
|
if result:
|
||||||
|
return "Key verification successful, you may close this page now."
|
||||||
|
else:
|
||||||
|
return "Unable to verify key"
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def osxnotifyregister(self, app):
|
def osxnotifyregister(self, app):
|
||||||
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue