mirror of
				https://gitlab.com/gpvkt/twitchtts.git
				synced 2025-10-31 00:57:35 +01:00 
			
		
		
		
	Merge branch 'dev'
This commit is contained in:
		
						commit
						fedcc96610
					
				
					 5 changed files with 397 additions and 294 deletions
				
			
		
							
								
								
									
										53
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										53
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -2,49 +2,60 @@ | |||
| 
 | ||||
| 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.2.2] - 2022-08-13 | ||||
| 
 | ||||
| ### Changed 1.2.2 | ||||
| 
 | ||||
| * The message queue is only queried when the Init button is pressed | ||||
| * Further code optimization | ||||
| 
 | ||||
| ### Fixed 1.2.2 | ||||
| 
 | ||||
| * Minor fixes | ||||
| 
 | ||||
| ## [1.2.1] - 2022-08-13 | ||||
| 
 | ||||
| ### Changed | ||||
| ### Changed 1.2.1 | ||||
| 
 | ||||
|   * Reworked internal code structure | ||||
| * Reworked internal code structure | ||||
| 
 | ||||
| ### Fixed | ||||
| ### Fixed 1.2.1 | ||||
| 
 | ||||
|   * Publish vote info in chat when reloading config was not working when TTS was disabled | ||||
|   * Casting votes was allowed for broadcaster and mods only | ||||
| * Publish vote info in chat when reloading config was not working when TTS was disabled | ||||
| * Casting votes was allowed for broadcaster and mods only | ||||
| 
 | ||||
| ## [1.2.0] - 2022-08-13 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
|   * `!random` feature (see README.md for details) | ||||
| * `!random` feature (see README.md for details) | ||||
| 
 | ||||
| ### Changed | ||||
| ### Changed 1.2.0 | ||||
| 
 | ||||
|   * The vote result will be read out | ||||
| * The vote result will be read out | ||||
| 
 | ||||
| ### Fixed | ||||
| ### Fixed 1.2.0 | ||||
| 
 | ||||
|   * Improved handling of missing config values. | ||||
| * Improved handling of missing config values | ||||
| 
 | ||||
| ## [1.1.0] - 2022-08-12 | ||||
| 
 | ||||
| ### Added | ||||
| ### Added 1.1.0 | ||||
| 
 | ||||
|   * `!quickvote` feature (see README.md for details) | ||||
|   * `!ping` command added | ||||
|   * Configoption to start TTS in disabled mode | ||||
|   * OAuth-Token generator | ||||
|   * Webbrowser autostart | ||||
| * `!quickvote` feature (see README.md for details) | ||||
| * `!ping` command added | ||||
| * Configoption to start TTS in disabled mode | ||||
| * OAuth-Token generator | ||||
| * Webbrowser autostart | ||||
| 
 | ||||
| ### Changed | ||||
| ### Changed 1.1.0 | ||||
| 
 | ||||
|   * You need to review your `config.yml` as there a new config values added. | ||||
|   * The bot replies with a chat message when `!ton` or `!toff` is used | ||||
| * You need to review your `config.yml` as there a new config values added. | ||||
| * The bot replies with a chat message when `!ton` or `!toff` is used | ||||
| 
 | ||||
| ### Fixed | ||||
| ### Fixed 1.1.0 | ||||
| 
 | ||||
|   * Improved error handling | ||||
| * Improved error handling | ||||
| 
 | ||||
| ## [1.0.0] - 2022-08-11 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										82
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										82
									
								
								README.md
									
										
									
									
									
								
							|  | @ -34,7 +34,7 @@ By using Javascript for the actual TTS part it's not only very easy to access th | |||
| 
 | ||||
| Adapt `config.yml` to your needs. Example: | ||||
| 
 | ||||
| ``` | ||||
| ``` lang=yaml | ||||
| irc: | ||||
|   channel: "#gpkvt" | ||||
|   username: "ttsbot" | ||||
|  | @ -82,11 +82,11 @@ whitelist: | |||
| 
 | ||||
| ##### irc | ||||
| 
 | ||||
|   * `channel`: Channel you want to monitor (e.g. #gpkvt) | ||||
|   * `username`: The bots username (e.g. gpkvt) | ||||
|   * `oauth_token`: The bots OAUTH-Token (e.g. oauth:ohkoace0wooghue8she9xaN0nooSau) | ||||
|   * `server`: Twitch IRC server to be used (default should be fine) | ||||
|   * `clearmsg_timeout`: Time to wait for an moderator to delete a message, before it's added to the TTS queue | ||||
| * `channel`: Channel you want to monitor (e.g. #gpkvt) | ||||
| * `username`: The bots username (e.g. gpkvt) | ||||
| * `oauth_token`: The bots OAUTH-Token (e.g. oauth:ohkoace0wooghue8she9xaN0nooSau) | ||||
| * `server`: Twitch IRC server to be used (default should be fine) | ||||
| * `clearmsg_timeout`: Time to wait for an moderator to delete a message, before it's added to the TTS queue | ||||
| 
 | ||||
| You can generate your `oauth_token` by leaving the value empty when starting `tts.exe/tts.py`. The integrated webserver will then provide an OAuth-Generator. Due to limitations to the `redirect_url` parameter used by twitch, this is only possible if you use Port `8080` or `80` as `http:bind`. If you use a different port, you will need to use another [Twitch OAuth Generator](https://html.duckduckgo.com/html/?q=twitch+oauth+token+generator). The bot will need `chat:edit` and `chat:read` permissions. | ||||
| 
 | ||||
|  | @ -94,36 +94,36 @@ Please note that the `oauth_token` is valid for approximately 60 days. If it bec | |||
| 
 | ||||
| ##### http | ||||
| 
 | ||||
|   * `port`: Internal Webserver Port to listen to (e.g. 8080) | ||||
|   * `bind`: Interface/IP to bind server to (e.g. localhost) | ||||
| * `port`: Internal Webserver Port to listen to (e.g. 8080) | ||||
| * `bind`: Interface/IP to bind server to (e.g. localhost) | ||||
| 
 | ||||
| ##### bot | ||||
| 
 | ||||
|   * `start_enabled`: Enable the bot on start? If `False` you need to use `!ton` first to make TTS work. | ||||
|   * `subonly`: If `True` only Subs can use TTS | ||||
|   * `modonly`: If `True` only Mods can use TTS | ||||
|   * `message_length`: Maximum allowed message length for TTS | ||||
| * `start_enabled`: Enable the bot on start? If `False` you need to use `!ton` first to make TTS work. | ||||
| * `subonly`: If `True` only Subs can use TTS | ||||
| * `modonly`: If `True` only Mods can use TTS | ||||
| * `message_length`: Maximum allowed message length for TTS | ||||
| 
 | ||||
| ##### messages | ||||
| 
 | ||||
|   * `toff`: The bots reply when `!toff` is used. | ||||
|   * `ton`: The bots reply when `!ton` is used. | ||||
|   * `too_long`: The bots reply if message exceeds `message_length` | ||||
|   * `disabled`: The bots reply if TTS is disabled | ||||
|   * `denied`: The bots reply if the user is not allowed to use TTS | ||||
|   * `subonly`: The bots reply if `subonly` is active and the user isn't one. | ||||
|   * `whitelist`: The bots reply if `whitelist` is set and user isn't on the list. | ||||
|   * `ready`: The bots init message | ||||
|   * `says`: Prefix to add between username and message | ||||
|   * `votestart`: Message when a quickvote is started. | ||||
|   * `voteend`: Message if a quickvote ends. | ||||
|   * `votenobody`: Message if quickvote ends, but nobody has voted. | ||||
|   * `voteresult`: Prefix for the result (will be read out) | ||||
|   * `votes`: Suffix to vote count. | ||||
| * `toff`: The bots reply when `!toff` is used. | ||||
| * `ton`: The bots reply when `!ton` is used. | ||||
| * `too_long`: The bots reply if message exceeds `message_length` | ||||
| * `disabled`: The bots reply if TTS is disabled | ||||
| * `denied`: The bots reply if the user is not allowed to use TTS | ||||
| * `subonly`: The bots reply if `subonly` is active and the user isn't one. | ||||
| * `whitelist`: The bots reply if `whitelist` is set and user isn't on the list. | ||||
| * `ready`: The bots init message | ||||
| * `says`: Prefix to add between username and message | ||||
| * `votestart`: Message when a quickvote is started. | ||||
| * `voteend`: Message if a quickvote ends. | ||||
| * `votenobody`: Message if quickvote ends, but nobody has voted. | ||||
| * `voteresult`: Prefix for the result (will be read out) | ||||
| * `votes`: Suffix to vote count. | ||||
| 
 | ||||
| ##### log | ||||
| 
 | ||||
|   * `level`: The loglevel, valid values are: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | ||||
| * `level`: The loglevel, valid values are: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | ||||
| 
 | ||||
| Do not use `DEBUG` in a production environment. | ||||
| 
 | ||||
|  | @ -139,7 +139,7 @@ You can add a whitelist section to `config.yml`, a whitelist will override any o | |||
| 
 | ||||
| A whitelist looks as follows: | ||||
| 
 | ||||
| ``` | ||||
| ``` lang=yaml | ||||
| whitelist: | ||||
|   - gpkvt | ||||
|   - foo | ||||
|  | @ -152,7 +152,7 @@ Please note: Usernames MUST be lowercase. | |||
| 
 | ||||
| ### Executing program | ||||
| 
 | ||||
| Execute `tts.exe` (or `tts.py` if you have Python installed), open the TTS webpage in your browser (the URL depends on your `bind` and `port` configuration, usually it's just http://localhost). Click the `Init` button at the button of the TTS webpage (you should hear `Init complete`). | ||||
| Execute `tts.exe` (or `tts.py` if you have Python installed), open the TTS webpage in your browser (the URL depends on your `bind` and `port` configuration, usually it's just `http://localhost`). Click the `Init` button at the button of the TTS webpage (you should hear `Init complete`). | ||||
| 
 | ||||
| Connect to the configured Twitch channel and send a message starting with `!tts`. After a few seconds (depending on your `clearmsg_timeout` config), the message should be read. | ||||
| 
 | ||||
|  | @ -160,11 +160,11 @@ Connect to the configured Twitch channel and send a message starting with `!tts` | |||
| 
 | ||||
| Additional commands (broadcaster and mods only) are: | ||||
| 
 | ||||
|   * `!ping`: Check if bot is alive (the bot should reply: `Pong!`) | ||||
|   * `!toff`: Turn TTS off (will also empty the current TTS queue) | ||||
|   * `!ton`: Turn TTS back on | ||||
|   * `!dtts <username>`: Disable TTS for the given user | ||||
|   * `!ptts <username>`: Allow TTS for the given user | ||||
| * `!ping`: Check if bot is alive (the bot should reply: `Pong!`) | ||||
| * `!toff`: Turn TTS off (will also empty the current TTS queue) | ||||
| * `!ton`: Turn TTS back on | ||||
| * `!dtts <username>`: Disable TTS for the given user | ||||
| * `!ptts <username>`: Allow TTS for the given user | ||||
| 
 | ||||
| ### Additional features | ||||
| 
 | ||||
|  | @ -180,10 +180,10 @@ The `!random` command will read a random line from a file called `random.txt`. Y | |||
| 
 | ||||
| If you prefer to build your own `tts.exe` instead of using the shipped one, you can do as follows: | ||||
| 
 | ||||
|   * Install Python 3 | ||||
|   * Install pyinstaller: `pip install pyinstaller` | ||||
|   * Install the required dependencies: `pip install -r requirements.txt -v` | ||||
|   * Create the executeable: `pyinstaller --onefile tts.py` | ||||
| * Install Python 3 | ||||
| * Install pyinstaller: `pip install pyinstaller` | ||||
| * Install the required dependencies: `pip install -r requirements.txt -v` | ||||
| * Create the executeable: `pyinstaller --onefile tts.py` | ||||
| 
 | ||||
| ## Voices | ||||
| 
 | ||||
|  | @ -211,9 +211,9 @@ This project is licensed under the GPLv3 License - see [LICENSE](https://gitlab. | |||
| 
 | ||||
| ### Ideas and Testing | ||||
| 
 | ||||
| * [GERBrowny and community](https://www.twitch.tv/gerbrowny/)  | ||||
| * [DerZugger and community](https://www.twitch.tv/derzugger/)  | ||||
| * [Timmeh74 and community](https://www.twitch.tv/timmeh74/)  | ||||
| * [GERBrowny and community](https://www.twitch.tv/gerbrowny/)  | ||||
| * [DerZugger and community](https://www.twitch.tv/derzugger/)  | ||||
| * [Timmeh74 and community](https://www.twitch.tv/timmeh74/)  | ||||
| 
 | ||||
| ### Libraries | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								dist/tts.exe
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								dist/tts.exe
									
										
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										5
									
								
								tts.js
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								tts.js
									
										
									
									
									
								
							|  | @ -34,6 +34,7 @@ document.querySelector("#start").addEventListener("click", () => { | |||
|   speech.text = "Init complete"; | ||||
|   window.speechSynthesis.speak(speech); | ||||
|   $("#start").hide(); | ||||
|   init(); | ||||
| }); | ||||
| 
 | ||||
| document.querySelector("#pause").addEventListener("click", () => { | ||||
|  | @ -54,7 +55,7 @@ function sleep(ms) { | |||
| 
 | ||||
| reload = true; | ||||
| 
 | ||||
| $(document).ready(function() { | ||||
| function init() { | ||||
|     setInterval(function(){ | ||||
|       if (reload) { | ||||
|         $.ajax({ | ||||
|  | @ -89,4 +90,4 @@ $(document).ready(function() { | |||
|         }); | ||||
|       } | ||||
|     }, 1000); | ||||
| }); | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										551
									
								
								tts.py
									
										
									
									
									
								
							
							
						
						
									
										551
									
								
								tts.py
									
										
									
									
									
								
							|  | @ -95,6 +95,277 @@ class IRC: | |||
|         """ | ||||
|         self.irc.send(bytes("PRIVMSG "+channel+" :"+user+" "+msg+"\r\n", "UTF-8")) | ||||
| 
 | ||||
|     def resp_ping(self): | ||||
|         """ Respond to PING """ | ||||
|         logging.debug('PING received') | ||||
|         self.irc.send(bytes('PONG :tmi.twitch.tv\r\n', "UTF-8")) | ||||
| 
 | ||||
|     def resp_notice(self, resp): | ||||
|         """ Respond to NOTICE """ | ||||
|         if 'Login authentication failed' in resp: | ||||
|             try: | ||||
|                 raise RuntimeError() | ||||
|             except RuntimeError: | ||||
|                 logging.exception('Login failed, please check your credentials and try again.') | ||||
|                 sys.exit(251) | ||||
| 
 | ||||
|     def resp_clearmsg(self, resp): | ||||
|         """ Respond to CLEARMSG """ | ||||
|         logging.info('CLEARMSG received') | ||||
|         msgid = False | ||||
| 
 | ||||
|         global msg_queue_raw # pylint: disable=global-statement,invalid-name | ||||
|         filtered_msg_queue = [] | ||||
| 
 | ||||
|         tags = resp.split(';') | ||||
|         for tag in tags: | ||||
|             if "target-msg-id=" in tag: | ||||
|                 msgid = tag.rsplit('target-msg-id=',1)[1] | ||||
|                 logging.debug('Trying to suppress message') | ||||
|                 logging.debug(msgid) | ||||
| 
 | ||||
|                 for msg in list(msg_queue_raw): | ||||
|                     if msg['msgid'] == msgid: | ||||
|                         logging.info('Suppressing message %s', msgid) | ||||
|                     else: | ||||
|                         filtered_msg_queue.append(msg) | ||||
| 
 | ||||
|                 msg_queue_raw = filtered_msg_queue | ||||
| 
 | ||||
|     def resp_privmsg(self, resp): | ||||
|         """ Respond to PRIVMSG """ | ||||
|         logging.debug('PRIVMSG received') | ||||
| 
 | ||||
|         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('#') and self.quickvote_status is True: | ||||
|             logging.info('Quickvote: Cast detected') | ||||
|             self.pollcount += 1 | ||||
|             self.poll[user] = msg.lower() | ||||
|             logging.debug(self.poll) | ||||
| 
 | ||||
|         if msg.startswith('!tts'): | ||||
|             logging.info('!tts command detected') | ||||
|             self.tts_command(message, tags) | ||||
| 
 | ||||
|     def get_tags(self, resp): | ||||
|         """ Strip tags from response """ | ||||
| 
 | ||||
|         tags = resp.split(';') | ||||
|         for tag in tags: | ||||
|             if tag.startswith('badges='): | ||||
|                 badges = tag.rsplit('badges=',1)[1] | ||||
|             if tag.startswith('subscriber='): | ||||
|                 subscriber = tag.rsplit('subscriber=',1)[1] | ||||
|                 logging.debug('Subscriber: %s', subscriber) | ||||
|             if tag.startswith('id='): | ||||
|                 msgid = tag.rsplit('id=',1)[1] | ||||
|                 logging.debug('Message ID: %s', msgid) | ||||
|             if tag.startswith('display-name='): | ||||
|                 user = tag.rsplit('display-name=',1)[1].lower() | ||||
|                 logging.debug('Username: %s', user) | ||||
| 
 | ||||
|         tags = {} | ||||
|         tags['badges'] = badges | ||||
|         tags['subscriber'] = subscriber | ||||
|         tags['msgid'] = msgid | ||||
|         tags['user'] = user | ||||
| 
 | ||||
|         return tags | ||||
| 
 | ||||
|     def get_message(self, resp): | ||||
|         """ Process message """ | ||||
| 
 | ||||
|         msg = {} | ||||
|         msg['message'] = resp.rsplit('PRIVMSG #',1)[1].split(':',1)[1].replace('\r\n','') | ||||
|         msg['length'] = len(msg['message']) | ||||
| 
 | ||||
|         return msg | ||||
| 
 | ||||
|     def priviledged_commands(self, message, tags): | ||||
|         """ Process priviledged commands """ | ||||
| 
 | ||||
|         msg = message['message'] | ||||
|         badges = tags['badges'] | ||||
|         user = tags['user'] | ||||
| 
 | ||||
|         if 'broadcaster' in badges or 'moderator' in badges: | ||||
|             if msg.startswith('!ping'): | ||||
|                 logging.debug("Ping check received.") | ||||
|                 self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), "Pong!") | ||||
| 
 | ||||
|             elif msg.startswith('!dtts'): | ||||
|                 logging.debug("!dtts command detected") | ||||
|                 self.Commands.dtts(self, msg) | ||||
| 
 | ||||
|             elif msg.startswith('!random'): | ||||
|                 logging.info('!random command detected') | ||||
|                 self.Commands.random(self, msg) | ||||
| 
 | ||||
|             elif msg.startswith('!quickvote'): | ||||
|                 logging.info("!quickvote command detected") | ||||
|                 self.Commands.quickvote(self, msg) | ||||
| 
 | ||||
|             elif msg.startswith('!ptts'): | ||||
|                 logging.debug("!ptts command detected") | ||||
|                 self.Commands.ptts(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): | ||||
|         """ subonly """ | ||||
| 
 | ||||
|         if not conf['IRC_SUBONLY']: | ||||
|             return False | ||||
| 
 | ||||
|         subscriber = tags['subscriber'] | ||||
|         badges = tags['badges'] | ||||
|         user = tags['user'] | ||||
| 
 | ||||
|         if subscriber != "0" or 'moderator' in badges or 'broadcaster' in badges: | ||||
|             logging.info('TTS is sub-only and user has allowance') | ||||
|             return False | ||||
| 
 | ||||
|         logging.debug('TTS is sub-only') | ||||
|         self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['SUBONLY']) | ||||
|         return True | ||||
| 
 | ||||
|     def check_modonly(self, tags): | ||||
|         """ modonly """ | ||||
| 
 | ||||
|         if not conf['IRC_MODONLY']: | ||||
|             return False | ||||
| 
 | ||||
|         badges = tags['badges'] | ||||
|         user = tags['user'] | ||||
| 
 | ||||
|         if 'moderator' in badges or 'broadcaster' in badges: | ||||
|             logging.info('TTS is mod-only and user has allowance') | ||||
|             return False | ||||
| 
 | ||||
|         logging.debug('TTS is mod-only') | ||||
|         self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['MODONLY']) | ||||
|         return True | ||||
| 
 | ||||
|     def check_tts_disabled(self, user): | ||||
|         """ Check if TTS is disabled """ | ||||
|         if not self.tts_status: | ||||
|             self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['DISABLED']) | ||||
|             return True | ||||
| 
 | ||||
|         logging.debug('TTS is enabled') | ||||
|         return False | ||||
| 
 | ||||
|     def check_msg_too_long(self, message, user): | ||||
|         """ Check if message is too long """ | ||||
| 
 | ||||
|         if message['length'] > conf['IRC_TTS_LEN']: | ||||
|             self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOO_LONG']) | ||||
|             return True | ||||
| 
 | ||||
|         logging.debug('Check length: Message is ok') | ||||
|         return False | ||||
| 
 | ||||
|     def check_user_denied(self, user): | ||||
|         """ Check if user is on denied list """ | ||||
|         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']) | ||||
|             return True | ||||
| 
 | ||||
|         logging.debug("%s is allowed to use TTS", user) | ||||
|         return False | ||||
| 
 | ||||
|     def check_whitelist(self, user): | ||||
|         """ Check Whitelist """ | ||||
| 
 | ||||
|         if conf['WHITELIST']: | ||||
|             if user not in self.tts_allowed: | ||||
|                 logging.debug("tts_allowed: %s", self.tts_allowed) | ||||
|                 self.sendmsg( | ||||
|                     conf['IRC_CHANNEL'], | ||||
|                     "@"+str(user), conf['MESSAGE']['WHITELISTONLY'] | ||||
|                 ) | ||||
|                 return False | ||||
|             return True | ||||
|         return False | ||||
| 
 | ||||
|     def send_tts_msg(self, message, tags): | ||||
|         """ Send message to TTS queue """ | ||||
|         logging.info('Valid TTS message, adding to raw queue') | ||||
| 
 | ||||
|         tts = True | ||||
|         now = datetime.datetime.now() | ||||
|         timestamp = str(time.time_ns()) | ||||
| 
 | ||||
|         user = tags['user'] | ||||
|         msgid = tags['msgid'] | ||||
|         badges = tags['badges'] | ||||
|         subscriber = tags['subscriber'] | ||||
| 
 | ||||
|         msg = message['message'] | ||||
|         msglen = message['length'] | ||||
|         msg = msg.replace('!tts','',1) | ||||
| 
 | ||||
|         msg = { | ||||
|             "TTS": tts, | ||||
|             "msg": msg, | ||||
|             "badges": badges, | ||||
|             "subscriber": subscriber, | ||||
|             "msgid": msgid, | ||||
|             "user": user, | ||||
|             "length": msglen, | ||||
|             "queuetime": now, | ||||
|             "timestamp": timestamp | ||||
|         } | ||||
| 
 | ||||
|         msg_queue_raw.append(msg) | ||||
| 
 | ||||
|     def tts_command(self, message, tags): | ||||
|         """ Process !tts command """ | ||||
| 
 | ||||
|         user = tags['user'] | ||||
| 
 | ||||
|         if self.check_tts_disabled(user): | ||||
|             logging.info('TTS is disabled') | ||||
|         elif self.check_msg_too_long(message, user): | ||||
|             logging.info('TTS message is too long') | ||||
|         elif self.check_user_denied(user): | ||||
|             logging.info('User is not allowed to use TTS') | ||||
|         elif self.check_subonly(tags): | ||||
|             logging.info('TTS is sub-only') | ||||
|         elif self.check_modonly(tags): | ||||
|             logging.info('TTS is mod-only') | ||||
|         elif self.check_whitelist(user): | ||||
|             logging.info('User is not on whitelist') | ||||
|         else: | ||||
|             logging.info('Sending TTS message to raw_queue') | ||||
|             self.send_tts_msg(message, tags) | ||||
| 
 | ||||
|     def get_response(self): | ||||
|         """Get and process response from IRC""" | ||||
|         try: | ||||
|  | @ -102,202 +373,22 @@ class IRC: | |||
|             logging.debug('resp:') | ||||
|             logging.debug(resp) | ||||
|         except socket.timeout: | ||||
|             return False | ||||
|             return | ||||
|         except Exception: # pylint: disable=broad-except | ||||
|             logging.exception('An unknown error occured while getting a IRC response.') | ||||
|             sys.exit(255) | ||||
| 
 | ||||
|         if resp.find('PING') != -1: | ||||
|             logging.debug('PING received') | ||||
|             self.irc.send(bytes('PONG :tmi.twitch.tv\r\n', "UTF-8")) | ||||
|             self.resp_ping() | ||||
| 
 | ||||
|         if resp.find('CLEARMSG') != -1: | ||||
|             logging.info('CLEARMSG received') | ||||
|             msgid = False | ||||
| 
 | ||||
|             global msg_queue_raw # pylint: disable=global-statement,invalid-name | ||||
|             filtered_msg_queue = [] | ||||
| 
 | ||||
|             tags = resp.split(';') | ||||
|             for tag in tags: | ||||
|                 if "target-msg-id=" in tag: | ||||
|                     msgid = tag.rsplit('target-msg-id=',1)[1] | ||||
|                     logging.debug('Trying to suppress message') | ||||
|                     logging.debug(msgid) | ||||
| 
 | ||||
|                     for msg in list(msg_queue_raw): | ||||
|                         if msg['msgid'] == msgid: | ||||
|                             logging.info('Suppressing message %s', msgid) | ||||
|                         else: | ||||
|                             filtered_msg_queue.append(msg) | ||||
| 
 | ||||
|                     msg_queue_raw = filtered_msg_queue | ||||
| 
 | ||||
|             return True | ||||
|             self.resp_clearmsg(resp) | ||||
| 
 | ||||
|         if resp.find('NOTICE') != -1: | ||||
|             if 'Login authentication failed' in resp: | ||||
|                 try: | ||||
|                     raise RuntimeError() | ||||
|                 except RuntimeError: | ||||
|                     logging.exception('Login failed, please check your credentials and try again.') | ||||
|                     sys.exit(251) | ||||
|             self.resp_notice(resp) | ||||
| 
 | ||||
|         if resp.find('PRIVMSG') != -1: | ||||
|             logging.debug('PRIVMSG received') | ||||
|             badges = False | ||||
|             subscriber = False | ||||
|             msgid = False | ||||
|             msg = False | ||||
|             msglen = False | ||||
|             user = False | ||||
|             tts = False | ||||
| 
 | ||||
|             tags = resp.split(';') | ||||
|             for tag in tags: | ||||
|                 if tag.startswith('badges='): | ||||
|                     badges = tag.rsplit('badges=',1)[1] | ||||
|                 if tag.startswith('subscriber='): | ||||
|                     subscriber = tag.rsplit('subscriber=',1)[1] | ||||
|                     logging.debug('Subscriber: %s', subscriber) | ||||
|                 if tag.startswith('id='): | ||||
|                     msgid = tag.rsplit('id=',1)[1] | ||||
|                     logging.debug('Message ID: %s', msgid) | ||||
|                 if tag.startswith('display-name='): | ||||
|                     user = tag.rsplit('display-name=',1)[1].lower() | ||||
|                     logging.debug('Username: %s', user) | ||||
| 
 | ||||
|             msg = resp.rsplit('PRIVMSG #',1)[1] | ||||
|             msg = msg.split(':',1)[1] | ||||
|             msg = msg.replace('\r\n','') | ||||
|             msglen = len(msg) | ||||
| 
 | ||||
|             logging.debug('Msg: %s', msg) | ||||
|             logging.debug('Msg length: %s', msglen) | ||||
|             logging.debug('Deny List:') | ||||
|             logging.debug(self.tts_denied) | ||||
| 
 | ||||
|             if msg.startswith('#') and self.quickvote_status is True: | ||||
|                 logging.info('Quickvote: Cast detected') | ||||
|                 self.pollcount += 1 | ||||
|                 self.poll[user] = msg.lower() | ||||
|                 logging.debug(self.poll) | ||||
|                 return True | ||||
| 
 | ||||
|             if 'broadcaster' in badges or 'moderator' in badges: | ||||
|                 if msg.startswith('!ping'): | ||||
|                     logging.debug("Ping check received.") | ||||
|                     self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), "Pong!") | ||||
|                     return True | ||||
| 
 | ||||
|                 if msg.startswith('!dtts'): | ||||
|                     logging.debug("!dtts command detected") | ||||
|                     self.Commands.dtts(self, msg) | ||||
|                     return True | ||||
| 
 | ||||
|                 if msg.startswith('!random'): | ||||
|                     logging.info('!random command detected') | ||||
|                     self.Commands.random(self, msg) | ||||
|                     return True | ||||
| 
 | ||||
|                 if msg.startswith('!quickvote'): | ||||
|                     logging.info("!quickvote command detected") | ||||
|                     self.Commands.quickvote(self, msg) | ||||
|                     return True | ||||
| 
 | ||||
|                 if msg.startswith('!ptts'): | ||||
|                     logging.debug("!ptts command detected") | ||||
|                     self.Commands.ptts(self, msg) | ||||
|                     return True | ||||
| 
 | ||||
|                 if 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']) | ||||
|                     return True | ||||
| 
 | ||||
|                 if 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']) | ||||
|                     return True | ||||
| 
 | ||||
|             if msg.startswith('!tts'): | ||||
|                 logging.debug('!tts command detected') | ||||
|                 logging.debug("tts status: %s", self.tts_status) | ||||
| 
 | ||||
|                 if not self.tts_status: | ||||
|                     logging.info('TTS is disabled') | ||||
|                     self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['DISABLED']) | ||||
|                     return False | ||||
| 
 | ||||
|                 if msglen > conf['IRC_TTS_LEN']: | ||||
|                     logging.info('TTS message is to long') | ||||
|                     self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOO_LONG']) | ||||
|                     return False | ||||
| 
 | ||||
|                 if conf['IRC_SUBONLY']: | ||||
|                     if subscriber != "0" or 'moderator' in badges or 'broadcaster' in badges: | ||||
|                         logging.debug('TTS is sub-only and user has allowance') | ||||
|                     else: | ||||
|                         logging.info('TTS is sub-only') | ||||
|                         self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['SUBONLY']) | ||||
|                         return False | ||||
| 
 | ||||
|                 if conf['IRC_MODONLY']: | ||||
|                     if 'moderator' in badges or 'broadcaster' in badges: | ||||
|                         logging.debug('TTS is mod-only and user has allowance') | ||||
|                     else: | ||||
|                         logging.info('TTS is sub-only') | ||||
|                         self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['MODONLY']) | ||||
|                         return False | ||||
| 
 | ||||
|                 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']) | ||||
|                     return False | ||||
| 
 | ||||
|                 if conf['WHITELIST']: | ||||
|                     if user not in self.tts_allowed: | ||||
|                         logging.info('User is not on whitelist') | ||||
|                         logging.info(self.tts_allowed) | ||||
|                         self.sendmsg( | ||||
|                             conf['IRC_CHANNEL'], | ||||
|                             "@"+str(user), conf['MESSAGE']['WHITELISTONLY'] | ||||
|                         ) | ||||
|                         return False | ||||
| 
 | ||||
|                     logging.warning('Nobody is on the whitelist.') | ||||
|                     self.sendmsg( | ||||
|                         conf['IRC_CHANNEL'], | ||||
|                         "@"+str(user), conf['MESSAGE']['WHITELISTONLY'] | ||||
|                     ) | ||||
|                     return False | ||||
| 
 | ||||
|                 logging.info('Valid TTS message, adding to raw queue') | ||||
|                 tts = True | ||||
|                 now = datetime.datetime.now() | ||||
|                 msg = msg.replace('!tts','',1) | ||||
|                 msg = { | ||||
|                     "TTS": tts, | ||||
|                     "msg": msg, | ||||
|                     "badges": badges, | ||||
|                     "subscriber": subscriber, | ||||
|                     "msgid": msgid, | ||||
|                     "user": user, | ||||
|                     "length": msglen, | ||||
|                     "queuetime": now, | ||||
|                     "timestamp": str(time.time_ns()) | ||||
|                 } | ||||
|                 msg_queue_raw.append(msg) | ||||
| 
 | ||||
|                 return True | ||||
| 
 | ||||
|         return False | ||||
|             self.resp_privmsg(resp) | ||||
| 
 | ||||
|     class Commands(): | ||||
|         """ Bot commands """ | ||||
|  | @ -345,7 +436,7 @@ class IRC: | |||
| 
 | ||||
|                     self.quickvote_status = False | ||||
|                     self.poll = {} | ||||
|                     return False | ||||
|                     return | ||||
| 
 | ||||
|                 logging.info("Counting votes") | ||||
|                 count = 0 | ||||
|  | @ -640,7 +731,7 @@ def load_config(): | |||
|             conf['IRC_SUBONLY'] = cfg.get('bot', {}).get('subonly', False) | ||||
|             conf['IRC_MODONLY'] = cfg.get('bot', {}).get('modonly', False) | ||||
|             conf['IRC_TTS_LEN'] = cfg.get('bot', {}).get('message_length', 200) | ||||
|             conf['TTS_STARTENABLED'] = cfg.get('bot', {}).get('start_enabled', False) | ||||
|             conf['TTS_STARTENABLED'] = cfg.get('bot', {}).get('start_enabled', True) | ||||
| 
 | ||||
|             conf['LOG_LEVEL'] = cfg.get('log', {}).get('level', "INFO") | ||||
|             conf['HTTP_PORT'] = cfg.get('http', {}).get('port', 80) | ||||
|  | @ -704,9 +795,25 @@ sys.tracebacklimit = 0 | |||
| if sys.argv[1:]: | ||||
|     if sys.argv[1] == "--version": | ||||
|         print('Simple TTS Bot') | ||||
|         print('Version 1.2.1') | ||||
|         print('Version 1.2.2') | ||||
|         sys.exit(1) | ||||
| 
 | ||||
| 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 main(): | ||||
|     """Main loop""" | ||||
| 
 | ||||
|  | @ -734,58 +841,42 @@ def main(): | |||
|         logging.info('Please complete the OAuth process within the next 15 minutes.') | ||||
|         time.sleep(900) | ||||
|         sys.exit(250) | ||||
|     else: | ||||
|         logging.info("Starting IRC bot") | ||||
|         irc = IRC() | ||||
| 
 | ||||
|         irc.connect(conf['IRC_SERVER'], 6667, conf['IRC_CHANNEL'], conf['IRC_USERNAME'], conf['IRC_OAUTH_TOKEN']) | ||||
|         irc.sendmsg(conf['IRC_CHANNEL'], 'MrDestructoid', conf['MESSAGE']['READY']) | ||||
|     logging.info("Starting IRC bot") | ||||
|     irc = IRC() | ||||
| 
 | ||||
|         logging.info("Please open your browser and visit: http://%s:%s/", conf['HTTP_BIND'], conf['HTTP_PORT']) | ||||
|         url = 'http://'+str(conf['HTTP_BIND'])+':'+str(conf['HTTP_PORT']) | ||||
|         webbrowser.open_new_tab(url) | ||||
|     irc.connect(conf['IRC_SERVER'], 6667, conf['IRC_CHANNEL'], conf['IRC_USERNAME'], conf['IRC_OAUTH_TOKEN']) | ||||
|     irc.sendmsg(conf['IRC_CHANNEL'], 'MrDestructoid', conf['MESSAGE']['READY']) | ||||
| 
 | ||||
|         while True: | ||||
|             if conf['LOG_LEVEL'] == "DEBUG": | ||||
|                 time.sleep(1) | ||||
|     logging.info("Please open your browser and visit: http://%s:%s/", conf['HTTP_BIND'], conf['HTTP_PORT']) | ||||
|     url = 'http://'+str(conf['HTTP_BIND'])+':'+str(conf['HTTP_PORT']) | ||||
|     webbrowser.open_new_tab(url) | ||||
| 
 | ||||
|             try: | ||||
|                 irc.get_response() | ||||
|     while True: | ||||
|         if conf['LOG_LEVEL'] == "DEBUG": | ||||
|             time.sleep(1) | ||||
| 
 | ||||
|                 confreload = datetime.datetime.now() | ||||
|                 if confreload - lastreload > datetime.timedelta(seconds=60): | ||||
|                     conf = load_config() | ||||
|                     lastreload = datetime.datetime.now() | ||||
|         try: | ||||
|             irc.get_response() | ||||
| 
 | ||||
|                     if irc.quickvote_status and irc.votemsg: | ||||
|                         logging.info('Quickvote is active') | ||||
|                         irc.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTESTART'] + " (" + str(irc.votemsg) + ")") | ||||
|             confreload = datetime.datetime.now() | ||||
|             if confreload - lastreload > datetime.timedelta(seconds=60): | ||||
|                 conf = load_config() | ||||
|                 lastreload = datetime.datetime.now() | ||||
| 
 | ||||
|                 if not irc.tts_status: | ||||
|                     logging.debug("TTS is disabled") | ||||
|                     if conf['LOG_LEVEL'] == "DEBUG": | ||||
|                         time.sleep(1) | ||||
|                     continue | ||||
|                 if irc.quickvote_status and irc.votemsg: | ||||
|                     logging.info('Quickvote is active') | ||||
|                     irc.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTESTART'] + " (" + str(irc.votemsg) + ")") | ||||
| 
 | ||||
|                 logging.debug('Raw message queue:') | ||||
|                 logging.debug(msg_queue_raw) | ||||
|             if not irc.tts_status: | ||||
|                 continue | ||||
| 
 | ||||
|                 for raw_msg in msg_queue_raw: | ||||
|                     logging.debug('Raw msg:') | ||||
|                     logging.debug(msg_queue_raw) | ||||
|             logging.debug('msg_queue_raw: %s', msg_queue_raw) | ||||
|             send_tts_queue() | ||||
| 
 | ||||
|                     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) | ||||
|                         else: | ||||
|                             logging.debug('Msg is already in queue') | ||||
|             except KeyboardInterrupt: | ||||
|                 logging.info('Exiting...') | ||||
|                 sys.exit() | ||||
|         except KeyboardInterrupt: | ||||
|             logging.info('Exiting...') | ||||
|             sys.exit() | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue