diff --git a/.gitignore b/.gitignore index 25acfa2..0130b99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ config.yml build +dist tts.spec -tts.exe random*.txt quotes.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d31e5..5a389b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. If there is a `Changed` section please read carefully, as this often means that you will need to adapt your `config.yml`, otherwise the bot might fail to start. +## [1.3.1] - 2022-08-19 + +### Added 1.3.1 + +* Added `game_name` and date to quote + +### Fixed (hopefully) 1.3.1 + +* Improved HTTP request handling (hopefully removes delay in Chrome) + ## [1.3.0] - 2022-08-18 ### Added 1.3.0 diff --git a/README.md b/README.md index 5a717ff..3e068a8 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,13 @@ By using Javascript for the actual TTS part it's not only very easy to access th 1. Clone the repo, or download and unzip the newest [Release](https://gitlab.com/gpvkt/twitchtts/-/releases) 2. Rename/copy `config-dist.yml` to `config.yml` -3. Move/copy `./dist/tts.exe` into the main directory (you can omit this step if you have Python installed) +3. Adapt `config.yml` to your needs. See `Configuration` for details. ### Configuration -Adapt `config.yml` to your needs. Please use `UTF-8` as encoding. Example: +Please use `UTF-8` as encoding, when editing `config.yml`. + +Example: ``` lang=yaml irc: diff --git a/dist/tts.exe b/tts.exe similarity index 87% rename from dist/tts.exe rename to tts.exe index 6297bea..7746b94 100644 Binary files a/dist/tts.exe and b/tts.exe differ diff --git a/tts.py b/tts.py index a5aac67..f7d9662 100644 --- a/tts.py +++ b/tts.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# pylint: disable=line-too-long +# pylint: disable=line-too-long,too-many-lines """ TwitchTTS @@ -24,12 +24,12 @@ import os import sys import time import json +import signal import socket import random import logging import datetime import webbrowser -import socketserver import urllib.parse import urllib.request @@ -37,9 +37,11 @@ from threading import Thread from collections import Counter from urllib.parse import parse_qs from urllib.error import HTTPError -from http.server import BaseHTTPRequestHandler +from http.server import HTTPServer, BaseHTTPRequestHandler +from socketserver import ThreadingMixIn import yaml +import requests from fuzzywuzzy import process @@ -421,7 +423,7 @@ class IRC: def addquote(self, tags, msg): """ !addquote command - + Adds a newline to quotes.txt """ @@ -433,25 +435,53 @@ class IRC: logging.info('TTS is sub-only') else: try: - with open("quotes.txt", "rb") as fp: - nol = len(fp.readlines()) - fp.close() + with open("quotes.txt", "rb") as file: + nol = len(file.readlines()) + file.close() except FileNotFoundError: - logging.warn("quotes.txt does not exists, will create") + logging.warning("quotes.txt does not exists, will create") nol = 0 nol = nol + 1 quote = msg.replace("!addquote ", "").strip() quote = quote.split(" ",1) username = quote[0] - - quote = '#%s: "%s" -%s' % (nol, quote[1], username) + + date = time.strftime("%d.%m.%Y") + + try: + token = conf['IRC_OAUTH_TOKEN'].replace('oauth:','') + login = conf['IRC_CHANNEL'].replace('#','') + api_endpoint = "https://api.twitch.tv/helix/users?login="+str(login) + headers = { + 'Content-type': 'application/x-form-urlencoded', + 'Authorization': 'Bearer '+token, + 'Client-Id': 'ebo548vs6tq54c9zlrgin2yfzzlrrs' + } + req = requests.get(url=api_endpoint, headers=headers) + data = req.json() + user_id = data['data'][0]['id'] + + api_endpoint = "https://api.twitch.tv/helix/channels?broadcaster_id="+str(user_id) + headers = { + 'Content-type': 'application/x-form-urlencoded', + 'Authorization': 'Bearer '+token, + 'Client-Id': 'ebo548vs6tq54c9zlrgin2yfzzlrrs' + } + req = requests.get(url=api_endpoint, headers=headers) + data = req.json() + game = data['data'][0]['game_name'] + + quote = f"#{nol}: \"{quote[1]}\" -{username}/{game} ({date})\n" + except: # pylint: disable=bare-except + logging.warning('Could not get metadata for quote') + quote = f"#{nol}: \"{quote[1]}\" -{username} ({date})\n" + logging.info('Adding quote %s', quote) + with open("quotes.txt", "ab") as file: + file.write(quote.encode('utf-8')) - with open("quotes.txt", "ab") as fp: - fp.write(quote.encode('utf-8')) - - msg = "%s #%s %s" % (conf['MESSAGE']['QUOTE_ADDED_PREFIX'], nol, conf['MESSAGE']['QUOTE_ADDED_SUFFIX']) + msg = f"{conf['MESSAGE']['QUOTE_ADDED_PREFIX']} #{nol} {conf['MESSAGE']['QUOTE_ADDED_SUFFIX']}" raw_msg = { "TTS": True, @@ -483,38 +513,42 @@ class IRC: try: if query.isdigit(): logging.info('Fetching quote #%s', query) - - fp = open("quotes.txt", "rb") - quotes = fp.readlines() + + file = open("quotes.txt", "rb") + quotes = file.readlines() for line in quotes: if line.decode('utf-8').startswith("#"+str(query)+":"): quote = line break - fp.close() + file.close() elif query != "": logging.info('Fetching match for %s', query) - fp = open("quotes.txt", "rb") - quotes = fp.readlines() + file = open("quotes.txt", "rb") + quotes = file.readlines() matches = process.extract(query, quotes, limit=20) quotes = [] for match, score in matches: if score >= 60: quotes.append(match) - - quote = random.choice(quotes) + + logging.debug('Quotes: %s', quotes) + if len(quotes) >= 1: + quote = random.choice(quotes) else: logging.info('Fetching random quote') - + with open("quotes.txt", "rb") as file: lines = file.read().splitlines() quote = random.choice(lines) except FileNotFoundError: logging.error('"quotes.txt does not exists.') + except IndexError: + logging.error('Error fetching quote.') if not 'quote' in vars(): logging.info('No quote found.') @@ -522,9 +556,7 @@ class IRC: IRC.sendmsg(self, conf['IRC_CHANNEL'], "", quote) return False - if isinstance(quote, str): - quote = quote - else: + if not isinstance(quote, str): quote = quote.decode('utf-8') if IRC.check_tts_disabled(self, user): @@ -733,6 +765,9 @@ class IRC: return +class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): + """ Threaded HTTP Server """ + class HTTPserv(BaseHTTPRequestHandler): """Simple HTTP Server""" @@ -1015,9 +1050,7 @@ def main(): sys.tracebacklimit = 5 logging.info("Starting Webserver") - httpd = socketserver.TCPServer((conf['HTTP_BIND'], conf['HTTP_PORT']), HTTPserv) - httpd.allow_reuse_port = True - httpd.allow_reuse_address = True + httpd = ThreadingSimpleServer((conf['HTTP_BIND'], conf['HTTP_PORT']), HTTPserv) http_thread = Thread(target=http_serve_forever, daemon=True, args=(httpd, )) http_thread.start() @@ -1057,8 +1090,9 @@ def main(): send_tts_queue() except KeyboardInterrupt: + httpd.shutdown() logging.info('Exiting...') - sys.exit() + os.kill(os.getpid(), signal.SIGTERM) if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(module)s %(threadName)s %(levelname)s: %(message)s') @@ -1072,7 +1106,7 @@ if __name__ == "__main__": if sys.argv[1:]: if sys.argv[1] == "--version": print('Simple TTS Bot') - print('Version 1.3.0') + print('Version 1.3.1') sys.exit(1) main()