Compare commits

...

3 Commits

Author SHA1 Message Date
dtookey
f02bb4d3ee Merge remote-tracking branch 'origin/tookeymeddling' into tookeymeddling
# Conflicts:
#	config.py
#	requirements.txt
#	tests/test_twitch_script_class.py
#	tts.py
#	twitch_script_class.py
2020-09-20 13:36:18 -04:00
dtookey
17e1a092be updated tts.py
support both gtts and aws polly (we borrow from the streamlabs api)
moved some configuration stuff around
added one test method for creating file names, but it doesn't have any assertions, so it's useless
added enums for different configuration properties like tts engine, file naming, and poly voices
2020-09-20 13:35:53 -04:00
dtookey
3e24942d54 Let PEP8 do it's thing and reformat all the files.
Removed bots.py and slurs.py and moved them into config.py. twitch_script_class.py has been updated to handle this
added a test harness for twitch_script_class.py
contains_url and contains_slur are both validated
refactored contains_url to use a simple(ish) regex instead of an if cascade

Let PEP8 do it's thing and reformat all the files.
Removed bots.py and slurs.py and moved them into config.py. twitch_script_class.py has been updated to handle this
added a test harness for twitch_script_class.py
contains_url and contains_slur are both validated
refactored contains_url to use a simple(ish) regex instead of an if cascade
2020-09-20 11:48:15 -04:00
6 changed files with 179 additions and 39 deletions

View File

@ -1,3 +1,83 @@
from enum import Enum
class Speaker(Enum):
GOOGLE_TEXT_TO_SPEECH = 1
STREAMLABS_API = 2
class FileNameStrategy(Enum):
TIME_BASED = 1
CONTENT_BASED = 2
class PollyVoices(Enum):
Aditi = "Aditi"
Amy = "Amy"
Astrid = "Astrid"
Bianca = "Bianca"
Brian = "Brian"
Camila = "Camila"
Carla = "Carla"
Carmen = "Carmen"
Celine = "Celine"
Chantal = "Chantal"
Conchita = "Conchita"
Cristiano = "Cristiano"
Dora = "Dora"
Emma = "Emma"
Enrique = "Enrique"
Ewa = "Ewa"
Filiz = "Filiz"
Geraint = "Geraint"
Giorgio = "Giorgio"
Gwyneth = "Gwyneth"
Hans = "Hans"
Ines = "Ines"
Ivy = "Ivy"
Jacek = "Jacek"
Jan = "Jan"
Joanna = "Joanna"
Joey = "Joey"
Justin = "Justin"
Karl = "Karl"
Kendra = "Kendra"
Kimberly = "Kimberly"
Lea = "Lea"
Liv = "Liv"
Lotte = "Lotte"
Lucia = "Lucia"
Lupe = "Lupe"
Mads = "Mads"
Maja = "Maja"
Marlene = "Marlene"
Mathieu = "Mathieu"
Matthew = "Matthew"
Maxim = "Maxim"
Mia = "Mia"
Miguel = "Miguel"
Mizuki = "Mizuki"
Naja = "Naja"
Nicole = "Nicole"
Penelope = "Penelope"
Raveena = "Raveena"
Ricardo = "Ricardo"
Ruben = "Ruben"
Russell = "Russell"
Salli = "Salli"
Seoyeon = "Seoyeon"
Takumi = "Takumi"
Tatyana = "Tatyana"
Vicki = "Vicki"
Vitoria = "Vitoria"
Zeina = "Zeina"
Zhiyu = "Zhiyu"
botList = ("Nightbot", "StreamElements", "Moobot", "praxis_bot") botList = ("Nightbot", "StreamElements", "Moobot", "praxis_bot")
slurList = ("fag", "faggot", "niga", "nigga", "nigger", "retard", "tard", "rtard", "coon") slurList = ("fag", "faggot", "niga", "nigga", "nigger", "retard", "tard", "rtard", "coon")
currentSpeaker = Speaker.STREAMLABS_API
fileNameStrategy = FileNameStrategy.CONTENT_BASED
streamlabsVoice = PollyVoices.Justin

