def __init__(self): if not hasattr(botconfig, "igdbAuthKey"): ptf("igdbAuthKey not found in botconfig") if not hasattr(botconfig, "steamAuthKey"): ptf("steamAuthKey not found in botconfig") self.igdbHeader = { "user-key" : botconfig.igdbAuthKey, "Accept" : "application/json" } params = { "key" : botconfig.steamAuthKey } response = requests.get("https://api.steampowered.com/ISteamApps/GetAppList/v2/", params=params) data = response.json() self.steamApps = {} if response.ok: self.steamApps = { app["name"] : app["appid"] for app in data["applist"]["apps"] } self.activeCommands = { # snippet start # findgame (TEXT) # findgame # findgame Silent Hill # Remarks # findgame on its own will search for the currently played game. "findgame" : self.ExecuteFindGame, } self.findGameRegex = re.compile(f"^findgame {groups.text}$")
def CheckGetAccessToken(self): response = requests.get( f"https://api.spotify.com/v1/tracks/11dFghVXANMlKmJXsNCbNl", headers=self.spotifyHeader) if not response.ok: response = requests.post("https://accounts.spotify.com/api/token", headers=self.authHeader, data=self.authBody) data = response.json() ptf(data) self.spotifyHeader[ "Authorization"] = f"Bearer {data['access_token']}"
def add_capability(self, capability): assert type(capability) in [list, str] self.capability = capability if type(capability) == list: for cap in capability: self.send_req(cap.lower()) else: self.send_req(capability.lower()) ptf("") ptf("Connected to Twitch IRC\n", time=True) self.send_message("Hi chat. I'm back! :)") self.conn.settimeout(330)
def _send(self, command, message): if (sent := self.conn.send(bytes("{}{}\r\n".format(command, message), 'UTF-8'))) == 0: ptf("Socket connection broken, sent is 0. Reconnecting", time=True) self.conn.shutdown(socket.SHUT_WR) self._initialize_websocket() if len(self.nick) > 0: self.login(self.nick, self.auth) if len(self.chan) > 1: self.join_channel(self.chan) if self.capability is not None: self.add_capability(self.capability)
def __init__(self): self.header = {"Accept": "application/json"} if not hasattr(botconfig, "spoonacularAuthKey"): ptf("spoonacularAuthKey not found in botconfig") self.params = {"apiKey": botconfig.spoonacularAuthKey} self.activeCommands = { # snippet start # findfood TEXT # findfood burger # findfood chicken alfredo "findfood": self.ExecuteFindFood, } self.findFoodRegex = re.compile(f"^findfood {groups.text}$")
def CheckPingAsync(self): while not self.stopped(): time.sleep(10) if (datetime.datetime.now() - self.lastPingTime).total_seconds() > 330: ptf("No PING. Reconnecting", time=True) self.conn.shutdown(socket.SHUT_WR) self._initialize_websocket() if len(self.nick) > 0: self.login(self.nick, self.auth) if len(self.chan) > 1: self.join_channel(self.chan) if self.capability is not None: self.add_capability(self.capability)
def _initialize_websocket(self): while True: try: self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # We set the timeout to 330 seconds, as the PING from the Twitch server indicating # That the connection is still live is sent roughly every 5 minutes it seems. # the extra 30 seconds prevents us from checking the connection when it's not # needed. #TODO Error handle socket.gaierror: [Errno 11001] getaddrinfo failed and other errors self.conn.settimeout(10) self.conn.connect( (self.host, self.port) ) self.lastPingTime = datetime.datetime.now() return except socket.gaierror: # Sleep and retry if not successful ptf("Failed to connect socket. Sleeping and retrying...", time=True) time.sleep(5)
def run(self): while not self.stopped(): try: try: # Receive data from Twitch Websocket. packet = self.conn.recv(8192).decode('UTF-8') except UnicodeDecodeError: ptf("Received bad packet", time=True) # In case of unexpected end of data. continue self.data += packet data_split = self.data.split("\r\n") self.data = data_split.pop() # Iterate over seperately sent data. for line in data_split: m = Message(line) # We will do some handling depending on the message ourself, # so the developer doesn't have to. if m.type == "PING": self.send_pong() self.lastPingTime = datetime.datetime.now() self.callback(m) except OSError as e: ptf(f"OSError: {e} -> {line}", time=True) self._initialize_websocket() if len(self.nick) > 0: self.login(self.nick, self.auth) if len(self.chan) > 1: self.join_channel(self.chan) if self.capability is not None: self.add_capability(self.capability)
def message_handler(self, m): # Check for proper message type if (m.type != "PRIVMSG" and m.type != "WHISPER"): return # Check for valid message with prefix and valid rewards validReward = "custom-reward-id" in m.tags validCommand = m.message != None and len( m.message) > 1 and m.message[0] in self.prefixes if (not validReward and not validCommand): return try: if validReward: util.LogReceived(m.type, m.user, m.message, m.tags) util.SendMessage(self.redeem[m.tags["custom-reward-id"]](m), m.type, m.user) if validCommand: # Retrieve first word without prefix m.message = m.message[1:] token = m.message.lower().split()[0] if (token in self.execute): util.LogReceived(m.type, m.user, m.message, m.tags, True) util.SendMessage(self.execute[token](m), m.type, m.user) return # Simple response commands # Note that we don't get this far unless the message does not match other commands response = self.commands["CustomCommands"].Execute(m) if response != None: util.SendMessage(response, m.type, m.user) return except Exception as e: ptf(f"Error: {e}", time=True)
def __init__(self): if not hasattr(botconfig, "spotifyIdAndSecret"): ptf("spotifyIdAndSecret not found in botconfig") self.authHeader = { "Authorization": f"Basic {base64.urlsafe_b64encode(botconfig.spotifyIdAndSecret.encode('utf-8')).decode('utf-8')}" } self.authBody = {"grant_type": "client_credentials"} self.spotifyHeader = { "Authorization": "Bearer [Invalid]", "Accept": "application/json" } self.activeCommands = { # snippet start # findsong TEXT # findsong Piano Man # findsong Killer Queen "findsong": self.ExecuteFindSong, } self.findSongRegex = re.compile(f"^findsong {groups.text}$")
def __init__(self, chan, mongoClient): self.colLeaderboard = mongoClient.purebotdb[chan + "Leaderboards"] self.colLeaderboard.create_index([("user", pymongo.ASCENDING)]) self.colLeaderboard.create_index([("score", pymongo.ASCENDING)]) if not hasattr(botconfig, "scoreLifespan"): ptf("scoreLifespan not found in botconfig") if not hasattr(botconfig, "clearScoreId"): ptf("clearScoreId not found in botconfig") if not hasattr(botconfig, "stealScoreId"): ptf("stealScoreId not found in botconfig") if not hasattr(botconfig, "swapScoreId"): ptf("swapScoreId not found in botconfig") # Set expiration timer on collection documents self.colLeaderboard.create_index( [("_ts", pymongo.ASCENDING)], expireAfterSeconds=botconfig.scoreLifespan) self.scoreMin = -1 self.scoreMax = 101 self.activeCommands = { # snippet start # purecount "purecount": self.ExecutePureCount, # snippet start # pureboard "pureboard": self.ExecutePureBoard, # snippet start # curseboard "curseboard": self.ExecuteCurseBoard, # snippet start # clearboard # remarks # Mod Only. Clears leaderboard. "clearboard": self.ExecuteClearBoard, # snippet start # stealscore USER # stealscore BabotzInc # remarks # Requires spending sushi rolls. Only needed when you don't provide a name in the reward message. "stealscore": self.ExecuteStealScore, # snippet start # swapscore USER # swapscore BabotzInc # remarks # Requires spending sushi rolls. Only needed when you don't provide a name in the reward message. "swapscore": self.ExecuteSwapScore, } self.activeRewards = { botconfig.clearScoreId: self.RedeemClearScore, botconfig.stealScoreId: self.RedeemStealScore, botconfig.swapScoreId: self.RedeemSwapScore, } self.rewardStealId = "stealScore" self.rewardSwapId = "swapScore" self.redeemStealSwapScoreRegex = re.compile(f"^{groups.user}") self.stealScoreRegex = re.compile(f"^stealscore {groups.user}") self.swapScoreRegex = re.compile(f"^swapscore {groups.user}")
return f"[{msg.user}]: Only mods and subs can add a quote" if (regMatch := self.quoteAddRegex.match(msg.message)) is None: return util.GetSyntax(msg.user, "quote add TEXT") try: result = self.colCounters.find_one_and_update( {"name": self.counterName}, {"$inc": { "value": 1 }}, upsert=True) except TimeoutError: return f"[{msg.user}]: Server took too long" if result == None: ptf(f"Counter not found for {self.counterName}") return f"[{msg.user}]: Unable to add quote" quoteId = result["value"] if (gameName := GetGame()) is None: gameName = "[Unknown Game]" quoteText = regMatch.group("text") quoteText.strip("\"'") try: self.colQuotes.insert_one({ "id": quoteId, "user": msg.user, "text": quoteText,
def __init__(self, host, port, chan, nick, auth, callback, capability = None, live = False): assert type(host) == str and type(port) == int and (type(callback) == types.FunctionType or type(callback) == types.MethodType) threading.Thread.__init__(self) self.pingThread = threading.Thread(target=self.CheckPingAsync) self.name = "TwitchWebsocket" self._stop_event = threading.Event() # Variables self.host = host self.port = port self.chan = chan self.nick = nick self.auth = auth self.live = live self.callback = callback self.capability = capability self.data = str() # Lambda Functions. self.send_join = lambda message, command="JOIN ": self._send(command, message) self.send_pong = lambda message="", command="PONG ": self._send(command, message) self.send_ping = lambda message="", command="PING ": self._send(command, message) self.send_nick = lambda message, command="NICK ": self._send(command, message) self.send_pass = lambda message, command="PASS ": self._send(command, message) self.send_part = lambda message, command="PART ": self._send(command, message) self.send_req = lambda message, command="CAP REQ :twitch.tv/": self._send(command, message) self.send_message = lambda message, command="PRIVMSG ": self._send("{}{} :".format(command, self.chan.lower()), message) if self.live else ptf(message) self.send_whisper = lambda sender, message: self.send_message(f"/w {sender} {message}")