Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save spuniun/56624e1464c621c91e52f88e03321582 to your computer and use it in GitHub Desktop.
Save spuniun/56624e1464c621c91e52f88e03321582 to your computer and use it in GitHub Desktop.
Tautulli notification script for Facebook Groups

Tautulli notification script for Facebook Groups

Prerequisites

You need Plex Media Server and Tautulli setup as well as a Facebook Group (you do not need to be a group admin). The script requires python2 and the python modules

argparse
ConfigParser
os
pickle
re
requests
sys
time

Installing

Place facebook.py and fbsettings.ini in the same folder where you execute Tautulli custom scripts from. This path must be readable and writable by the Tautulli process so that the script can store your Facebook login session.

Edit fbsettings.ini and provide your Facebook username, password and the group_id of the Facebook Group you want to post to. Do not use the vanity name of the group. To find your group_id, navigate to the group in your browser and View Source, then search for "group_id". You will find strings like the following throughout the source code with your numeric group_id:

?group_id=123456789101112&

Testing the script

You can easily test the script from the CLI to ensure your credentials are working.

usage: facebook.py [-h] -c POST_CONTENT [-u POST_URL] [-d]

optional arguments:
  -h, --help            show this help message and exit
  -c POST_CONTENT, --content POST_CONTENT
                        Post Content - use \n for line breaks
  -u POST_URL, --url POST_URL
                        Link to Include in Post
  -d, --debug           Enable Debugging Output - This may reveal Facebook
                        login details

For example

python facebook.py -c 'This is a test post' -u 'http://www.superbad.com/' -d

This step should help you correct any setup or configuration issues prior to integrating into Tautulli.

Configuration

  • In Tautulli > Settings > Notification Agents click Add a new notification agent.
  • Select Script.
  • Enter the folder where you saved facebook.py and fbsettings.ini and select facebook.py for your script file.
  • Select any triggers you wish to use.
  • Provide any notification conditions you require.
  • Provide the script arguments using the same conditional tags and metadata variables you would use for other notification agents.

For example, a Recently Added trigger argument might look like

<movie>-c "Added to {library_name}\n{title} ({year})" -u '{imdb_url}'</movie><episode>-c "Added to {library_name}\n{show_name} - S{season_num00}E{episode_num00} - {episode_name}" -u '{trakt_url}'</episode><season>-c "Added to {library_name}\n{show_name} - Season {season_num}" -u '{trakt_url}'</season><album>-c "Added to {library_name}\n{album_name} ({year}) - {artist_name}" -u '{lastfm_url}'</album> -d

Remember when using the Test feature in Tautulli, the conditional tags and metadata variables are not processed so use simple test strings.

-c 'This is a test post' -u 'http://www.superbad.com/' -d

Troubleshooting

You will find errors output in the Tautulli logs. Be sure to look in both the Notification Logs and Tautulli Logs to get a complete picture of what is occuring. If you need additional log output, use the -d argument to enable debugging mode.

You can message me @spuniun on Discord if you need additional assistance.

Acknowledgments

TODO

  • Nag for script updates based on gist hash

Since GraphAPI was revoked, no apps are getting approved and posting via email was shutdown, I pulled together a Tautulli notification python script for Facebook Groups using HTTP POST.

Changelog

0.19 - 2018-08-31

  • Removed sensitive information from debug logging
  • Convert HTML encoded characters to unicode

0.18 - 2018-08-17

  • Check for ability to write session cache
  • PEP8

0.17 - 2018-08-16

  • Fix: ConfigParser for python2
  • Style & Formating

0.13 - 2018-08-14

  • Support for line breaks
  • Other optimizations

0.10 - 2018-08-12

  • Bulletproof script output

0.8 - 2018-08-10

  • Added persistent login sessions

0.5 - 2018-08-09

  • Removed pyquery dependency

0.2 - 2018-08-04

  • Improved args