View File

@ -6,3 +6,4 @@ pandas
numpy numpy
gTTS gTTS
playsound playsound
pyglet

12
tests/test_tts.py Normal file
View File

@ -0,0 +1,12 @@
import unittest
import tts
class TTSTest(unittest.TestCase):
def test_file_name(self):
tts.create_file_name("test", "mp3")
if __name__ == '__main__':
unittest.main()

View File

@ -4,7 +4,7 @@ import twitch
testValidUrls = ['https://shady.ru', 'http://stolencards.zn', 'https://i.imgur.com/FL6slHd.jpg'] testValidUrls = ['https://shady.ru', 'http://stolencards.zn', 'https://i.imgur.com/FL6slHd.jpg']
testInvalidUrls = ['this is just a sentence. With a period', 'gotta have some other stuff', 'bad punctuation produces false positives'] testInvalidUrls = ['this is just a sentence. With a period', 'gotta have some other stuff', 'bad punctuation.does not produces false positives']
class TwitchBotTest(unittest.TestCase): class TwitchBotTest(unittest.TestCase):

86
tts.py
View File

@ -1,31 +1,89 @@
import datetime
import hashlib
import os import os
import datetime import requests
from gtts import gTTS
from playsound import playsound from playsound import playsound
import config
streamLabsUrl = "https://streamlabs.com/polly/speak"
def tts(inputText: str, *args): def tts(inputText: str, *args):
destPath = os.getcwd() + "\\tts\\" outpath = create_speech_file(inputText)
playsound(outpath)
def create_speech_gtts(input_text: str):
path = os.path.join(get_tts_dir(), create_file_name(input_text, "mp3"))
if not os.path.exists(path):
sound_digest = gTTS(text=input_text, lang='en')
sound_digest.save(path)
return path
def create_speech_streamlabs(text: str):
path = os.path.join(get_tts_dir(), create_file_name(text, "ogg"))
if not os.path.exists(path):
body = {"voice": config.streamlabsVoice.value, "text": text}
resp = requests.post(streamLabsUrl, data=body).json()
sound_file_url = resp["speak_url"]
if sound_file_url is not None:
sound_bytes = requests.get(sound_file_url, stream=True)
f = open(path, "+wb")
f.write(sound_bytes.content)
f.close()
return path
speechCreationFunctions = { # this is a mapping of the Speaker enum to function pointers
config.Speaker.STREAMLABS_API: create_speech_streamlabs,
config.Speaker.GOOGLE_TEXT_TO_SPEECH: create_speech_gtts
}
def create_speech_file(text: str):
text_creation_function = speechCreationFunctions.get(config.currentSpeaker)
output_path = text_creation_function(text)
return output_path
def create_file_name(text: str, ext: str):
"""
:param text: the content of the message. using the CONTENT_BASED FileNameStrategy, this will (ostensibly) produce a
unique file name based on the content of the message. Two messages of equal content will produce the same name
:param ext: the desired file extension i.e. mp3, ogg, wav, etc...
:return: returns the formatted filename i.e. 01-01-20_01-01-01_tts.mp3
"""
if config.fileNameStrategy == config.FileNameStrategy.CONTENT_BASED:
unique_id = hashlib.md5(bytes(text, 'utf-8')).hexdigest()
return "%s_tts.%s" % (unique_id, ext)
elif config.fileNameStrategy == config.FileNameStrategy.CONTENT_BASED:
time = datetime.datetime.now() time = datetime.datetime.now()
fileName: str = time.strftime("%m-%d-%Y_%H-%M-%S") + "_tts.mp3" return "%s_tts.%s" % (time.strftime("%m-%d-%Y_%H-%M-%S"), ext)
if len(args) == 1: else:
fileName = args[0] + "_tts.mp3" return "unconfigured_tts.%s" % ext
# tts = gTTS(text=inputText, lang='en')
# tts.save(destPath + fileName)
playsound(destPath + fileName)
# os.system(filename)
def play_speech(fileName): def play_speech(fileName):
destPath = os.getcwd() + "\\tts\\" destPath = get_tts_dir()
playsound(destPath + fileName) playsound(destPath + fileName)
def get_tts_dir():
"""
Checks for the tts directory, and will create it if it does not exist
:return: the relative file path of the tts dir
"""
dir = os.path.join(os.getcwd(), "tts") # this is platform-agnostic
if not os.path.exists(dir):
os.mkdir(dir)
return dir
if __name__ == "__main__": if __name__ == "__main__":
print("Enter Text: ") print("Enter Text: ")
textInput = str(input()) textInput = str(input())

