diff --git a/README.md b/README.md index 9406164..ebc005f 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,15 @@ Oauth = `https://twitchapps.com/tmi/` V5 Client ID = `https://twitchtokengenerator.com/` ### For Database Credentials +Nickname = `Anything You Want` + Engine = `"mysql+mysqlconnector://root:password@localhost:3306/DatabaseName"` +### For Discord Credentials +Nickname = `Anything You Want` + +Token = `https://discord.com/developers/` + ## Credential Usage: Place json credentials in the `/credentials/` folder. diff --git a/commands/implemented/command_roll_discord.py b/commands/implemented/command_roll_discord.py index b517b2d..8c86d07 100644 --- a/commands/implemented/command_roll_discord.py +++ b/commands/implemented/command_roll_discord.py @@ -19,12 +19,13 @@ class CommandRoll(AbstractCommand, metaclass=ABCMeta): def __init__(self): super().__init__(CommandRoll.command, n_args=1, command_type=AbstractCommand.CommandType.DISCORD) - def do_command(self, bot, discord_message: discord.Message): + async def do_command(self, bot, discord_message: discord.Message): print("!roll Detected") #twitch_message.chat.send("test acknowledged") diceRoll: str = "" - discord_message.channel.send("Rolling Dice...") + await bot.send_message(discord_message, "Rolling Dice...") + #await discord_message.channel.send("Rolling Dice...") print("Rolling Dice...") temp_preParsedMessage = discord_message.content.split("+") @@ -67,6 +68,8 @@ class CommandRoll(AbstractCommand, metaclass=ABCMeta): else: diceRoll = str(roll) - diceRoll = "@" + message.author.display_name + " rolled: " + diceRoll + + diceRoll = discord_message.author.mention + " rolled: " + diceRoll print(diceRoll) - discord_message.channel.send(diceRoll) \ No newline at end of file + await bot.send_message(discord_message, diceRoll) + #await discord_message.channel.send(diceRoll) diff --git a/commands/implemented/command_tts.py b/commands/implemented/command_tts.py index 305e1cf..133d851 100644 --- a/commands/implemented/command_tts.py +++ b/commands/implemented/command_tts.py @@ -18,5 +18,5 @@ class CommandTTS(AbstractCommand, metaclass=ABCMeta): bot.tts_enabled = True elif args[1] == "stop": if twitch_message.sender.lower() == twitch_message.channel: - bot.send_message("tts activated on #%s" % twitch_message.channel) + bot.send_message("tts deactivated on #%s" % twitch_message.channel) bot.tts_enabled = False diff --git a/commands/implemented/command_tts_discord.py b/commands/implemented/command_tts_discord.py new file mode 100644 index 0000000..36e0064 --- /dev/null +++ b/commands/implemented/command_tts_discord.py @@ -0,0 +1,30 @@ +from abc import ABCMeta + +from commands.command_base import AbstractCommand + +import discord +import discord.message + +class CommandTTS(AbstractCommand, metaclass=ABCMeta): + command = "!tts" + + def __init__(self): + super().__init__(CommandTTS.command, n_args=1, command_type=AbstractCommand.CommandType.DISCORD) + + async def do_command(self, bot, discord_message: discord.message): + args = self.get_args(discord_message.content) + if args[1] == "start": + print(discord_message.author.top_role) + print("start detected") + if str(discord_message.author.top_role) == "Admin": + print("Admin Check") + response = str("tts activated on %s" % discord_message.guild.name) + await bot.send_message(discord_message, response) + bot.tts_enabled = True + elif args[1] == "stop": + print("stop detected") + if str(discord_message.author.top_role) == "Admin": + print("Admin Check") + response = str("tts deactivated on %s" % discord_message.guild.name) + await bot.send_message(discord_message, response) + bot.tts_enabled = False diff --git a/commands/loader.py b/commands/loader.py index cab69b1..e885988 100644 --- a/commands/loader.py +++ b/commands/loader.py @@ -12,6 +12,9 @@ def load_commands() -> Dict[str, AbstractCommand]: commands = compile_and_load() return commands +def load_commands_new(commandType: AbstractCommand.CommandType) -> Dict[str, AbstractCommand]: + commands = compile_and_load_new(commandType) + return commands def compile_and_load_file(path: str) -> (str, AbstractCommand): module_name = os.path.split(path)[1].replace(".py", "") @@ -41,6 +44,18 @@ def compile_and_load() -> Dict[str, AbstractCommand]: break return dic +def compile_and_load_new(commandType: AbstractCommand.CommandType) -> Dict[str, AbstractCommand]: + dic = {} + implementations = get_implementations_dir() + for dirName, subdirList, fileList in os.walk(implementations): + for file in fileList: + name = os.path.join(dirName, file) + print("compiling %s" % name) + name, command = compile_and_load_file(name) + if command is not None and command.command_type is commandType: + dic[name] = command + break + return dic def get_base_dir() -> str: cwd = os.getcwd() @@ -52,7 +67,7 @@ def get_base_dir() -> str: return check_dir(os.path.join(cwd, "commands")) else: print("could not find working directory for Praxis_Bot/commands") - raise + raise Exception def get_implementations_dir() -> str: diff --git a/config.py b/config.py index e4899e4..1d1bb5a 100644 --- a/config.py +++ b/config.py @@ -74,7 +74,7 @@ class PollyVoices(Enum): Zhiyu = "Zhiyu" -botList = ("Nightbot", "StreamElements", "Moobot", "praxis_bot") +botList = ("Nightbot", "StreamElements", "Moobot", "Praxis Bot", "praxis_bot", "MEE6 +", "Nerdy", "Rythm", "Groovy") slurList = ("fag", "faggot", "niga", "nigga", "nigger", "retard", "tard", "rtard", "coon") diff --git a/credential_templates/credential_template_discord.json b/credential_templates/credential_template_discord.json index 51f9dc0..7a721bd 100644 --- a/credential_templates/credential_template_discord.json +++ b/credential_templates/credential_template_discord.json @@ -1,4 +1,4 @@ { - "username": "AnythingYouWant", + "nickname": "AnythingYouWant", "token": "" } \ No newline at end of file diff --git a/discord_script.py b/discord_script.py index bcddc24..64d0bf2 100644 --- a/discord_script.py +++ b/discord_script.py @@ -3,28 +3,44 @@ import re from discord import message from discord.client import Client +import asyncio import config as config import db import tts +import commands.loader as command_loader +from commands.command_base import AbstractCommand + import credentials import discord import discord.message import discord.channel +from cooldowns import Cooldown_Module + class Discord_Module(discord.Client): def __init__(self): super().__init__() + self.loop = asyncio.get_event_loop() self.dbCredential: credentials.DB_Credential self.discordCredential: credentials.Discord_Credential + self.cooldownModule:Cooldown_Module = Cooldown_Module() + self.cooldownModule.setupCooldown("discordRateLimit", 10, 1) + + self.commands = command_loader.load_commands_new(AbstractCommand.CommandType.DISCORD) + self.tts_enabled: bool = False + async def startup(self): + await self.start(self.discordCredential.token) + def main(self): - self.run(self.discordCredential.token) - + print("starting loop") + self.loop.create_task(self.startup()) + self.loop.run_forever() async def on_ready(self): print('Logged on as', self.user) @@ -39,8 +55,85 @@ class Discord_Module(discord.Client): if message.content == "//test": await message.channel.send('test response') - def do_command(self): - pass + if not await self.isSenderBot(message): + if self.cooldownModule.isCooldownActive("discordRateLimit") == False: + await self.eval_commands(message) + await self.tts_message(message) + + + async def eval_commands(self, message: discord.Message): + # containsURL: bool = self.contains_url(message) + try: + #first_space_idx = message.text.index(' ') + + # This fixes a error where if you send a command without arguments it fails because + # it cant find the substring. + if message.content.find(" ") != -1: + first_space_idx = message.content.index(' ') + else: + first_space_idx = -1 + + command_text = ' ' + if first_space_idx > -1: + command_text = message.content[0:first_space_idx] + else: + command_text = message.content + + command = self.commands[command_text] + if command is not None and command.command_type is AbstractCommand.CommandType.DISCORD: + await command.do_command(self, message) + except Exception as e: + # Undo the following for debug stuff + #print(e) + pass # we don't care + + async def send_message(self, message, response): + if self.cooldownModule.isCooldownActive("discordRateLimit") == False: + await message.channel.send(response) + self.cooldownModule.actionTrigger("discordRateLimit") + + async def tts_message(self, message: discord.Message): + if not await self.contains_slur(message): + if self.tts_enabled: + if not message.content.startswith('!'): + text_to_say: str = "%s says, %s" % (message.author.display_name, message.content) + channel_text = "%s user msg" % message.channel + + tts.tts(text_to_say) + + # Checks for basic slurs. + async def contains_slur(self, message: discord.Message): + containsSlur: bool = False + + if await self.slur_check(message.content) or await self.slur_check(message.author.display_name): + containsSlur = True + + return containsSlur + + async def slur_check(self, text): + containsSlur: bool = False + parsedMessage = text.split(" ") + for word in parsedMessage: + for slur in config.slurList: + if word.lower() == slur: + containsSlur = True + break # we want to immediately escape if we found a slur + if containsSlur: + break + + if containsSlur: + print("<{ slur detected! }>") + #print("<{ slur detected! }> " + " [#" + message.channel + "](" + message.author.display_name + ") used a slur in chat") + return containsSlur + + # Checks if Sender is bot. + async def isSenderBot(self, message: discord.Message): + isBot = False + for bot in config.botList: + if message.author.display_name.lower() == bot.lower(): + isBot = True + print("<{ bot detected! }> ") + return isBot @@ -54,4 +147,6 @@ if __name__ == "__main__": testModule.discordCredential = credentials_manager.find_Discord_Credential("praxis_bot") testModule.main() + testModule.main() + \ No newline at end of file diff --git a/main.py b/main.py index 9491bb2..4939d57 100644 --- a/main.py +++ b/main.py @@ -11,35 +11,66 @@ import utilities_script as utility import credentials +import threading + twitch_chat: twitch_script.Twitch_Module credentials_manager: credentials.Credentials_Module +discord_connection: discord_script.Discord_Module def main(): - global twitch_chat - global credentials_manager - credentials_manager = credentials.Credentials_Module() - credentials_manager.load_credentials() + pass - dbCert: credentials.DB_Credential = credentials_manager.find_Credential(credentials.DB_Credential, "praxis_bot") - twitchCert: credentials.Twitch_Credential = credentials_manager.find_Twitch_Credential("praxis_bot") - discordCert: credentials.Discord_Credential = credentials_manager.find_Discord_Credential("praxis_bot") +def new_main(inputArg): + args = utility.get_args(inputArg) - twitch_chat = twitch_script.Twitch_Module() - +def twitch_module_init(dbCert, twitchCert): twitch_chat.db_manager.setup_engine(dbCert) twitch_chat.twitchCredential = twitchCert twitch_chat.join_channel(None, "thecuriousnerd") - discord_connection = discord_script.Discord_Module() + +def discord_module_init(dbCert, discordCert): discord_connection.dbCredential = dbCert discord_connection.discordCredential = discordCert discord_connection.main() - #Doesn't do stuff past this point - twitch_chat.send_message("activated") + + +def thread_main(): + global twitch_chat + global discord_connection + global credentials_manager + + twitch_chat = twitch_script.Twitch_Module() + discord_connection = discord_script.Discord_Module() + credentials_manager = credentials.Credentials_Module() + + credentials_manager.load_credentials() + dbCert: credentials.DB_Credential = credentials_manager.find_Credential(credentials.DB_Credential, "praxis_bot") + twitchCert: credentials.Twitch_Credential = credentials_manager.find_Twitch_Credential("praxis_bot") + discordCert: credentials.Discord_Credential = credentials_manager.find_Discord_Credential("praxis_bot") + + threads = [] + + twitch = threading.Thread(target=twitch_module_init, args=(dbCert, twitchCert)) + threads.append(twitch) + twitch.start() + + discord = threading.Thread(target=discord_module_init, args=(dbCert, discordCert)) + threads.append(discord) + discord.start() + + print("---Post Thread Creation Test---") + for t in threads: + t.join() + + print("---Point of no return---") + + + if __name__ == "__main__": - main() + thread_main() diff --git a/twitch_script.py b/twitch_script.py index bf4ef2c..ef530ae 100644 --- a/twitch_script.py +++ b/twitch_script.py @@ -23,7 +23,7 @@ class Twitch_Module(): self.db_manager: db.db_module = db.db_module() self.chat: twitch.Chat - self.commands = command_loader.load_commands() + self.commands = command_loader.load_commands_new(AbstractCommand.CommandType.TWITCH) self.tts_enabled: bool = False self.tts_whitelist_enabled: bool = False self.links_allowed: bool = True @@ -34,8 +34,8 @@ class Twitch_Module(): # Default Twitch Chat limit is 20 per 30 seconds # If Mod or Op, Twitch Chat limit is 100 per 30 seconds - self.twitchChat_cooldown:Cooldown_Module = Cooldown_Module() - self.twitchChat_cooldown.setupCooldown("twitchChat", 20, 32) + self.cooldownModule:Cooldown_Module = Cooldown_Module() + self.cooldownModule.setupCooldown("twitchChat", 20, 32) def join_channel(self, credential: credentials.Twitch_Credential, channel_name:str): channel_name = "#" + channel_name @@ -60,9 +60,9 @@ class Twitch_Module(): self.chat.irc.socket.close() def send_message(self, message): - if self.twitchChat_cooldown.isCooldownActive("twitchChat") == False: + if self.cooldownModule.isCooldownActive("twitchChat") == False: self.chat.send(message) - self.twitchChat_cooldown.actionTrigger("twitchChat") + self.cooldownModule.actionTrigger("twitchChat") def send_whisper(self, user, message): pass @@ -73,13 +73,12 @@ class Twitch_Module(): if message.channel == "thecuriousnerd": if not self.isSenderBot(message): - if self.twitchChat_cooldown.isCooldownActive("twitchChat") == False: + if self.cooldownModule.isCooldownActive("twitchChat") == False: self.eval_commands(message) - #elif message.channel == message.sender: - #self.eval_commands(message) self.tts_message(message) def eval_commands(self, message: twitch.chat.Message): + print("evaling command") # containsURL: bool = self.contains_url(message) try: #first_space_idx = message.text.index(' ') @@ -99,9 +98,12 @@ class Twitch_Module(): command = self.commands[command_text] if command is not None and command.command_type is AbstractCommand.CommandType.TWITCH: + print("evaling command") command.do_command(self, message) except Exception as e: - print(e) + # Undo the following for debug stuff + #print(e) + print("failed command") pass # we don't care @@ -112,11 +114,7 @@ class Twitch_Module(): text_to_say: str = "%s says, %s" % (message.sender, message.text) channel_text = "%s user msg" % message.channel - if message.sender.lower() == message.channel: - tts.tts(text_to_say) - else: - # tts.tts(message.sender + " says, " + message.text) - tts.tts(text_to_say, channel_text) + tts.tts(text_to_say) def contains_url(self, message: twitch.chat.Message): containsURL = re.search(self._urlMatcher, message.text.lower()) is not None diff --git a/utilities_script.py b/utilities_script.py index dc1f6b6..38a1292 100644 --- a/utilities_script.py +++ b/utilities_script.py @@ -1,3 +1,6 @@ import os clearScreen = lambda: os.system('cls' if os.name == 'nt' else 'clear') + +def get_args(text: str) -> list: + return text.split(" ") \ No newline at end of file