mirror of
				https://gitlab.com/gpvkt/twitchtts.git
				synced 2025-10-31 09:07:34 +01:00 
			
		
		
		
	Merge branch 'dev' into 'main'
v1.2.0 See merge request gpvkt/twitchtts!2
This commit is contained in:
		
						commit
						e434c3b128
					
				
					 6 changed files with 130 additions and 38 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -2,3 +2,4 @@ config.yml | ||||||
| build | build | ||||||
| tts.spec | tts.spec | ||||||
| tts.exe | tts.exe | ||||||
|  | random*.txt | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -2,6 +2,20 @@ | ||||||
| 
 | 
 | ||||||
| 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. | 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.0] - 2022-08-13 | ||||||
|  | 
 | ||||||
|  | ### Added | ||||||
|  | 
 | ||||||
|  |   * `!random` feature (see README.md for details) | ||||||
|  | 
 | ||||||
|  | ### Changed | ||||||
|  | 
 | ||||||
|  |   * The vote result will be read out | ||||||
|  | 
 | ||||||
|  | ### Fixed | ||||||
|  | 
 | ||||||
|  |   * Improved handling of missing config values. | ||||||
|  | 
 | ||||||
| ## [1.1.0] - 2022-08-12 | ## [1.1.0] - 2022-08-12 | ||||||
| 
 | 
 | ||||||
| ### Added | ### Added | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
										
									
									
									
								
							|  | @ -65,6 +65,7 @@ messages: | ||||||
|   votestart: "Quickvote started. Send #yourchoice to participate." |   votestart: "Quickvote started. Send #yourchoice to participate." | ||||||
|   voteend: "Quickvote ended. The results are:" |   voteend: "Quickvote ended. The results are:" | ||||||
|   votenobody: "Nobody casted a vote. :(" |   votenobody: "Nobody casted a vote. :(" | ||||||
|  |   voteresult: "Voting has ended. The result is:" | ||||||
|   votes: "Votes" |   votes: "Votes" | ||||||
| 
 | 
 | ||||||
| log: | log: | ||||||
|  | @ -117,6 +118,7 @@ Please note that the `oauth_token` is valid for approximately 60 days. If it bec | ||||||
|   * `votestart`: Message when a quickvote is started. |   * `votestart`: Message when a quickvote is started. | ||||||
|   * `voteend`: Message if a quickvote ends. |   * `voteend`: Message if a quickvote ends. | ||||||
|   * `votenobody`: Message if quickvote ends, but nobody has voted. |   * `votenobody`: Message if quickvote ends, but nobody has voted. | ||||||
|  |   * `voteresult`: Prefix for the result (will be read out) | ||||||
|   * `votes`: Suffix to vote count. |   * `votes`: Suffix to vote count. | ||||||
| 
 | 
 | ||||||
| ##### log | ##### log | ||||||
|  | @ -166,7 +168,13 @@ Additional commands (broadcaster and mods only) are: | ||||||
| 
 | 
 | ||||||
| ### Additional features | ### Additional features | ||||||
| 
 | 
 | ||||||
| The bot also contains a `!quickvote` feature. If a broadcaster or moderator send the `!quickvote` command a vote will be started (or a already running vote will be ended). After a quickvote has been started your community can casts votes by sending a chat message starting with `#`. You can include a message after `!quickvote` (e.g. `!quickvote Is pizza hawaii any good? #yes/#no`). If you do so, this message will be repeated every 60 seconds, so everyone keeps in mind, that a vote is still active. | #### !quickvote | ||||||
|  | 
 | ||||||
|  | The `!quickvote` feature implements a simple vote system. If a broadcaster or moderator send the `!quickvote` command a vote will be started (or a already running vote will be ended). After a quickvote has been started your community can casts votes by sending a chat message starting with `#`. You can include a message after `!quickvote` (e.g. `!quickvote Is pizza hawaii any good? #yes/#no`). If you do so, this message will be repeated every 60 seconds, so everyone keeps in mind, that a vote is still active. | ||||||
|  | 
 | ||||||
|  | #### !random | ||||||
|  | 
 | ||||||
|  | The `!random` command will read a random line from a file called `random.txt`. You can also use multiple files, if you call `!random foo` the bot fetch the random line from a file called `random_foo.txt` instead of `random.txt`. `!random bar` will use the file `random_bar.txt` and so on. The `!random` command is restricted to the broadcaster and moderators. | ||||||
| 
 | 
 | ||||||
| ## Build | ## Build | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ messages: # Things the bot can send as chat message | ||||||
|   votestart: "Quickvote started. Send #yourchoice to participate." |   votestart: "Quickvote started. Send #yourchoice to participate." | ||||||
|   voteend: "Quickvote ended. The results are:" |   voteend: "Quickvote ended. The results are:" | ||||||
|   votenobody: "Nobody casted a vote. :(" |   votenobody: "Nobody casted a vote. :(" | ||||||
|  |   voteresult: "Voting has ended. The result is:" | ||||||
|   votes: "Votes" |   votes: "Votes" | ||||||
| 
 | 
 | ||||||
| log: | log: | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								dist/tts.exe
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								dist/tts.exe
									
										
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										142
									
								
								tts.py
									
										
									
									
									
								
							
							
						
						
									
										142
									
								
								tts.py
									
										
									
									
									
								
							|  | @ -20,21 +20,23 @@ | ||||||
|     along with this program.  If not, see <http://www.gnu.org/licenses/>. |     along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import json | import os | ||||||
| import logging |  | ||||||
| import socket |  | ||||||
| import sys | import sys | ||||||
| import time | import time | ||||||
|  | import json | ||||||
|  | import socket | ||||||
|  | import random | ||||||
|  | import logging | ||||||
| import datetime | import datetime | ||||||
| import socketserver |  | ||||||
| import urllib.request |  | ||||||
| import urllib.parse |  | ||||||
| import webbrowser | import webbrowser | ||||||
|  | import socketserver | ||||||
|  | import urllib.parse | ||||||
|  | import urllib.request | ||||||
| 
 | 
 | ||||||
| from threading import Thread | from threading import Thread | ||||||
| from http.server import BaseHTTPRequestHandler |  | ||||||
| from urllib.parse import parse_qs |  | ||||||
| from collections import Counter | from collections import Counter | ||||||
|  | from urllib.parse import parse_qs | ||||||
|  | from http.server import BaseHTTPRequestHandler | ||||||
| 
 | 
 | ||||||
| import yaml | import yaml | ||||||
| 
 | 
 | ||||||
|  | @ -77,8 +79,8 @@ class IRC: | ||||||
|         try: |         try: | ||||||
|             self.irc.send(bytes("JOIN " + channel + "\r\n", "UTF-8")) |             self.irc.send(bytes("JOIN " + channel + "\r\n", "UTF-8")) | ||||||
|         except ConnectionResetError: |         except ConnectionResetError: | ||||||
|             logging.warning('JOIN was refused, will try again in 5 seconds.') |             logging.warning('JOIN was refused, will try again in 30 seconds.') | ||||||
|             time.sleep(5) |             time.sleep(30) | ||||||
|             logging.warning('Please check your credentials, if this error persists.') |             logging.warning('Please check your credentials, if this error persists.') | ||||||
|             self.irc.send(bytes("JOIN " + channel + "\r\n", "UTF-8")) |             self.irc.send(bytes("JOIN " + channel + "\r\n", "UTF-8")) | ||||||
| 
 | 
 | ||||||
|  | @ -198,6 +200,38 @@ class IRC: | ||||||
| 
 | 
 | ||||||
|                     return True |                     return True | ||||||
| 
 | 
 | ||||||
|  |                 if msg.startswith('!random'): | ||||||
|  |                     logging.info('!random command detected') | ||||||
|  | 
 | ||||||
