Experimental

This commit is contained in:
gpkvt 2022-08-12 16:54:40 +00:00
parent 74240d03f1
commit facb86e15f
4 changed files with 294 additions and 94 deletions

View File

@ -2,12 +2,26 @@
All notable changes to this project will be documented in this file.
## [1.0.0] - 2022-08-11
Initial Release
## [1.1.0] - unreleased
### Added
* `!quickvote` feature (see README.md for details)
* `!ping` command added
* Configoption to start TTS in disabled mode
* OAuth-Token generator
* Webbrowser autostart
### Changed
* 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
* Improved error handling
## [1.0.0] - 2022-08-11
Initial Release

View File

@ -45,6 +45,7 @@ http:
bind: "localhost"
bot:
start_enabled: True
subonly: False
modonly: False
message_length: 200
@ -59,6 +60,10 @@ messages:
whitelist: "Sorry, you are not allowed to use TTS."
ready: "TTS bot alpha ready!"
says: "says"
votestart: "Quickvote started. Send #yourchoice to participate."
voteend: "Quickvote ended. The results are:"
votenobody: "Nobody casted a vote. :("
votes: "Votes"
log:
level: "INFO"
@ -80,13 +85,18 @@ whitelist:
* `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).
Please note that the `oauth_token` is valid for approximately 60 days. If it become invalid the bot will not connect anymore and you will have to renew the token.
##### http
* `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
@ -102,11 +112,17 @@ whitelist:
* `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.
* `votes`: Suffix to vote count.
##### log
* `level`: The loglevel, valid values are: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
Do not use `DEBUG` in a production environment.
##### usermapping
Use this section to define key:value pairs of usernames. The first value is the Twitch username, the second value is how the bot should pronouce the user, when reading the message. This is helpfull if you have regulars with numbers or strangs chars in the name. You can add new/change entries on the fly without restarting the bot (changes took up to 60 seconds).
@ -146,6 +162,10 @@ Additional commands (broadcaster and mods only) are:
* `!dtts <username>`: Disable TTS for the given user
* `!ptts <username>`: Allow TTS for the given user
### 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.
## Build
If you prefer to build your own `tts.exe` instead of using the shipped one, you can do as follows:

View File

@ -10,6 +10,7 @@ http:
bind: "localhost"
bot:
start_enabled: True
subonly: False
modonly: False
message_length: 200
@ -25,6 +26,10 @@ messages:
modonly: "Sorry, TTS is a mod-only feature."
ready: "TTS bot alpha ready!"
says: "says"
votestart: "Quickvote started. Send #yourchoice to participate."
voteend: "Quickvote ended. The results are:"
votenobody: "Nobody casted a vote. :("
votes: "Votes"
log:
level: "INFO"

341
tts.py
View File

@ -1,5 +1,6 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# pylint: disable=line-too-long
"""
TwitchTTS
@ -20,32 +21,43 @@
"""
import json
import yaml
import logging
import socket
import sys
import time
import datetime
import socketserver
import urllib.request
import urllib.parse
import webbrowser
from threading import Thread
from http.server import BaseHTTPRequestHandler
from urllib.parse import parse_qs
from collections import Counter
import yaml
class IRC:
"""IRC bot"""
irc = socket.socket()
def __init__(self):
self.irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tts_denied = []
self.tts_allowed = []
self.tts_status = True
self.tts_status = conf['TTS_STARTENABLED']
self.quickvote = False
self.votemsg = False
self.poll = {}
self.pollcount = 0
if 'WHITELIST_USER' in conf:
self.tts_allowed = conf['WHITELIST_USER']
def connect(self, server, port, channel, botnick, botpass):
logging.info("Connecting to: " + server)
"""Connect to Twitch IRC servers"""
logging.info("Connecting to: %s", server)
try:
self.irc.connect((server, port))
except ConnectionResetError:
@ -55,7 +67,9 @@ class IRC:
self.irc.settimeout(1)
self.irc.send(bytes("PASS " + botpass + "\r\n", "UTF-8"))
self.irc.send(bytes("USER " + botnick + " " + botnick +" " + botnick + " :python\r\n", "UTF-8"))
self.irc.send(bytes(
"USER " + botnick + " " + botnick +" " + botnick + " :python\r\n", "UTF-8")
)
self.irc.send(bytes("NICK " + botnick + "\r\n", "UTF-8"))
self.irc.send(bytes("CAP REQ :twitch.tv/commands twitch.tv/tags \r\n", "UTF-8"))
time.sleep(5)
@ -63,22 +77,31 @@ class IRC:
try:
self.irc.send(bytes("JOIN " + channel + "\r\n", "UTF-8"))
except ConnectionResetError:
logging.warn('JOIN was refused, will try again in 5 seconds.')
logging.warning('JOIN was refused, will try again in 5 seconds.')
time.sleep(5)
logging.warn('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"))
def sendpriv(self, channel, user, msg):
def sendmsg(self, channel, user, msg):
"""
Send (a public) message to IRC channel
Parameters:
channel (str): Channel to post msg to
user (str): User to address message to
msg (str): Message to send
"""
self.irc.send(bytes("PRIVMSG "+channel+" :"+user+" "+msg+"\r\n", "UTF-8"))
def get_response(self):
"""Get and process response from IRC"""
try:
resp = self.irc.recv(2048).decode("UTF-8")
logging.debug('resp:')
logging.debug(resp)
except socket.timeout:
return False
except:
except Exception: # pylint: disable=broad-except
logging.exception('An unknown error occured while getting a IRC response.')
sys.exit(255)
@ -90,7 +113,7 @@ class IRC:
logging.info('CLEARMSG received')
msgid = False
global msg_queue_raw
global msg_queue_raw # pylint: disable=global-statement,invalid-name
filtered_msg_queue = []
tags = resp.split(';')
@ -102,7 +125,7 @@ class IRC:
for msg in list(msg_queue_raw):
if msg['msgid'] == msgid:
logging.info('Suppressing message '+str(msgid))
logging.info('Suppressing message %s', msgid)
else:
filtered_msg_queue.append(msg)
@ -134,22 +157,21 @@ class IRC:
badges = tag.rsplit('badges=',1)[1]
if tag.startswith('subscriber='):
subscriber = tag.rsplit('subscriber=',1)[1]
logging.debug('Subscriber: '+str(subscriber))
logging.debug('Subscriber: %s', subscriber)
if tag.startswith('id='):
msgid = tag.rsplit('id=',1)[1]
logging.debug('Message ID: '+str(msgid))
logging.debug('Message ID: %s', msgid)
if tag.startswith('display-name='):
user = tag.rsplit('display-name=',1)[1].lower()
logging.debug('Username: '+str(user))
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:')
logging.debug(msg)
logging.debug('Msg length: '+str(msglen))
logging.debug('Msg: %s', msg)
logging.debug('Msg length: %s', msglen)
logging.debug('Deny List:')
logging.debug(self.tts_denied)
@ -162,17 +184,62 @@ class IRC:
logging.debug('Removing "@" from username')
user = user.replace('@', '')
if user not in self.tts_denied:
logging.info("Adding "+str(user)+" to deny list")
logging.info("Adding %s to deny list", user)
self.tts_denied.append(user)
if user in self.tts_allowed:
logging.info("Removing "+str(user)+" from allowed list")
logging.info("Removing %s from allowed list", user)
self.tts_allowed.remove(user)
return True
if msg.startswith('!ping'):
logging.debug('Ping check received.')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), "Pong!")
logging.debug("Ping check received.")
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), "Pong!")
return True
if msg.startswith('!quickvote'):
logging.info("!quickvote command detected")
if self.quickvote:
logging.debug('Quickvote stopped')
if self.pollcount == 0:
logging.info("Nobody voted")
self.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTEEND'])
self.sendmsg(conf['IRC_CHANNEL'], "*", conf['MESSAGE']['VOTENOBODY'])
self.quickvote = False
self.poll = {}
return False
logging.info("Counting votes")
count = 0
count = Counter(self.poll.values()).most_common(5)
self.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTEEND'])
logging.debug(count)
for key, value in count:
self.sendmsg(
conf['IRC_CHANNEL'], "*",
str(key)+" ("+str(value)+ " "+ conf['MESSAGE']['VOTES'] + ")"
)
self.quickvote = False
self.poll = {}
self.pollcount = 0
return True
else:
logging.debug('Quickvote started')
self.quickvote = True
self.votemsg = resp.split('!quickvote', 1)[1].strip()
if self.votemsg:
self.sendmsg(
conf['IRC_CHANNEL'], "@chat",
conf['MESSAGE']['VOTESTART'] + " (" + str(self.votemsg) + ")"
)
else:
self.sendmsg(conf['IRC_CHANNEL'], "@chat", conf['MESSAGE']['VOTESTART'])
return True
return True
@ -184,21 +251,27 @@ class IRC:
logging.debug('Removing "@" from username')
user = user.replace('@', '')
logging.info("Adding "+str(user)+" to whitelist")
logging.info("Adding %s to whitelist", user)
self.tts_allowed.append(user)
if user in self.tts_denied:
logging.info("Removing "+str(user)+" from deny list")
logging.info("Removing %s from deny list", user)
self.tts_denied.remove(user)
return True
if msg.startswith('#') and self.quickvote is True:
logging.info('Quickvote: Cast detected')
self.pollcount += 1
self.poll[user] = msg.lower()
logging.debug(self.poll)
if msg.startswith('!toff'):
logging.info('TTS is now turned off')
msg_queue.clear()
msg_queue_raw.clear()
self.tts_status = False
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOFF'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOFF'])
return True
@ -207,7 +280,7 @@ class IRC:
msg_queue.clear()
msg_queue_raw.clear()
self.tts_status = True
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TON'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TON'])
return True
@ -216,17 +289,20 @@ class IRC:
if msglen > conf['IRC_TTS_LEN']:
logging.info('TTS message is to long')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOO_LONG'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['TOO_LONG'])
return False
logging.debug("tts status: %s", self.tts_status)
logging.debug(conf['TTS_STARTENABLED'])
if not self.tts_status:
logging.info('TTS is disabled')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['DISABLED'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['DISABLED'])
return False
if user in self.tts_denied:
logging.info(str(user) + " is not allowed to use TTS")
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['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['IRC_SUBONLY']:
@ -234,7 +310,7 @@ class IRC:
logging.debug('TTS is sub-only and user has allowance')
else:
logging.info('TTS is sub-only')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['SUBONLY'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['SUBONLY'])
return False
if conf['IRC_MODONLY']:
@ -242,25 +318,41 @@ class IRC:
logging.debug('TTS is mod-only and user has allowance')
else:
logging.info('TTS is sub-only')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['MODONLY'])
self.sendmsg(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['MODONLY'])
return False
if conf['WHITELIST']:
if not user in self.tts_allowed:
if user not in self.tts_allowed:
logging.info('User is not on whitelist')
logging.info(self.tts_allowed)
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['WHITELISTONLY'])
self.sendmsg(
conf['IRC_CHANNEL'],
"@"+str(user), conf['MESSAGE']['WHITELISTONLY']
)
return False
else:
logging.info('Nobody is on the whitelist.')
self.sendpriv(conf['IRC_CHANNEL'], "@"+str(user), conf['MESSAGE']['WHITELISTONLY'])
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 = {
"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
@ -268,48 +360,52 @@ class IRC:
return False
class HTTPserv(BaseHTTPRequestHandler):
def log_message(self, format, *args):
"""Simple HTTP Server"""
def log_message(self, format, *args): # pylint: disable=redefined-builtin
"""Suppress HTTP log messages"""
return
def do_GET(self):
def do_GET(self): # pylint: disable=invalid-name
"""Process GET requests"""
if self.path == '/':
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
with open("tts.html", "rb") as fh:
html = fh.read()
with open("tts.html", "rb") as file:
html = file.read()
self.wfile.write(html)
elif self.path == '/favicon.ico':
self.send_response(200)
self.send_header('Content-type', 'image/x-icon')
self.end_headers()
with open("favicon.ico", "rb") as fh:
icon = fh.read()
with open("favicon.ico", "rb") as file:
icon = file.read()
self.wfile.write(icon)
elif self.path == '/tts.js':
self.send_response(200)
self.send_header('Content-type', 'text/javascript')
self.end_headers()
with open("tts.js", "rb") as fh:
html = fh.read()
with open("tts.js", "rb") as file:
html = file.read()
self.wfile.write(html)
elif self.path == '/jquery.js':
self.send_response(200)
self.send_header('Content-type', 'text/javascript')
self.end_headers()
with open("jquery.js", "rb") as fh:
html = fh.read()
with open("jquery.js", "rb") as file:
html = file.read()
self.wfile.write(html)
elif self.path == '/bootstrap.min.css':
self.send_response(200)
self.send_header('Content-type', 'text/css')
self.end_headers()
with open("bootstrap.min.css", "rb") as fh:
html = fh.read()
with open("bootstrap.min.css", "rb") as file:
html = file.read()
self.wfile.write(html)
elif self.path.startswith('/tts_queue'):
@ -325,9 +421,9 @@ class HTTPserv(BaseHTTPRequestHandler):
for key in list(sorted_tts.keys()):
if key not in tts_done:
if msg_queue[key][0].lower() in usermap:
tts = {key: usermap[msg_queue[key][0].lower()]+" "+str(conf['MESSAGE']['SAYS'])+":"+msg_queue[key][1]}
tts = {str(key): str(usermap[msg_queue[key][0].lower()]) + " " + str(conf['MESSAGE']['SAYS']) + ":" + str(msg_queue[key][1])}
else:
tts = {key: msg_queue[key][0]+" "+str(conf['MESSAGE']['SAYS'])+":"+msg_queue[key][1]}
tts = {str(key): str(msg_queue[key][0]) + " " + str(conf['MESSAGE']['SAYS']) + ":" + str(msg_queue[key][1])}
tts_json = json.dumps(tts)
self.wfile.write(bytes(str(tts_json)+"\n", "utf-8"))
@ -347,6 +443,41 @@ class HTTPserv(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(bytes("Internal Server error\n", "utf-8"))
elif self.path.startswith('/token') and conf['IRC_OAUTH_TOKEN'] == "Invalid":
data = {}
data['client_id'] = "ebo548vs6tq54c9zlrgin2yfzzlrrs"
data['response_type'] = "token"
data['scope'] = "chat:edit chat:read"
if conf['HTTP_PORT'] == 80:
data['redirect_uri'] = "http://localhost/token"
elif conf['HTTP_PORT'] == 8080:
data['redirect_uri'] = "http://localhost:8080/token"
elif conf['HTTP_PORT'] == 3000:
data['redirect_uri'] = "http://localhost:3000/token"
else:
self.send_response(500)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(bytes("You can only use this function if HTTP_PORT is 80, 8080 or 3000. Please change your port, or use https://www.21x9.org/twitch instead.\n", "utf-8"))
return False
try:
url_values = urllib.parse.urlencode(data)
url = "https://id.twitch.tv/oauth2/authorize"
full_url = url + "?" + url_values
data = urllib.request.urlopen(full_url)
if data:
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(bytes("<html><head><title>OAuth Token Generator</title></head><body onload=\"displayCode();\"><div id=\"code\"><a href=\""+str(data.geturl())+"\">Click to start the OAuth process.</a></div><script>function displayCode() { var url = window.location.href; var test = url.indexOf(\"access_token\");if (test != -1) { token = url.substring(42,72); document.getElementById(\"code\").innerHTML = \"<p>oauth:\" + token + \"</p><p>Copy the token into your config.yml and restart the bot.</p>\";}}</script></body></html>\n", "utf-8"))
else:
self.send_response(500)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(bytes("Could not get OAuth-URL from Twitch\n", "utf-8"))
except Exception: # pylint: disable=broad-except
logging.error('Could not fetch OAuth-URL from Twitch.')
else:
self.send_response(404)
self.send_header('Server', 'TTS')
@ -357,13 +488,16 @@ class HTTPserv(BaseHTTPRequestHandler):
return
def http_serve_forever(httpd):
httpd.serve_forever()
"""httpd loop"""
httpd.serve_forever()
def load_config():
"""Loading config variables"""
logging.info("Loading configfile")
try:
with open("config.yml", "r") as ymlfile:
with open("config.yml", "r", encoding="UTF-8") as ymlfile:
cfg = yaml.load(ymlfile, Loader=yaml.Loader)
except FileNotFoundError:
logging.fatal('Your config file is missing, please copy config-dist.yml to config.yml and review your settings.')
@ -371,17 +505,18 @@ def load_config():
for section in cfg:
try:
logging.debug('Fetching config: '+str(section))
logging.debug('Fetching config: %s', section)
conf['IRC_CHANNEL'] = cfg['irc']['channel']
conf['IRC_USERNAME'] = cfg['irc']['username']
conf['IRC_OAUTH_TOKEN'] = cfg['irc']['oauth_token']
conf['IRC_SERVER'] = cfg['irc']['server'] or "irc.chat.twitch.tv"
conf['IRC_CLEARMSG_TIMEOUT'] = cfg['irc']['clearmsg_timeout'] or 60
conf['IRC_SUBONLY'] = cfg['bot']['subonly'] or False
conf['IRC_MODONLY'] = cfg['bot']['modonly'] or False
conf['IRC_TTS_LEN'] = cfg['bot']['message_length'] or 200
conf['TTS_STARTENABLED'] = cfg['bot']['start_enabled'] or False
conf['LOG_LEVEL'] = cfg['log']['level'] or "INFO"
conf['HTTP_PORT'] = cfg['http']['port'] or 80
conf['HTTP_BIND'] = cfg['http']['bind'] or "localhost"
@ -398,6 +533,11 @@ def load_config():
conf['MESSAGE']['WHITELISTONLY'] = cfg['messages']['whitelist'] or False
conf['MESSAGE']['SAYS'] = cfg['messages']['says'] or "says"
conf['MESSAGE']['VOTESTART'] = cfg['messages']['votestart'] or "Quickvote started. Send #yourchoice to participate."
conf['MESSAGE']['VOTEEND'] = cfg['messages']['voteend'] or "Quickvote ended. The results are:"
conf['MESSAGE']['VOTENOBODY'] = cfg['messages']['votenobody'] or "Nobody casted a vote. :("
conf['MESSAGE']['VOTES'] = cfg['messages']['votes'] or "Stimmen"
conf['USERMAP'] = cfg['usermapping'] or []
if 'whitelist' in cfg:
@ -420,7 +560,8 @@ def load_config():
if not conf['IRC_USERNAME']:
raise ValueError('Please add the bots username to config.yml.')
if not conf['IRC_OAUTH_TOKEN']:
raise ValueError('Please add the bots oauth-token to config.yml.')
conf['IRC_OAUTH_TOKEN'] = "Invalid"
return conf
if not conf['IRC_OAUTH_TOKEN'].startswith('oauth:'):
raise ValueError('Your oauth-token is invalid, it has to start with: "oauth:"')
@ -435,12 +576,16 @@ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(module)s %(thread
sys.tracebacklimit = 0
def main():
"""Main loop"""
global conf # pylint: disable=global-statement,invalid-name
conf = load_config()
lastreload = datetime.datetime.now()
logging.getLogger().setLevel(conf['LOG_LEVEL'])
if conf['LOG_LEVEL'] == 'DEBUG':
sys.tracebacklimit = 5
logging.info("Starting Webserver")
httpd = socketserver.TCPServer((conf['HTTP_BIND'], conf['HTTP_PORT']), HTTPserv)
httpd.allow_reuse_port = True
@ -449,50 +594,66 @@ def main():
http_thread = Thread(target=http_serve_forever, daemon=True, args=(httpd, ))
http_thread.start()
logging.info("Starting IRC bot")
irc = IRC()
irc.connect(conf['IRC_SERVER'], 6667, conf['IRC_CHANNEL'], conf['IRC_USERNAME'], conf['IRC_OAUTH_TOKEN'])
irc.sendpriv(conf['IRC_CHANNEL'], 'MrDestructoid', conf['MESSAGE']['READY'])
if conf['IRC_OAUTH_TOKEN'] == "Invalid":
logging.error('No OAuth Token, skipping start of IRC bot.')
logging.error('Please open http://%s:%s/token to generate your OAuth-Token.', conf['HTTP_BIND'], conf['HTTP_PORT'])
url = 'http://'+str(conf['HTTP_BIND'])+':'+str(conf['HTTP_PORT'])+'/token'
webbrowser.open_new_tab(url)
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()
logging.info("Please open your browser and visit: http://"+str(conf['HTTP_BIND']+":"+str(conf['HTTP_PORT'])+"/"))
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)
if not irc.tts_status:
logging.debug("TTS is disabled")
if conf['LOG_LEVEL'] == "DEBUG":
time.sleep(1)
continue
try:
irc.get_response()
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
logging.debug('Raw message queue:')
logging.debug(msg_queue_raw)
confreload = datetime.datetime.now()
if confreload - lastreload > datetime.timedelta(seconds=60):
conf = load_config()
lastreload = datetime.datetime.now()
for raw_msg in msg_queue_raw:
logging.debug('Raw msg:')
if irc.quickvote 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)
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()
for raw_msg in msg_queue_raw:
logging.debug('Raw msg:')
logging.debug(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)
else:
logging.debug('Msg is already in queue')
except KeyboardInterrupt:
logging.info('Exiting...')
sys.exit()
if __name__ == "__main__":
main()