hooks.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/var/www/html/webhooks/.venv/bin/python
  2. # File name: Hooks.py
  3. # Date created: 09/16/2022
  4. # Date last modified: 10/15/2022
  5. # Python Version: 3.9.2
  6. # Copyright © 2022 DeadTOm
  7. # TODO: Make routes for various chat and video links
  8. try:
  9. from names import t_name, m_name
  10. from config import *
  11. from auth import *
  12. from flask import Flask, jsonify, current_app, request
  13. import requests
  14. import logging
  15. import time
  16. from twython import Twython, TwythonError
  17. import socket
  18. from mcstatus import JavaServer
  19. except Exception as import_error: # Log any errors loading modules, and try to keep running
  20. fail_log = open('/var/www/html/webhooks.log', 'a')
  21. fail_log.write(f'------{import_error}------\n')
  22. fail_log.close()
  23. logging.basicConfig(filename='/var/www/html/webhooks.log', level=logging.INFO)
  24. current_names = [] # Initialize empty list to hold current Twitter and Mastodon names
  25. testing = 0 # Are we testing? 1 for testing. 0 for live.
  26. twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
  27. get_t_info = twitter.verify_credentials() # Get current Twitter name
  28. get_m_info = requests.get('https://fosstodon.org/api/v1/accounts/verify_credentials', headers={'Authorization': m_api_key, 'Accept': 'application/json'}) # Get Mastodon account information
  29. get_m_info = get_m_info.json() # Jsonify the response
  30. app = Flask(__name__)
  31. def get_now(): # This creates and returns a time stamp
  32. now = str(time.strftime("%Y/%m/%d %H:%M:%S"))
  33. return now
  34. logging.info(f'\n\n\n\n{get_now()} - Webhook called.\n')
  35. def streaming_t_name(): # Change Twitter name to show we're streaming'
  36. get_t_info = twitter.verify_credentials() # Get current Twitter name
  37. current_t_name = f't_name = \'{get_t_info["name"]}\'' # Create Twitter list entry
  38. current_names.append(current_t_name) # Append to list of current names
  39. logging.info(f'{get_now()} - Current Twitter name is \"{get_t_info["name"]}\".')
  40. set_t_name = twitter.update_profile(name=t_streaming_name)
  41. logging.info(f'{get_now()} - Twitter name is now {set_t_name["name"]}.')
  42. def streaming_m_name(): # Change Mastodon name to show we're streaming
  43. get_m_info = requests.get('https://fosstodon.org/api/v1/accounts/verify_credentials', headers={'Authorization': m_api_key, 'Accept': 'application/json'}) # Get Mastodon account information
  44. get_m_info = get_m_info.json() # Jsonify the response
  45. current_m_name = f'm_name = \'{get_m_info["display_name"]}\'' # Create Mastodon list entry
  46. current_names.append(current_m_name) # Append to list of current names
  47. logging.info(f'{get_now()} - Current Mastodon name is \"{get_m_info["display_name"]}\".')
  48. set_m_name = requests.patch('https://fosstodon.org/api/v1/accounts/update_credentials', params={'display_name': m_streaming_name}, headers={'Authorization': m_api_key, 'Accept': 'application/json'}) # Set Mastodon display name
  49. set_m_name = set_m_name.json() # Jsonify the response
  50. logging.info(f'{get_now()} - Mastodon name is now \"{set_m_name["display_name"]}\".')
  51. def reg_t_name(): # Change Twitter name to regular name
  52. set_t_name = twitter.update_profile(name=t_name)
  53. logging.info(f'{get_now()} - Twitter name is now {set_t_name["name"]}.')
  54. def reg_m_name(): # Change Mastodon name to regular name
  55. set_m_name = requests.patch('https://fosstodon.org/api/v1/accounts/update_credentials', params={'display_name': m_name}, headers={'Authorization': m_api_key, 'Accept': 'application/json'}) # Set Mastodon display name
  56. set_m_name = set_m_name.json() # Jsonify the response
  57. logging.info(f'{get_now()} - Mastodon name is now \"{set_m_name["display_name"]}\".')
  58. def write_current_names(): # Write current names to names.py
  59. logging.info(f'{get_now()} - Storing {current_names}.')
  60. try:
  61. file = open("/var/www/html/webhooks/names.py", "w")
  62. for name in current_names:
  63. file.write(f'{name}\n')
  64. file.close()
  65. except Exception as write_error:
  66. logging.info(f'{get_now()} - {write_error}')
  67. def mc_chat(mc_msg): # Send chat message to Minecraft chat
  68. logging.info(f'{get_now()} - Checking Minecraft server for players.')
  69. mc_server = JavaServer.lookup('mc.deadtom.me:25565')
  70. query = mc_server.query() # Query Minecraft server for players
  71. cur_players = query.players.names
  72. if '_DeadTOm_' in cur_players: # If I'm on the server, send message
  73. logging.info(f'{get_now()} - DeadTOm is on the server, sending message.')
  74. logging.info(f'{get_now()} - Connecting...')
  75. sock_host = 'mc.deadtom.me'
  76. sock_port = 6791
  77. mySocket = socket.socket()
  78. mySocket.connect((sock_host, sock_port))
  79. time.sleep(1)
  80. logging.info(f'{get_now()} - Connected. Sending {mc_msg}...')
  81. mySocket.send(mc_msg.encode())
  82. logging.info(f'{get_now()} - sent.')
  83. mySocket.close()
  84. else: # If I'm not on the server, don't send the message
  85. logging.info(f'{get_now()} - DeadTOm is not on the server, so not sending message.')
  86. def set_hashtags(title): # Sets up hash tags to be appended to social media
  87. logging.info(f'{get_now()} - Examining stream title \"{title}\" to apply hashtags.')
  88. check_title = title.lower()
  89. default_tags = '#Owncast '
  90. tags = ''
  91. # tag_dict located in config.py
  92. for tag in tag_dict.keys(): # Iterate through each dict entry, and check for hashtag triggers
  93. if tag in check_title:
  94. print(f'Found {tag}, adding {tag_dict[tag]} to hashtags.')
  95. tags = f'{tags}{tag_dict[tag]} '
  96. tags = f'{tags}#Owncast #NSFW' # Adding NSFW tag, just for good measure
  97. logging.info(f'{get_now()} - Adding {tags} to title.')
  98. return tags
  99. def social_post(msg, discmsg): # Post to Mastodon
  100. # Send to Mastodon
  101. logging.info(f'{get_now()} - Posting to Mastodon.')
  102. response = requests.post(m_api_url,
  103. params={'status': msg},
  104. headers={'Authorization': m_api_key,
  105. 'visibility': 'public',
  106. 'Accept': 'application/json'})
  107. json_response = response.json()
  108. for line in json_response:
  109. logging.debug(f'{get_now()} - API returned: {line}: {json_response[line]}')
  110. # Send to Twitter
  111. twitter.update_status(status=msg) # Tweet the message
  112. logging.info(f'{get_now()} - Posted to Twitter.')
  113. # Send to Discord
  114. # for all params, see https://discordapp.com/developers/docs/resources/webhook#execute-webhook
  115. data = {
  116. 'content': discmsg
  117. }
  118. talkback = requests.post(discordwebhookurl, json=data)
  119. if 200 <= talkback.status_code < 300:
  120. logging.info(f'{get_now()} - Discord message sent {talkback.status_code}')
  121. else:
  122. logging.info(
  123. f'{get_now()} - Discord message not sent with {talkback.status_code}, response:\n{talkback.json()}')
  124. @app.route('/stream_started/', methods=["POST"])
  125. def start():
  126. logging.info(f'{get_now()} - stream_started request')
  127. raw_data = request.get_json(force=True) # Get the raw data
  128. event_data = raw_data['eventData']
  129. stream_title = event_data['streamTitle']
  130. hashtags = set_hashtags(stream_title.lower())
  131. msg = f'I\'m streaming on Owncast, at https://owncast.deadtom.me. {stream_title} {hashtags}'
  132. logging.debug(f'{get_now()} - Constructed Mastodon/Twitter message: {msg}')
  133. discmsg = f'DeadTOm is streaming on Owncast, at https://owncast.deadtom.me. {stream_title}'
  134. if testing != 1: # If we're testing, don't actually send out notification
  135. streaming_m_name()
  136. streaming_t_name()
  137. write_current_names()
  138. social_post(msg, discmsg)
  139. else:
  140. logging.info(f'-----------------------\n\n'
  141. f'{get_now()} - Currently running in test mode. We are streaming, but no notifications are being sent to social media.'
  142. f'\n\n---------------------------------')
  143. return jsonify({"Data": raw_data,})
  144. @app.route('/stream_stopped/', methods=["POST"])
  145. def stop():
  146. logging.info(f'{get_now()} - stream_stopped request')
  147. raw_data = request.get_json(force=True) # Get the raw data
  148. event_data = raw_data['eventData']
  149. stream_title = event_data['streamTitle']
  150. hashtags = set_hashtags(stream_title.lower()) # Make title lower case, for comparison with list of tags
  151. msg = f'All done streaming, for now. Maybe catch me next time on Owncast, at https://owncast.deadtom.me.\n\n{hashtags}'
  152. discmsg = f'DeadTOm is done streaming.'
  153. if testing != 1: # If we're testing, don't actually send out notification
  154. reg_m_name()
  155. reg_t_name()
  156. social_post(msg, discmsg)
  157. return jsonify({"Data": raw_data,})
  158. @app.route('/user_joined/', methods=["POST"])
  159. def joined():
  160. logging.info(f'{get_now()} - user_joined request')
  161. raw_data = request.get_json(force=True) # Get the raw data
  162. event_data = raw_data['eventData']
  163. chatter_name = event_data['user']['displayName']
  164. ownchat_msg = f'{chatter_name} joined the chat.'
  165. logging.info(f'{get_now()} - {ownchat_msg}')
  166. mc_chat(ownchat_msg)
  167. logging.info(f'{get_now()} - Sent to Minecraft server.')
  168. return jsonify({"Data": raw_data,})
  169. @app.route('/name_changed/', methods=["POST"])
  170. def changed():
  171. logging.info(f'{get_now()} - name_changed request')
  172. raw_data = request.get_json(force=True) # Get the raw data
  173. event_data = raw_data['eventData']
  174. chatter_old_name = event_data['user']['previousNames']
  175. chatter_new_name = event_data['user']['displayName']
  176. last_name = len(chatter_old_name) - 1 # Get last name in previousNames list
  177. chatter_old_name = event_data['user']['previousNames'][last_name]
  178. ownchat_msg = f'{chatter_old_name} changed their name to {chatter_new_name}'
  179. logging.debug(f'{get_now()} - {type}\n{raw_data}')
  180. logging.info(f'{get_now()} - {ownchat_msg}')
  181. mc_chat(ownchat_msg)
  182. logging.info(f'{get_now()} - Sent to Minecraft server.')
  183. return jsonify({"Data": raw_data,})
  184. @app.route('/message_sent/', methods=["POST"])
  185. def sent():
  186. logging.info(f'{get_now()} - message_sent request')
  187. raw_data = request.get_json(force=True) # Get the raw data
  188. event_data = raw_data['eventData']
  189. chatter_name = event_data['user']['displayName']
  190. chat_msg = event_data['rawBody']
  191. ownchat_msg = f'{chatter_name} on Owncast says: {chat_msg}'
  192. logging.info(f'{get_now()} - Chat message: \"{ownchat_msg}\".')
  193. mc_chat(ownchat_msg)
  194. logging.info(f'{get_now()} - Sent to Minecraft server.')
  195. return jsonify({"Data": raw_data,})
  196. if __name__ == '__main__':
  197. try:
  198. app.run()
  199. except Exception as main_error:
  200. logging.info(f'{get_now()} - {main_error}')