View File

@ -1,13 +1,13 @@
import random import random
import re
import twitch import twitch
import twitch.chat import twitch.chat
import twitch_cred as twitch_credentials
import config as config import config as config
import tts
import db import db
import re import tts
import twitch_cred as twitch_credentials
class Twitch_Module(): class Twitch_Module():
@ -19,7 +19,8 @@ class Twitch_Module():
self.links_allowed: bool = True self.links_allowed: bool = True
self.whitelisted_users: list = ["thecuriousnerd", "theredpoint", "lakotor"] self.whitelisted_users: list = ["thecuriousnerd", "theredpoint", "lakotor"]
# don't freak out, this is *merely* a regex for matching urls that will hit just about everything # don't freak out, this is *merely* a regex for matching urls that will hit just about everything
self._urlMatcher = re.compile("(https?:(/{1,3}|[a-z0-9%])|[a-z0-9.-]+[.](com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw))") self._urlMatcher = re.compile(
"(https?:(/{1,3}|[a-z0-9%])|[a-z0-9.-]+[.](com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw))")
def join_channel(self, channel_name): def join_channel(self, channel_name):
channel_name = "#" + channel_name channel_name = "#" + channel_name
@ -81,31 +82,19 @@ class Twitch_Module():
print("{something went wrong}") print("{something went wrong}")
def tts_message(self, message: twitch.chat.Message): def tts_message(self, message: twitch.chat.Message):
if self.contains_slur(message) == False: if not self.contains_slur(message):
if self.tts_enabled == True: if self.tts_enabled:
if message.text.startswith('!') == False: if not message.text.startswith('!'):
text_to_say: str = "%s says, %s" % (message.sender, message.text)
channel_text = "%s user msg" % message.channel
if message.sender.lower() == message.channel: if message.sender.lower() == message.channel:
tts.tts(message.sender + " says, " + message.text) tts.tts(text_to_say)
else: else:
# tts.tts(message.sender + " says, " + message.text) # tts.tts(message.sender + " says, " + message.text)
tts.tts(message.sender + " says, " + message.text, message.channel + " user msg") tts.tts(text_to_say, channel_text)
def contains_url(self, message: twitch.chat.Message): def contains_url(self, message: twitch.chat.Message):
# containsURL: bool = False
# if message.text.lower().find("http") != -1:
# containsURL = True
# if message.text.lower().find("https") != -1:
# containsURL = True
# if message.text.lower().find(".com") != -1:
# containsURL = True
# if message.text.lower().find(".net") != -1:
# containsURL = True
# if message.text.lower().find(".org") != -1:
# containsURL = True
# if message.text.lower().find(".tv") != -1:
# containsURL = True
# if message.text.lower().find(".io") != -1:
# containsURL = True
containsURL = re.search(self._urlMatcher, message.text.lower()) is not None containsURL = re.search(self._urlMatcher, message.text.lower()) is not None
if containsURL: if containsURL:
print("<{ link detected! }> " + " [#" + message.channel + "](" + message.sender + ") sent a link in chat") print("<{ link detected! }> " + " [#" + message.channel + "](" + message.sender + ") sent a link in chat")