0.1 - 2018-08-04

  • Initial release
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Tautulli notification script for Facebook Groups
import errno
import os
import pickle
import re
import requests
import sys
import tempfile
import time
from argparse import ArgumentParser
from ConfigParser import ConfigParser, NoOptionError, NoSectionError
from HTMLParser import HTMLParser
version = 19
gist = 'https://gist.github.com/spuniun/56624e1464c621c91e52f88e03321582/'
# Location of settings and cache files.
credential_path = os.path.dirname(os.path.realpath(__file__))
credential_file = 'fbsettings.ini'
cookie_file = 'fbcookies.dat'
# Ensure that session storage path is writable.
try:
testfile = tempfile.TemporaryFile(dir = credential_path)
testfile.close()
except OSError as e:
if e.errno == errno.EACCES: # 13
print('ERROR: %s is not writable' % credential_path)
sys.exit(1)
# Read and validate configuration settings.
config = ConfigParser()
try:
with open('%s/%s' % (credential_path,credential_file)) as f:
config.readfp(f)
# Fail if Facebook credential settings are missing or mailformed.
try:
fb_username = config.get('Facebook', 'username')
if not re.match(
r"^[A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*$",
fb_username):
print('ERROR: Facebook username must be an email address')
sys.exit(1)
except (NoSectionError, NoOptionError):
print('ERROR: %s not setup - missing username' % credential_file)
sys.exit(1)
try:
fb_password = config.get('Facebook', 'password')
except (NoSectionError, NoOptionError):
print('ERROR: % not setup - missing password' % credential_file)
sys.exit(1)
try:
fb_group_id = config.get('Facebook', 'group-id')
try:
i = int(fb_group_id)
except ValueError:
print('ERROR: Please use the numeric Facebook Group ID')
sys.exit(1)
except (NoSectionError, NoOptionError):
print('ERROR: %s not setup - missing group-id' % credential_file)
sys.exit(1)
# Set default Session Time to 24 hours if not set.
try:
maxSessionTime = int(config.get('Facebook', 'session-time'))
except (NoSectionError, NoOptionError):
maxSessionTime = 86400
except IOError:
print('ERROR: %s/%s not found' % (credential_path,credential_file))
sys.exit(1)
# Parse the arguments.
parser = ArgumentParser()
parser.add_argument(
'-c',
'--content',
dest = 'post_content',
help = 'Post Content - use \\n for line breaks',
type = str,
required= True
)
parser.add_argument(
'-u',
'--url',
dest = 'post_url',
help = 'Link to Include in Post',
type = str,
default = ''
)
parser.add_argument(
'-d',
'--debug',
dest = 'debug',
help = 'Enable Debugging Output',
action='store_true'
)
args = parser.parse_args()
# Master function to log in and post.
def main(username,password,group_id):
versionCheck(gist,version)
session = requests.session()
sess,uid,dtsg = login(session, username, password)
htmlparser = HTMLParser()
escaped = args.post_content.decode('string_escape')
unescaped = htmlparser.unescape(escaped)
postToFacebook(
session = sess,
dtsg = dtsg,
pageID = group_id,
uID = uid,
message = unescaped,
link = args.post_url
)
print('Successfully posted to Facebook Group:\n'
'%s\n%s' % (unescaped,args.post_url))
# Function to handle Facebook login.
def login(session,username,password):
# If the last cache access is too old perform a proper login.
wasReadFromCache = False
fbcookies = '%s/%s' % (credential_path,cookie_file)
if os.path.exists(fbcookies):
last_time = os.path.getmtime(fbcookies)
current_time = time.time()
lastModification = current_time - last_time
if lastModification < maxSessionTime:
with open(fbcookies, "rb") as f:
session = pickle.load(f)
response = session.get('https://www.facebook.com')
wasReadFromCache = True
if args.debug:
print(
'DEBUG: loaded session from cache (last '
'access %ds ago)' % lastModification)
# Login if no recent session data
if not wasReadFromCache:
response = session.get('https://www.facebook.com')
# Find the unique login form input control value.
l = re.search('name=\"lsd\"\ value=\"([A-Z,a-z,0-9]+)\"', \
response.text)
lsd = l.group(1)
if args.debug:
print('DEBUG: LSD = %s' % lsd)
# Perform the login request.
response = session.post(
'https://www.facebook.com/login.php?login_attempt=1',
data={
'lsd' : lsd,
'email' : fb_username,
'pass' : fb_password,
'default_persistent' : '0',
'timezone' : '-60',
'lgndim' : '',
'lgnrnd' : '',
'lgnjs' : '',
'locale' : 'en_US',
'qsstamp' : ''
}
)
# Verify login by looking for Faccebook's login cookie.
try:
## Get user id from the Facebook login cookie.
uid = session.cookies['c_user']
# Get value of logged in user token.
d = re.search('name=\"fb_dtsg\"\ value=\"([A-Z,a-z,0-9,\-,_]'
'{12}:[A-Z,a-z,0-9,\-,_]{12})\"', response.text)
if d:
dtsg = d.group(1)
else:
raise Exception('ERROR: Failed to find fb_dtsg')
except KeyError:
raise Exception('ERROR: Login Failed!')
if args.debug:
print('DEBUG: Facebook Login ' + str(response))
# Store Facebook login cookies for later use.
with open(fbcookies, "wb") as f:
pickle.dump(session, f, protocol=2)
if args.debug:
print('DEBUG: updated session cache-file %s' % fbcookies)
return session, uid, dtsg
# Function to post to Facebook Group
def postToFacebook(
session = '',
dtsg = '',
pageID = '',
uID = '',
message = '',
link = ''):
data = {
"__user" : uID,
"fb_dtsg" : dtsg,
"message" : message,
"target" : pageID
}
if link != '':
data['linkUrl'] = link
response = session.post(
'https://m.facebook.com/a/group/post/add/?gid='+pageID+'&refid=18',
data = data,
headers = {'Content-Type':'application/x-www-form-urlencoded'}
)
if args.debug:
print('DEBUG: Facebook Post ' + str(response))
# Function to check gist for script updates.
def versionCheck(gist,version):
session = requests.session()
response = session.get(gist)
v = re.search('\<td.*class=\"blob-code\ blob-code-inner.*\>version\ '
'\<.*\>=\<.*\>\ \<.*\>(\d\d)\<\/span\>\<\/td\>', response.text
)
if v:
ver = v.group(1)
if args.debug:
print('DEBUG: gist ver = %s' % ver)
else:
ver = version
if int(ver) > int(version):
print('New version of %s available: %s' \
% (os.path.basename(__file__),gist))
else:
print('This is the latest verion of %s' % os.path.basename(__file__))
# Execute the script, output exceptions
try:
main(
username = fb_username,
password = fb_password,
group_id = fb_group_id
)
except Exception as exception:
print('ERROR: ' + str(exception))
[Facebook]
username: facebook@login.user
password: fb_passwd
group-id: 123456789101112
session-time: 86400
@seanvree
Copy link

hey @spuniun thanks for this man!

I had this working for a few weeks, but I was out of town and my server was down. When I came back I updated a lot shit on it - Windows, PLEX, Tautulli, Python ... just a bunch of shit and now I'm getting the error below.

Running Windows 10E w/ Python 2.7.14

Here is my output when I run a test, I've tested a lot of different combinations, but can't get past the NO MODULE NAMED ORDERED_DICT

C:\Tautulli\scripts\fb>python facebook.py -c "No vulnerabilities detected - Python2.7" -d
DEBUG: gist ver = 19
This is the latest verion of facebook.py
ERROR: No module named ordered_dict

Any ideas?

BTW: you have a type in "VERSION"

@seanvree
Copy link

seanvree commented Dec 1, 2018

@spuniun

Found the issue...not sure if this is a windows thing but I had to do the following to get it to work:

  • Downgrade urllib3:
python -m pip uninstall urllib3

python -m pip install urllib3==1.22
  • Add the following at line 19 in facebook.py:
from collections import OrderedDict

image

  • I had also had to remove the facebookcookies.dat file

OUTPUT:

C:\Tautulli\scripts\fb>python facebook.py -c "No vulnerabilities detected - Python2.7" -d
DEBUG: gist ver = 19
This is the latest verion of facebook.py
DEBUG: loaded session from cache (last access 49s ago)
DEBUG: Facebook Login <Response [200]>
DEBUG: updated session cache-file C:\Tautulli\scripts\fb/fbcookies.dat
DEBUG: Facebook Post <Response [200]>
Successfully posted to Facebook Group:
No vulnerabilities detected - Python2.7


C:\Tautulli\scripts\fb>

@jeff15110168
Copy link

hi there - has anyone figured out the issue with facebook locking accounts every few days because of this script running?

is there any way to avoid this locking? I unlock the account and manually change the password in the fbsettings.ini but every few days after the script runs it will again lock the account

@jandreaucodes
Copy link

This seems to fail with "Login Failed!" if you have two-factor auth enabled for your Facebook account.

Is there a workaround to make it work with TFA? The only other option we had to post to Facebook was Buffer by Email, but that got discontinued last night, so this seems to be our last hope.

@jandreaucodes
Copy link

I set up another Facebook account and added it to my group. This one doesn't have TFA enabled. It seems to work, but I get ERROR: ERROR: Failed to find fb_dtsg and can't find what that means.

The only Google result is linking back here in the actual code where it's generating that error.

@MiTvSpain
Copy link

Hola este servicio sigue funcionando?

@spuniun
Copy link
Author

spuniun commented Jul 10, 2020

Hola este servicio sigue funcionando?

Nope, Facebook killed it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment