|
|
@ -4,22 +4,33 @@ |
|
|
|
|
|
|
|
from mastodon import Mastodon, StreamListener |
|
|
|
from TootHTMLParser import TootHTMLParser |
|
|
|
import requests, os, random, sys, time, json, logging, argparse |
|
|
|
import requests, os, random, sys, time, json, logging, argparse, socket, multiprocessing, tempfile, fcntl |
|
|
|
|
|
|
|
from logging.handlers import RotatingFileHandler |
|
|
|
from pprint import pprint |
|
|
|
|
|
|
|
# Mastodon Stream Listener defines functions to react when something happens on Mastodon. We inherit it. |
|
|
|
class BotListener(StreamListener): |
|
|
|
def __init__(self, mastodon, log): |
|
|
|
def __init__(self, mastodon, log, hb_filename, pid_filename): |
|
|
|
log.debug("initialisation step") |
|
|
|
#StreamListener.__init__(self) |
|
|
|
self.mastodon = mastodon |
|
|
|
self.log = log |
|
|
|
self.hb_filename = hb_filename |
|
|
|
self.pid_filename = pid_filename |
|
|
|
self.pid_file = open(self.pid_filename, 'w') |
|
|
|
fcntl.lockf(self.pid_file, fcntl.LOCK_EX) |
|
|
|
print("%s" % os.getpid(), file=self.pid_file) |
|
|
|
fcntl.lockf(self.pid_file, fcntl.LOCK_UN) |
|
|
|
self.pid_file.close() |
|
|
|
self.heartbeat_file = open(self.hb_filename, 'w') |
|
|
|
fcntl.lockf(self.heartbeat_file, fcntl.LOCK_EX) |
|
|
|
print(time.time(), file=self.heartbeat_file) |
|
|
|
fcntl.lockf(self.heartbeat_file, fcntl.LOCK_UN) |
|
|
|
self.heartbeat_file.close() |
|
|
|
super() |
|
|
|
#pprint(vars(mastodon.stream_user(StreamListener))) |
|
|
|
|
|
|
|
def on_notification(self, notification): |
|
|
|
def on_notification(self): |
|
|
|
log.debug("Got a notification!") |
|
|
|
#for thread in self.threads: |
|
|
|
# if(not thread.is_alive()): |
|
|
@ -47,8 +58,13 @@ class BotListener(StreamListener): |
|
|
|
#If the toot is not in answer to a drawing |
|
|
|
answerTo = status["in_reply_to_id"] |
|
|
|
|
|
|
|
def on_update(self, status): |
|
|
|
log.debug("update ! ") |
|
|
|
def handle_heartbeat(self): |
|
|
|
log.debug("HEARTBEAT") # Sent every 15 seconds by Mastodon |
|
|
|
self.heartbeat_file = open(self.hb_filename, 'w') |
|
|
|
fcntl.lockf(self.heartbeat_file, fcntl.LOCK_EX) |
|
|
|
print(time.time(), file=self.heartbeat_file) |
|
|
|
fcntl.lockf(self.heartbeat_file, fcntl.LOCK_UN) |
|
|
|
self.heartbeat_file.close() |
|
|
|
|
|
|
|
def get_parameter( parameter, file_path ): |
|
|
|
# Check if secrets file exists |
|
|
@ -136,7 +152,79 @@ def main(): |
|
|
|
if args.command == 'img': |
|
|
|
post_img(mastodon, "NSFW", 1, log, config_file) |
|
|
|
elif args.command == 'stream': |
|
|
|
run(mastodon, log) |
|
|
|
name_bot = get_parameter("name_bot", config) |
|
|
|
while True: |
|
|
|
try: |
|
|
|
tmp_hb_filename = tempfile.mkstemp(prefix=HOME + name_bot, suffix='.heartbeat') |
|
|
|
tmp_pid_filename = tempfile.mkstemp(prefix=HOME + name_bot+, suffix='.pid') |
|
|
|
log.debug("Creating a new process") |
|
|
|
proc = multiprocessing.Process(target=driver, args=(log, tmp_hb_filename[1],tmp_pid_filename[1])) |
|
|
|
proc.start() |
|
|
|
while True: |
|
|
|
time.sleep(TIMER_DELAY) |
|
|
|
if proc.is_alive(): |
|
|
|
h = open(tmp_hb_filename[1], 'r') |
|
|
|
fcntl.lockf(h, fcntl.LOCK_SH) |
|
|
|
data = h.read(128) |
|
|
|
fcntl.lockf(h, fcntl.LOCK_UN) |
|
|
|
h.close() |
|
|
|
try: |
|
|
|
last_heartbeat = float(data) |
|
|
|
except ValueError: # TODO there is a bug somewhere |
|
|
|
# since we sometimes get empty |
|
|
|
# heartbeat files. That's strange |
|
|
|
# since it is supposed to be filled |
|
|
|
# in when the listener starts, even |
|
|
|
# before the first heartbeat is |
|
|
|
# received. May be establishing the |
|
|
|
# session failed, triggering an |
|
|
|
# exception, and leaving the file |
|
|
|
# unfilled? |
|
|
|
log.error("Invalid value in heartbeat file %s: \"%s\"" % (tmp_hb_filename[1], data)) |
|
|
|
last_heartbeat = 0 |
|
|
|
if time.time() - last_heartbeat > MAXIMUM_HEARTBEAT_DELAY: |
|
|
|
log.error("No more heartbeats, kill %s" % proc.pid) |
|
|
|
proc.terminate() |
|
|
|
proc.join() |
|
|
|
log.debug("Done, it exited with code %s" % proc.exitcode) |
|
|
|
try: |
|
|
|
os.remove(tmp_hb_filename[1]) |
|
|
|
except: |
|
|
|
pass |
|
|
|
try: |
|
|
|
os.remove(tmp_pid_filename[1]) |
|
|
|
except: |
|
|
|
pass |
|
|
|
break |
|
|
|
else: |
|
|
|
log.error("Bot died, we restart it") |
|
|
|
time.sleep(RESTART_DELAY) |
|
|
|
try: |
|
|
|
os.remove(tmp_hb_filename[1]) |
|
|
|
except: |
|
|
|
pass |
|
|
|
try: |
|
|
|
os.remove(tmp_pid_filename[1]) |
|
|
|
except |
|
|
|
pass |
|
|
|
break |
|
|
|
except Exception as error: |
|
|
|
log.critical("Unexpected error in the timer: \"%s\"" % error) |
|
|
|
try: # Try to clean |
|
|
|
proc.terminate() |
|
|
|
proc.join() |
|
|
|
try: |
|
|
|
os.remove(tmp_hb_filename[1]) |
|
|
|
except: |
|
|
|
pass |
|
|
|
try: |
|
|
|
os.remove(tmp_pid_filename[1]) |
|
|
|
except: |
|
|
|
pass |
|
|
|
except: |
|
|
|
pass |
|
|
|
time.sleep(RESTART_DELAY) |
|
|
|
run(mastodon, log) |
|
|
|
|
|
|
|
main() |
|
|
|
|