diff --git a/tts.py b/tts.py index 53f70cb..702432b 100644 --- a/tts.py +++ b/tts.py @@ -51,17 +51,25 @@ class IRC: def __init__(self): self.irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.tts_denied = [] - self.tts_allowed = [] - self.tts_status = CONF['TTS_STARTENABLED'] - self.quickvote_status = False - self.pick_status = False - self.votemsg = False - self.poll = {} - self.pollcount = 0 - self.pickme = [] - self.picknumber = 1 - self.pickcount = 0 + self.tts = { + "status": CONF['TTS_STARTENABLED'], + "whitelist": [], + "blacklist": [] + } + self.quickvote = { + "status": False, + "message": False + } + self.poll = { + "count": 0, + "data": {} + } + self.pick = { + "status": False, + "number": 1, + "count": 0, + "pickme": [] + } if 'WHITELIST_USER' in CONF: self.tts_allowed = CONF['WHITELIST_USER'] @@ -77,6 +85,7 @@ class IRC: """ logging.info("Connecting to: %s", server) logging.info('Waiting...') + try: self.irc.connect((server, port)) except ConnectionResetError: @@ -166,51 +175,8 @@ class IRC: tags = self.get_tags(resp) message = self.get_message(resp) - user = tags['user'] - msg = message['message'] - msglen = message['length'] - - logging.debug('Msg: %s', msg) - logging.debug('Msg length: %s', msglen) - logging.debug('Deny List:') - logging.debug(self.tts_denied) - self.priviledged_commands(message, tags) - - if msg.startswith('#pickme') and self.pick_status is True: - logging.info('Pickme detected') - self.pickcount = self.pickcount + 1 - self.pickme.append(user) - logging.debug("pickme %s added", user) - return - - if msg.startswith('#') and self.quickvote_status is True: - logging.info('Quickvote: Cast detected') - self.pollcount += 1 - self.poll[user] = msg.lower() - logging.debug("poll: %s", self.poll) - return - - if msg.startswith('!tts'): - logging.info('!tts command detected') - self.Commands.tts(self, message, tags) - return - - if msg.startswith('!wiki') and CONF['FEATURE']['WIKI']: - logging.debug("!wiki command detected") - self.Commands.wiki(self, tags, msg) - return - - if CONF['FEATURE']['QUOTE']: - if msg.startswith('!addquote'): - logging.debug("!addquote command detected") - self.Commands.addquote(self, tags, msg) - return - - if msg.startswith('!smartquote') or msg.startswith('!sq'): - logging.debug("!smartquote command detected") - self.Commands.quote(self, tags, msg) - return + self.unpriviledged_commands(message, tags) def get_tags(self, resp): """ Strip tags from response @@ -256,6 +222,51 @@ class IRC: return msg + def unpriviledged_commands(self, message, tags): + """ Process unpriviledged commands + + :param dict message: Message + :param dict tags: Message metadata (tags) + """ + + msg = message['message'] + user = tags['user'] + + if msg.startswith('#pickme') and self.pick['status'] is True: + logging.info('Pickme detected') + self.pick['count'] = self.pick['count'] + 1 + self.pick['pickme'].append(user) + logging.debug("pickme %s added", user) + return + + if msg.startswith('#') and self.quickvote['status'] is True: + logging.info('Quickvote: Cast detected') + self.poll['count'] += 1 + self.poll['data'][user] = msg.lower() + logging.debug("poll: %s", self.poll) + return + + if msg.startswith('!tts'): + logging.info('!tts command detected') + self.Commands.tts(self, message, tags) + return + + if msg.startswith('!wiki') and CONF['FEATURE']['WIKI']: + logging.debug("!wiki command detected") + self.Commands.wiki(self, tags, msg) + return + + if CONF['FEATURE']['QUOTE']: + if msg.startswith('!addquote'): + logging.debug("!addquote command detected") + self.Commands.addquote(self, tags, msg) + return + + if msg.startswith('!smartquote') or msg.startswith('!sq'): + logging.debug("!smartquote command detected") + self.Commands.quote(self, tags, msg) + return + def priviledged_commands(self, message, tags): """ Process priviledged commands @@ -270,11 +281,41 @@ class IRC: if 'broadcaster' in badges or 'moderator' in badges: if msg.startswith('!ping') and CONF['FEATURE']['PING']: logging.debug("Ping check received.") - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), "Pong!") + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + "Pong!" + ) elif msg.startswith('!version') and CONF['FEATURE']['VERSION']: logging.debug("!version command detected") - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), VERSION) + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + VERSION + ) + + elif msg.startswith('!toff'): + logging.info('TTS is now turned off') + msg_queue.clear() + MSG_QUEUE_RAW.clear() + self.tts['status'] = False + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['TOFF'] + ) + + elif msg.startswith('!ton'): + logging.info('TTS is now turned on') + msg_queue.clear() + MSG_QUEUE_RAW.clear() + self.tts['status'] = True + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['TON'] + ) elif msg.startswith('!pick') and CONF['FEATURE']['PICK']: logging.debug("!pick command detected") @@ -304,20 +345,6 @@ class IRC: logging.info('!delay command detected') self.Commands.delay(self, msg) - elif msg.startswith('!toff'): - logging.info('TTS is now turned off') - msg_queue.clear() - MSG_QUEUE_RAW.clear() - self.tts_status = False - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['TOFF']) - - elif msg.startswith('!ton'): - logging.info('TTS is now turned on') - msg_queue.clear() - MSG_QUEUE_RAW.clear() - self.tts_status = True - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['TON']) - def check_subonly(self, tags): """ Check if subonly mode is enabled and sender is sub @@ -338,7 +365,11 @@ class IRC: return False logging.debug('TTS is sub-only') - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['SUBONLY']) + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['SUBONLY'] + ) return True def check_modonly(self, tags): @@ -360,7 +391,11 @@ class IRC: return False logging.debug('TTS is mod-only') - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['MODONLY']) + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['MODONLY'] + ) return True def check_tts_disabled(self, user): @@ -370,8 +405,12 @@ class IRC: :return bool: """ - if not self.tts_status: - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['DISABLED']) + if not self.tts['status']: + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['DISABLED'] + ) return True logging.debug('TTS is enabled') @@ -387,7 +426,11 @@ class IRC: """ if message['length'] > CONF['IRC_TTS_LEN']: - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['TOO_LONG']) + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['TOO_LONG'] + ) return True logging.debug('Check length: Message is ok') @@ -403,9 +446,13 @@ class IRC: :return bool: """ - if user in self.tts_denied: + if user in self.tts['denied']: logging.info("%s is not allowed to use TTS", user) - self.sendmsg(CONF['IRC_CHANNEL'], "@"+str(user), CONF['MESSAGE']['DENIED']) + self.sendmsg( + CONF['IRC_CHANNEL'], + "@"+str(user), + CONF['MESSAGE']['DENIED'] + ) return True logging.debug("%s is allowed to use TTS", user) @@ -426,7 +473,8 @@ class IRC: logging.debug("tts_allowed: %s", self.tts_allowed) self.sendmsg( CONF['IRC_CHANNEL'], - "@"+str(user), CONF['MESSAGE']['WHITELISTONLY'] + "@"+str(user), + CONF['MESSAGE']['WHITELISTONLY'] ) return False return True @@ -584,8 +632,8 @@ class IRC: } req = requests.get(url=api_endpoint, headers=headers) data = req.json() - game = data['data'][0]['game_name'] + game = data['data'][0]['game_name'] quote = f"#{nol}: \"{quote[1]}\" -{username}/{game} ({date})\n" except Exception: logging.warning('Could not get metadata for quote') @@ -595,12 +643,11 @@ class IRC: with open("quotes.txt", "ab") as file: file.write(quote.encode('utf-8')) - msg = f"{CONF['MESSAGE']['QUOTE_ADDED_PREFIX']} \ + message = f"{CONF['MESSAGE']['QUOTE_ADDED_PREFIX']} \ #{nol} {CONF['MESSAGE']['QUOTE_ADDED_SUFFIX']}" - raw_msg = { "TTS": True, - "msg": msg, + "msg": message, "badges": True, "subscriber": True, "msgid": True, @@ -610,7 +657,7 @@ class IRC: "timestamp": str(time.time_ns()) } msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] - IRC.sendmsg(self, CONF['IRC_CHANNEL'], "@"+str(user), msg) + IRC.sendmsg(self, CONF['IRC_CHANNEL'], "@"+str(user), message) def wiki(self, tags, msg): """ !wiki command @@ -630,11 +677,10 @@ class IRC: IRC.sendmsg(self, CONF['IRC_CHANNEL'], "@"+str(user), wikiresult) IRC.sendmsg(self, CONF['IRC_CHANNEL'], "@"+str(user), wikipedia.page(msg).url) - wikiresult = wikiresult.replace('==', '') - + message = wikiresult.replace('==', '') raw_msg = { "TTS": True, - "msg": wikiresult, + "msg": message, "badges": True, "subscriber": True, "msgid": True, @@ -734,11 +780,10 @@ class IRC: logging.debug("Quote: %s", quote) IRC.sendmsg(self, CONF['IRC_CHANNEL'], "", quote) - quote = quote.rsplit('(', 1)[0] - + message = quote.rsplit('(', 1)[0] raw_msg = { "TTS": True, - "msg": quote, + "msg": message, "badges": True, "subscriber": True, "msgid": True, @@ -830,10 +875,11 @@ class IRC: logging.error("There was an error during picking.") joined_picks = False + message = f"{CONF['MESSAGE']['PICKRESULT']} {joined_picks}" if joined_picks: raw_msg = { "TTS": True, - "msg": CONF['MESSAGE']['PICKRESULT'] +" "+ str(joined_picks), + "msg": message, "badges": True, "subscriber": True, "msgid": True, @@ -924,9 +970,11 @@ class IRC: logging.debug(count) + count = str(count[0][0].replace('#','')) + message = f"{CONF['MESSAGE']['VOTERESULT']} {count}" raw_msg = { "TTS": True, - "msg": CONF['MESSAGE']['VOTERESULT'] +" "+ str(count[0][0].replace('#','')), + "msg": message , "badges": True, "subscriber": True, "msgid": True, @@ -941,10 +989,11 @@ class IRC: logging.debug('Votemsg: %s', msg) for key, value in count: + message = f"{key} ({value}) {CONF['MESSAGE']['VOTES']})" IRC.sendmsg( self, CONF['IRC_CHANNEL'], "*", - str(key)+" ("+str(value)+ " "+ CONF['MESSAGE']['VOTES'] + ")" + message ) self.quickvote_status = False @@ -979,7 +1028,7 @@ class IRC: randomfile = msg.replace('!random', '').strip().lower() if randomfile: - randomfile = "random_"+str(os.path.basename(randomfile))+".txt" + randomfile = f"random_{os.path.basename(randomfile)}.txt" else: randomfile = "random.txt" @@ -1223,6 +1272,68 @@ def http_serve_forever(httpd): """ httpd loop """ httpd.serve_forever() +def send_tts_queue(): + """ Send messages to TTS """ + + for raw_msg in MSG_QUEUE_RAW: + logging.debug('Raw msg: %s', MSG_QUEUE_RAW) + + now = datetime.datetime.now() + if now - raw_msg['queuetime'] > datetime.timedelta(seconds=CONF['IRC_CLEARMSG_TIMEOUT']): + logging.debug('clearmsg_timeout reached') + if raw_msg['timestamp'] not in msg_queue: + logging.info('Sending TTS message') + msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] + logging.debug("msg_queue: %s", msg_queue) + else: + logging.debug('Msg is already in queue') + +def get_url(path=False): + """ Generate a valid URL from config values """ + + if CONF['HTTP_BIND'] == "0.0.0.0": + url = "localhost" + else: + url = CONF['HTTP_BIND'] + + url = f"http://{url}:{CONF['HTTP_PORT']}/" + + if path: + url = f"{url}{path}" + + return url + +def check_oauth_token(): + """ Check for valid authentication via Twitch API """ + + global CONF + logging.debug('Checking OAuth Token') + + try: + url = 'https://id.twitch.tv/oauth2/validate' + oauth = CONF['IRC_OAUTH_TOKEN'].replace('oauth:','') + oauth = f"OAuth {oauth}" + request = urllib.request.Request(url) + request.add_header('Authorization', oauth) + urllib.request.urlopen(request) + except HTTPError: + logging.fatal('Twitch rejected your OAuth Token. Please check and generate a new one.') + logging.info('Please open http://%s:%s/token to generate your OAuth-Token.', + CONF['HTTP_BIND'], + CONF['HTTP_PORT'] + ) + + url = get_url("token") + webbrowser.open_new_tab(url) + logging.info('Please complete the OAuth process and add the token into your "config.yml" \ + within the next 5 minutes.') + time.sleep(300) + CONF = load_config() + check_oauth_token() + + logging.info('OAuth Token is valid') + return CONF + def load_config(): """ Loading config variables """ @@ -1358,13 +1469,27 @@ def load_config(): ) CONF['FEATURE'] = {} - CONF['FEATURE']['WIKI'] = cfg.get('features', {}).get('wiki', True) - CONF['FEATURE']['PICK'] = cfg.get('features', {}).get('pick', True) - CONF['FEATURE']['VOTE'] = cfg.get('features', {}).get('vote', True) - CONF['FEATURE']['QUOTE'] = cfg.get('features', {}).get('quote', True) - CONF['FEATURE']['RANDOM'] = cfg.get('features', {}).get('random', True) - CONF['FEATURE']['VERSION'] = cfg.get('features', {}).get('version', True) - CONF['FEATURE']['PING'] = cfg.get('features', {}).get('ping', True) + CONF['FEATURE']['WIKI'] = cfg.get('features', {}).get( + 'wiki', True + ) + CONF['FEATURE']['PICK'] = cfg.get('features', {}).get( + 'pick', True + ) + CONF['FEATURE']['VOTE'] = cfg.get('features', {}).get( + 'vote', True + ) + CONF['FEATURE']['QUOTE'] = cfg.get('features', {}).get( + 'quote', True + ) + CONF['FEATURE']['RANDOM'] = cfg.get('features', {}).get( + 'random', True + ) + CONF['FEATURE']['VERSION'] = cfg.get('features', {}).get( + 'version', True + ) + CONF['FEATURE']['PING'] = cfg.get('features', {}).get( + 'ping', True + ) CONF['USERMAP'] = cfg.get('usermapping', []) @@ -1394,65 +1519,6 @@ def load_config(): return CONF -def send_tts_queue(): - """ Send messages to TTS """ - - for raw_msg in MSG_QUEUE_RAW: - logging.debug('Raw msg: %s', MSG_QUEUE_RAW) - - now = datetime.datetime.now() - if now - raw_msg['queuetime'] > datetime.timedelta(seconds=CONF['IRC_CLEARMSG_TIMEOUT']): - logging.debug('clearmsg_timeout reached') - if raw_msg['timestamp'] not in msg_queue: - logging.info('Sending TTS message') - msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] - logging.debug("msg_queue: %s", msg_queue) - else: - logging.debug('Msg is already in queue') - -def get_url(path=False): - """ Generate a valid URL from config values """ - - if CONF['HTTP_BIND'] == "0.0.0.0": - url = "localhost" - else: - url = CONF['HTTP_BIND'] - - url = "http://"+str(url)+":"+str(CONF['HTTP_PORT'])+"/" - - if path: - url = url+str(path) - - return url - -def check_oauth_token(): - """ Check for valid authentication via Twitch API """ - - global CONF - logging.debug('Checking OAuth Token') - - try: - url = 'https://id.twitch.tv/oauth2/validate' - oauth = "OAuth "+str(CONF['IRC_OAUTH_TOKEN'].replace('oauth:','')) - request = urllib.request.Request(url) - request.add_header('Authorization', oauth) - urllib.request.urlopen(request) - except HTTPError: - logging.fatal('Twitch rejected your OAuth Token. Please check and generate a new one.') - logging.info('Please open http://%s:%s/token to generate your OAuth-Token.', - CONF['HTTP_BIND'], CONF['HTTP_PORT']) - - url = get_url("token") - webbrowser.open_new_tab(url) - logging.info('Please complete the OAuth process and add the token into your "config.yml" \ - within the next 5 minutes.') - time.sleep(300) - CONF = load_config() - check_oauth_token() - - logging.info('OAuth Token is valid') - return CONF - def main(): """ Main loop """ @@ -1465,7 +1531,11 @@ def main(): sys.tracebacklimit = 5 logging.info("Starting Webserver") - httpd = ThreadingSimpleServer((CONF['HTTP_BIND'], CONF['HTTP_PORT']), HTTPserv) + httpd = ThreadingSimpleServer( + (CONF['HTTP_BIND'], + CONF['HTTP_PORT']), + HTTPserv + ) http_thread = Thread(target=http_serve_forever, daemon=True, args=(httpd, )) http_thread.start() @@ -1481,7 +1551,11 @@ def main(): CONF['IRC_USERNAME'], CONF['IRC_OAUTH_TOKEN'] ) - irc.sendmsg(CONF['IRC_CHANNEL'], 'MrDestructoid', CONF['MESSAGE']['READY']) + irc.sendmsg( + CONF['IRC_CHANNEL'], + 'MrDestructoid', + CONF['MESSAGE']['READY'] + ) logging.info('Connected and joined') url = get_url() @@ -1500,15 +1574,15 @@ def main(): CONF = load_config() lastreload = datetime.datetime.now() - if irc.quickvote_status and irc.votemsg: + if irc.quickvote['status'] and irc.quickvote['message']: logging.info('Quickvote is active') irc.sendmsg( CONF['IRC_CHANNEL'], "@chat", - CONF['MESSAGE']['VOTESTART'] + " (" + str(irc.votemsg) + ")" + f"{CONF['MESSAGE']['VOTESTART']} ({irc.quickvote['message']})" ) - if not irc.tts_status: + if not irc.tts['status']: continue logging.debug('MSG_QUEUE_RAW: %s', MSG_QUEUE_RAW)