#!/var/www/html/webhooks/.venv/bin/python
# File name: Hooks.py
# Date created: 09/16/2022
# Date last modified: 10/15/2022
# Python Version: 3.9.2
# Copyright © 2022 DeadTOm
# Description: Webhooks for Owncast to send notifications to Discord, Mastodon and Twitter, and to interact with
# my Minecraft server
# TODO: Make routes for various chat and video links
try:
from config import *
from auth import *
from flask import Flask, jsonify, current_app, request
import requests
import logging
import time
from twython import Twython, TwythonError
import socket
from mcstatus import JavaServer
except Exception as import_error: # Log any errors loading modules, and try to keep running
fail_log = open(log_location, 'a')
fail_log.write(f'------{import_error}------\n')
fail_log.close()
logging.basicConfig(filename='/var/www/html/webhooks.log', level=logging.INFO)
#logging.basicConfig(filename='/var/www/html/webhooks.log', level=logging.DEBUG)
#logging.basicConfig(level=logging.DEBUG)
testing = 0 # Are we testing? 1 for testing. 0 for live.
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
app = Flask(__name__)
def get_now(): # This creates and returns a time stamp
now = str(time.strftime("%Y/%m/%d %H:%M:%S"))
return now
logging.info(f'\n\n\n\n{get_now()} - Webhook called.\n')
def mc_chat(mc_msg): # Send chat message to Minecraft chat
logging.info(f'{get_now()} - Checking Minecraft server for players.')
mc_server = JavaServer.lookup('mc.deadtom.me:25565')
query = mc_server.query() # Query Minecraft server for players
cur_players = query.players.names
if '_DeadTOm_' in cur_players: # If I'm on the server, send message
logging.info(f'{get_now()} - DeadTOm is on the server, sending message.')
logging.info(f'{get_now()} - Connecting...')
sock_host = 'mc.deadtom.me'
sock_port = 6791
mySocket = socket.socket()
mySocket.connect((sock_host, sock_port))
time.sleep(1)
logging.info(f'{get_now()} - Connected. Sending {mc_msg}...')
mySocket.send(mc_msg.encode())
logging.info(f'{get_now()} - sent.')
mySocket.close()
else: # If I'm not on the server, don't send the message
logging.info(f'{get_now()} - DeadTOm is not on the server, so not sending message.')
def set_hashtags(title): # Sets up hash tags to be appended to social media
logging.info(f'{get_now()} - Examining stream title \"{title}\" to apply hashtags.')
check_title = title.lower()
default_tags = '#Owncast '
tags = ''
# tag_dict located in config.py
for tag in tag_dict.keys(): # Iterate through each dict entry, and check for hashtag triggers
if tag in check_title:
print(f'Found {tag}, adding {tag_dict[tag]} to hashtags.')
tags = f'{tags}{tag_dict[tag]} '
tags = f'{tags}#Owncast #NSFW' # Adding NSFW tag, just for good measure
logging.info(f'{get_now()} - Adding {tags} to title.')
return tags
def social_post(msg, discmsg): # Post to Mastodon
# Send to Mastodon
logging.info(f'{get_now()} - Posting to Mastodon.')
response = requests.post(m_api_url,
params={'status': msg},
headers={'Authorization': m_api_key,
'visibility': 'public',
'Accept': 'application/json'})
json_response = response.json()
for line in json_response:
logging.debug(f'{get_now()} - API returned: {line}: {json_response[line]}')
# Send to Twitter
twitter.update_status(status=msg) # Tweet the message
logging.info(f'{get_now()} - Posted to Twitter.')
# Send to Discord
# for all params, see https://discordapp.com/developers/docs/resources/webhook#execute-webhook
data = {
'content': discmsg
}
talkback = requests.post(discordwebhookurl, json=data)
if 200 <= talkback.status_code < 300:
logging.info(f'{get_now()} - Discord message sent {talkback.status_code}')
else:
logging.info(
f'{get_now()} - Discord message not sent with {talkback.status_code}, response:\n{talkback.json()}')
@app.route('/stream_started/', methods=["POST"])
def start():
logging.info(f'{get_now()} - stream_started request')
raw_data = request.get_json(force=True) # Get the raw data
event_data = raw_data['eventData']
stream_title = event_data['streamTitle']
hashtags = set_hashtags(stream_title.lower())
msg = f'I\'m streaming on Owncast, at https://owncast.deadtom.me. {stream_title} {hashtags}'
logging.debug(f'{get_now()} - Constructed Mastodon/Twitter message: {msg}')
discmsg = f'DeadTOm is streaming on Owncast, at https://owncast.deadtom.me. {stream_title}'
if testing != 1: # If we're testing, don't actually send out notification
social_post(msg, discmsg)
else:
logging.info(f'-----------------------\n\n'
f'{get_now()} - Currently running in test mode. We are streaming, but no notifications are being sent to social media.'
f'\n\n---------------------------------')
return jsonify({"Data": raw_data,})
@app.route('/stream_stopped/', methods=["POST"])
def stop():
logging.info(f'{get_now()} - stream_stopped request')
raw_data = request.get_json(force=True) # Get the raw data
event_data = raw_data['eventData']
stream_title = event_data['streamTitle']
hashtags = set_hashtags(stream_title.lower()) # Make title lower case, for comparison with list of tags
msg = f'All done streaming, for now. Maybe catch me next time on Owncast, at https://owncast.deadtom.me.\n\n{hashtags}'
discmsg = f'DeadTOm is done streaming.'
if testing != 1: # If we're testing, don't actually send out notification
social_post(msg, discmsg)
return jsonify({"Data": raw_data,})
@app.route('/user_joined/', methods=["POST"])
def joined():
logging.info(f'{get_now()} - user_joined request')
raw_data = request.get_json(force=True) # Get the raw data
event_data = raw_data['eventData']
chatter_name = event_data['user']['displayName']
ownchat_msg = f'{chatter_name} joined the chat.'
logging.info(f'{get_now()} - {ownchat_msg}')
mc_chat(ownchat_msg)
logging.info(f'{get_now()} - Sent to Minecraft server.')
return jsonify({"Data": raw_data,})
@app.route('/name_changed/', methods=["POST"])
def changed():
logging.info(f'{get_now()} - name_changed request')
raw_data = request.get_json(force=True) # Get the raw data
event_data = raw_data['eventData']
chatter_old_name = event_data['user']['previousNames']
chatter_new_name = event_data['user']['displayName']
last_name = len(chatter_old_name) - 1 # Get last name in previousNames list
chatter_old_name = event_data['user']['previousNames'][last_name]
ownchat_msg = f'{chatter_old_name} changed their name to {chatter_new_name}'
logging.debug(f'{get_now()} - {type}\n{raw_data}')
logging.info(f'{get_now()} - {ownchat_msg}')
mc_chat(ownchat_msg)
logging.info(f'{get_now()} - Sent to Minecraft server.')
return jsonify({"Data": raw_data,})
@app.route('/message_sent/', methods=["POST"])
def sent():
logging.info(f'{get_now()} - message_sent request')
raw_data = request.get_json(force=True) # Get the raw data
event_data = raw_data['eventData']
chatter_name = event_data['user']['displayName']
chat_msg = event_data['rawBody']
ownchat_msg = f'{chatter_name} on Owncast says: {chat_msg}'
logging.info(f'{get_now()} - Chat message: \"{ownchat_msg}\".')
mc_chat(ownchat_msg)
logging.info(f'{get_now()} - Sent to Minecraft server.')
return jsonify({"Data": raw_data,})
if __name__ == '__main__':
try:
app.run()
except Exception as main_error:
logging.info(f'{get_now()} - {main_error}')