|  |                     randomfile = msg.replace('!random', '').strip().lower() | ||||||
|  |                     if randomfile: | ||||||
|  |                         randomfile = "random_"+str(os.path.basename(randomfile))+".txt" | ||||||
|  |                     else: | ||||||
|  |                         randomfile = "random.txt" | ||||||
|  | 
 | ||||||
|  |                     try: | ||||||
|  |                         with open(randomfile,"r", encoding="utf-8") as file: | ||||||
|  |                             lines = file.read().splitlines() | ||||||
|  |                             random_msg = random.choice(lines) | ||||||
|  |                     except FileNotFoundError: | ||||||
|  |                         logging.error('%s not found', randomfile) | ||||||
|  |                         return False | ||||||
|  | 
 | ||||||
|  |                     raw_msg = { | ||||||
|  |                         "TTS": True, | ||||||
|  |                         "msg": random_msg, | ||||||
|  |                         "badges": True, | ||||||
|  |                         "subscriber": True, | ||||||
|  |                         "msgid": True, | ||||||
|  |                         "user": conf['IRC_USERNAME'], | ||||||
|  |                         "length": conf['IRC_TTS_LEN'], | ||||||
|  |                         "queuetime": datetime.datetime.now(), | ||||||
|  |                         "timestamp": str(time.time_ns()) | ||||||
|  |                     } | ||||||
|  |                     msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] | ||||||
|  | 
 | ||||||
|  |                     return True | ||||||
|  | 
 | ||||||
|                 if msg.startswith('!quickvote'): |                 if msg.startswith('!quickvote'): | ||||||
|                     logging.info("!quickvote command detected") |                     logging.info("!quickvote command detected") | ||||||
|                     if self.quickvote: |                     if self.quickvote: | ||||||
|  | @ -207,6 +241,23 @@ class IRC: | ||||||
|                             logging.info("Nobody voted") |                             logging.info("Nobody voted") | ||||||
|                             self.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTEEND']) |                             self.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTEEND']) | ||||||
|                             self.sendmsg(conf['IRC_CHANNEL'], "*", conf['MESSAGE']['VOTENOBODY']) |                             self.sendmsg(conf['IRC_CHANNEL'], "*", conf['MESSAGE']['VOTENOBODY']) | ||||||
|  | 
 | ||||||
|  |                             raw_msg = { | ||||||
|  |                                 "TTS": True, | ||||||
|  |                                 "msg": conf['MESSAGE']['VOTENOBODY'], | ||||||
|  |                                 "badges": True, | ||||||
|  |                                 "subscriber": True, | ||||||
|  |                                 "msgid": True, | ||||||
|  |                                 "user": conf['IRC_USERNAME'], | ||||||
|  |                                 "length": conf['IRC_TTS_LEN'], | ||||||
|  |                                 "queuetime": datetime.datetime.now(), | ||||||
|  |                                 "timestamp": str(time.time_ns()) | ||||||
|  |                             } | ||||||
|  |                             msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] | ||||||
|  | 
 | ||||||
|  |                             logging.info('The result is: %s', conf['MESSAGE']['VOTENOBODY']) | ||||||
|  |                             logging.debug('Votemsg: %s', msg) | ||||||
|  | 
 | ||||||
|                             self.quickvote = False |                             self.quickvote = False | ||||||
|                             self.poll = {} |                             self.poll = {} | ||||||
|                             return False |                             return False | ||||||
|  | @ -218,6 +269,22 @@ class IRC: | ||||||
| 
 | 
 | ||||||
|                         logging.debug(count) |                         logging.debug(count) | ||||||
| 
 | 
 | ||||||
|  |                         raw_msg = { | ||||||
|  |                             "TTS": True, | ||||||
|  |                             "msg": conf['MESSAGE']['VOTERESULT'] +" "+ str(count[0][0].replace('#','')), | ||||||
|  |                             "badges": True, | ||||||
|  |                             "subscriber": True, | ||||||
|  |                             "msgid": True, | ||||||
|  |                             "user": conf['IRC_USERNAME'], | ||||||
|  |                             "length": conf['IRC_TTS_LEN'], | ||||||
|  |                             "queuetime": datetime.datetime.now(), | ||||||
|  |                             "timestamp": str(time.time_ns()) | ||||||
|  |                         } | ||||||
|  |                         msg_queue[raw_msg['timestamp']] = [raw_msg['user'], raw_msg['msg']] | ||||||
|  | 
 | ||||||
