Fork of mastaface from linos @
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
6.2 KiB

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Public Facebook-Page to Mastodon Cross-Poster
# Copyright (C) 2020 André Menrath (
# This program 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.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <>.
import json
import pickle
import subprocess
import logging
from datetime import datetime
from os import path
from facebook_scraper import get_posts
from mastodon import Mastodon
from vendor import split_tweet
# Global variables
database_filename = 'database.pkl'
config_filename = 'config.json'
mastodon_character_limit_default = 500'Execute mastaface at ' +"%Y-%m-%d, %H:%M:%S") )
try: # Open config file and verify it is valid json
with open(config_filename, 'r') as f:
logging.debug('found config: ' + config_filename)
config = json.load(f)
except Exception as e:
exit'Loaded config from ' + config_filename)
# Get history-database
# Check if database file exists
if path.exists(database_filename):'found briding-history-database at ' + database_filename)
database = pickle.load( open( database_filename, "rb" ) )
except Exception as e:
logging.error(e)'Loaded bridging-history-database')
logging.warning('No bridging-history-database found at' + database_filename)
# initialize database
database = {}
for bridge in config['bridges']:
try: # Generate Mastodon Python API Object
mastodon = Mastodon(
access_token = bridge['mastodon_access_token'],
api_base_url = bridge['mastodon_api_base_url']
# Get first=last facebook page
posts = get_posts(bridge['facebook_page'], pages=1)
# Extract very last post
post = list(posts)[0]
# Split-up facebook post into tweets according to character limit, and reserve room for links
if post['link']:
mastodon_character_limit = mastodon_character_limit_default - 30
mastodon_character_limit = mastodon_character_limit_default
toots_text = split_tweet.split_tweet(post['post_text'], mastodon_character_limit)
# Check if current bridge already exists in database
if bridge['facebook_page'] not in database:
database[bridge['facebook_page']] = {}
database[bridge['facebook_page']]['last_post_id'] = None
database[bridge['facebook_page']]['last_edited'] = None
database[bridge['facebook_page']]['mastodon_statuses'] = None
# Check if there is a new post, or the last one has been edited
if post['time'] != database[bridge['facebook_page']]['last_edited']:
if post['post_id'] == database[bridge['facebook_page']]['last_post_id']:
# Last post has been updated, so first delete the old toot
for status in database[bridge['facebook_page']]['mastodon_statuses']:
logging.debug('Deleted post ' + str(status['id']) + ' on mastodon because of facebook edit')
statuses = None
statuses = []
# Upload image if it exists
if post['image']:
url = post['image']['curl', url, '--output', 'tmp.jpg',
"-H", 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0',
"-H", 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
"-H", 'Accept-Language: de,en-US;q=0.7,en;q=0.3', "--compressed" ,
"-H", 'DNT: 1',
"-H", 'Connection: keep-alive', "-H", 'Upgrade-Insecure-Requests: 1'])
media = mastodon.media_post("tmp.jpg")
# Toot
# If post is too long, split it into main-toot and non-listed replies.
is_main_toot = True
for toot_text in toots_text:
if post['link'] and is_main_toot:
toot = toot_text + '\n\n' + post['link']
toot = toot_text
if is_main_toot:
if post['image']:
statuses.append(mastodon.status_post(toot, media_ids=media))
is_main_toot = False'Bridged post' + str(post['post_id']) + ' to ' + str(statuses[-1]['id']) )
statuses.append(mastodon.status_post(toot, in_reply_to_id = statuses[-1], visibility = 'unlisted'))
# After bridging was successfull update database of bridging history
database[bridge['facebook_page']] = {}
database[bridge['facebook_page']]['last_post_id'] = post['post_id']
database[bridge['facebook_page']]['last_edited'] = post['time']
database[bridge['facebook_page']]['mastodon_statuses'] = statuses
else:'No updates to bridge for'+ bridge['facebook_page'] )
except Exception as e:
logging.error('Error bridging' + bridge['facebook_page'])
# Save database to file
pickle.dump(database, open(database_filename, "wb" ))
logging.debug('updated bridging-history-database at' + database_filename)
except Exception as e:
logging.error('error writing bridging-history-database at' + database_filename)