|  |                         logging.info('The result is: %s', conf['MESSAGE']['VOTERESULT'] +" "+ str(count[0])) | ||||||
|  |                         logging.debug('Votemsg: %s', msg) | ||||||
|  | 
 | ||||||
|                         for key, value in count: |                         for key, value in count: | ||||||
|                             self.sendmsg( |                             self.sendmsg( | ||||||
|                                 conf['IRC_CHANNEL'], "*", |                                 conf['IRC_CHANNEL'], "*", | ||||||
|  | @ -504,39 +571,40 @@ def load_config(): | ||||||
|     for section in cfg: |     for section in cfg: | ||||||
|         try: |         try: | ||||||
|             logging.debug('Fetching config: %s', section) |             logging.debug('Fetching config: %s', section) | ||||||
|             conf['IRC_CHANNEL'] = cfg['irc']['channel'] |             conf['IRC_CHANNEL'] = cfg.get('irc', {}).get('channel', False) | ||||||
|             conf['IRC_USERNAME'] = cfg['irc']['username'] |             conf['IRC_USERNAME'] = cfg.get('irc', {}).get('username', False) | ||||||
|             conf['IRC_OAUTH_TOKEN'] = cfg['irc']['oauth_token'] |             conf['IRC_OAUTH_TOKEN'] = cfg.get('irc', {}).get('oauth_token', False) | ||||||
|             conf['IRC_SERVER'] = cfg['irc']['server'] or "irc.chat.twitch.tv" |             conf['IRC_SERVER'] = cfg.get('irc', {}).get('server', "irc.chat.twitch.tv") | ||||||
|             conf['IRC_CLEARMSG_TIMEOUT'] = cfg['irc']['clearmsg_timeout'] or 60 |             conf['IRC_CLEARMSG_TIMEOUT'] = cfg.get('irc', {}).get('clearmsg_timeout', 60) | ||||||
| 
 | 
 | ||||||
|             conf['IRC_SUBONLY'] = cfg['bot']['subonly'] or False |             conf['IRC_SUBONLY'] = cfg.get('bot', {}).get('subonly', False) | ||||||
|             conf['IRC_MODONLY'] = cfg['bot']['modonly'] or False |             conf['IRC_MODONLY'] = cfg.get('bot', {}).get('modonly', False) | ||||||
|             conf['IRC_TTS_LEN'] = cfg['bot']['message_length'] or 200 |             conf['IRC_TTS_LEN'] = cfg.get('bot', {}).get('message_length', 200) | ||||||
|             conf['TTS_STARTENABLED'] = cfg['bot']['start_enabled'] or False |             conf['TTS_STARTENABLED'] = cfg.get('bot', {}).get('start_enabled', False) | ||||||
| 
 | 
 | ||||||
|             conf['LOG_LEVEL'] = cfg['log']['level'] or "INFO" |             conf['LOG_LEVEL'] = cfg.get('log', {}).get('level', "INFO") | ||||||
|             conf['HTTP_PORT'] = cfg['http']['port'] or 80 |             conf['HTTP_PORT'] = cfg.get('http', {}).get('port', 80) | ||||||
|             conf['HTTP_BIND'] = cfg['http']['bind'] or "localhost" |             conf['HTTP_BIND'] = cfg.get('http', {}).get('bind', "localhost") | ||||||
| 
 | 
 | ||||||
|             conf['MESSAGE'] = {} |             conf['MESSAGE'] = {} | ||||||
|             conf['MESSAGE']['TOFF'] = cfg['messages']['toff'] or "TTS is now disabled." |             conf['MESSAGE']['TOFF'] = cfg.get('messages', {}).get('toff', "TTS is now disabled.") | ||||||
|             conf['MESSAGE']['TON'] = cfg['messages']['ton'] or "TTS is now active." |             conf['MESSAGE']['TON'] = cfg.get('messages', {}).get('ton', "TTS is now active.") | ||||||
|             conf['MESSAGE']['TOO_LONG'] = cfg['messages']['too_long'] or "Sorry, your message is too long." |             conf['MESSAGE']['TOO_LONG'] = cfg.get('messages', {}).get('too_long', "Sorry, your message is too long.") | ||||||
|             conf['MESSAGE']['DISABLED'] = cfg['messages']['disabled'] or "Sorry, TTS is disabled." |             conf['MESSAGE']['DISABLED'] = cfg.get('messages', {}).get('disabled', "Sorry, TTS is disabled.") | ||||||
|             conf['MESSAGE']['DENIED'] = cfg['messages']['denied'] or "Sorry, you're not allowed to use TTS." |             conf['MESSAGE']['DENIED'] = cfg.get('messages', {}).get('denied', "Sorry, you're not allowed to use TTS.") | ||||||
|             conf['MESSAGE']['SUBONLY'] = cfg['messages']['subonly'] or "Sorry, TTS is sub-only." |             conf['MESSAGE']['SUBONLY'] = cfg.get('messages', {}).get('subonly', "Sorry, TTS is sub-only.") | ||||||
|             conf['MESSAGE']['MODONLY'] = cfg['messages']['modonly'] or "Sorry, TTS is mod-only." |             conf['MESSAGE']['MODONLY'] = cfg.get('messages', {}).get('modonly', "Sorry, TTS is mod-only.") | ||||||
|             conf['MESSAGE']['READY'] = cfg['messages']['ready'] or "TTS bot is ready." |             conf['MESSAGE']['READY'] = cfg.get('messages', {}).get('ready', "TTS bot is ready.") | ||||||
|             conf['MESSAGE']['WHITELISTONLY'] = cfg['messages']['whitelist'] or False |             conf['MESSAGE']['WHITELISTONLY'] = cfg.get('messages', {}).get('whitelist', False) | ||||||
|             conf['MESSAGE']['SAYS'] = cfg['messages']['says'] or "says" |             conf['MESSAGE']['SAYS'] = cfg.get('messages', {}).get('says', "says") | ||||||
| 
 | 
 | ||||||
|             conf['MESSAGE']['VOTESTART'] = cfg['messages']['votestart'] or "Quickvote started. Send #yourchoice to participate." |             conf['MESSAGE']['VOTESTART'] = cfg.get('messages', {}).get('votestart', "Quickvote started. Send #yourchoice to participate.") | ||||||
|             conf['MESSAGE']['VOTEEND'] = cfg['messages']['voteend'] or "Quickvote ended. The results are:" |             conf['MESSAGE']['VOTEEND'] = cfg.get('messages', {}).get('voteend', "Quickvote ended. The results are:") | ||||||
|             conf['MESSAGE']['VOTENOBODY']  = cfg['messages']['votenobody'] or "Nobody casted a vote. :(" |             conf['MESSAGE']['VOTENOBODY']  = cfg.get('messages', {}).get('votenobody', "Nobody casted a vote. :(") | ||||||
|             conf['MESSAGE']['VOTES']  = cfg['messages']['votes'] or "Stimmen" |             conf['MESSAGE']['VOTERESULT']  = cfg.get('messages', {}).get('voteresult', "Voting has ended. The result is:") | ||||||
|  |             conf['MESSAGE']['VOTES']  = cfg.get('messages', {}).get('votes', "Stimmen") | ||||||
| 
 | 
 | ||||||
|             conf['USERMAP'] = cfg['usermapping'] or [] |             conf['USERMAP'] = cfg.get('usermapping', []) | ||||||
| 
 | 
 | ||||||
|             if 'whitelist' in cfg: |             if 'whitelist' in cfg: | ||||||
|                 conf['WHITELIST'] = True |                 conf['WHITELIST'] = True | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue