def sub_gift(self, tags): """Send a message when someone gift a sub to another viewer.""" # Auto-message generated by Twitch - "XXX gifted a $x.xx sub to xxx!" logging.warning("New sub donation detected") logging.warning(tags["system-msg"]) # Setup message to be printed responses = self.config.responses["usernotice"]["subgift"] plan = responses["subplan"]["msg"] donor = tags["display-name"] if tags["display-name"] else tags["login"] recipient = (tags["msg-param-recipient-display-name"] if tags["msg-param-recipient-display-name"] else tags["msg-param-recipient-user-name"]) months = int(tags["msg-param-months"]) subtype = tags["msg-param-sub-plan"] if int(months) <= 1: var = { "<DONOR>": donor, "<RECIPIENT>": recipient, "<SUBPLAN>": plan[subtype], } reply = replace_vars(responses["msg_standard"]["msg"], var) else: var = { "<DONOR>": donor, "<RECIPIENT>": recipient, "<SUBPLAN>": plan[subtype], "<MONTHS>": months, } reply = replace_vars(responses["msg_with_months"]["msg"], var) self.write(reply)
def send_success_message(self, bot): """Send a message for a successful pyramid.""" points = self.calculate_points(bot) if len(points) == 1: user = self.pyramid_builders[0] var = { "<USER>": bot.twitch.display_name(user), "<PRONOUN0>": bot.config.pronoun(user)[0], "<AMOUNT>": points[user], } bot.write(replace_vars(self.responses["pyramid"]["msg"], var)) bot.ranking.increment_points(user, points[user], bot) else: s = format_list( list( map(lambda x: bot.twitch.display_name(x), list(points.keys()))) ) # calls bot.displayName on every user p = format_list(list(points.values())) var = {"<MULTIUSERS>": s, "<AMOUNT>": p} bot.write(replace_vars(self.responses["multi_pyramid"]["msg"], var)) for u in list(points.keys()): bot.ranking.increment_points(u, points[u], bot) self.reset()
def run(self, bot, user, msg, tag_info): """Get stream object and return requested information.""" responses = bot.config.responses["StreamInfo"] stream = bot.twitch.get_stream(bot.config.channelID) cmd = msg.lower() if cmd.startswith("!bttv"): var = { "<MULTIEMOTES>": emote_list_to_string(bot.emotes.get_channel_bttv_emotes()) } bot.write(replace_vars(responses["bttv_msg"]["msg"], var)) elif stream["stream"] is None: bot.write(responses["stream_off"]["msg"]) elif cmd.startswith("!fps"): fps = format(stream["stream"]["average_fps"], ".2f") var = {"<FPS>": fps} bot.write(replace_vars(responses["fps_msg"]["msg"], var)) elif cmd.startswith("!uptime"): created_at = stream["stream"]["created_at"] streamstart = twitch_time_to_datetime(created_at) now = datetime.utcnow() elapsed_time = now - streamstart seconds = int(elapsed_time.total_seconds()) hours = seconds // 3600 minutes = (seconds % 3600) // 60 seconds = seconds % 60 var = { "<HOURS>": hours, "<MINUTES>": minutes, "<SECONDS>": seconds } bot.write(replace_vars(responses["uptime"]["msg"], var))
def sub_message(self, tags, _): """Send a message when someone subscribes.""" # Sub message by user is in subMsg, which is not part of IRC tags # Auto-message generated by Twitch - "XXX has subscribed for n months" logging.warning("New sub detected") logging.warning(tags["system-msg"]) # Setup message to be printed by bot responses = self.config.responses["usernotice"]["sub"] plan = responses["subplan"]["msg"] # according to Twitch doc, display-name can be empty if it is never set user = tags["display-name"] if tags["display-name"] else tags["login"] months = tags["msg-param-months"] subtype = tags["msg-param-sub-plan"] if int(months) <= 1: var = {"<USER>": user, "<SUBPLAN>": plan[subtype]} reply = replace_vars(responses["msg_standard"]["msg"], var) else: var = { "<USER>": user, "<SUBPLAN>": plan[subtype], "<MONTHS>": months } reply = replace_vars(responses["msg_with_months"]["msg"], var) self.write(reply)
def successful_pleb_pyramid(self, bot): """Write messages and time out people on pleb pyramid.""" unique_users = list(set(self.pyramid_builders)) if len(unique_users) == 1: user = unique_users[0] if bot.get_permission(user) in [Permission.User, Permission.Subscriber]: var = { "<USER>": bot.twitch.display_name(user), "<PRONOUN0>": bot.config.pronoun(user)[0], } bot.write(replace_vars(self.responses["plebpyramid"]["msg"], var)) bot.timeout(user, 60) else: var = { "<USER>": bot.twitch.display_name(user), "<PRONOUN0>": bot.config.pronoun(user)[0], } bot.write(replace_vars(self.responses["mod_plebpyramid"]["msg"], var)) else: s = format_list( list(map(lambda x: bot.twitch.display_name(x), unique_users)) ) var = {"<MULTIUSERS>": s} bot.write(replace_vars(self.responses["multi_plebpyramid"]["msg"], var)) for u in unique_users: if bot.get_permission(u) in [Permission.User, Permission.Subscriber]: bot.timeout(u, 60) self.reset()
def increment_points(self, username, amount, bot): """Increment points of a user by a certain value. Check if the user reached legend in the process. """ username = username.lower() viewer_id = self._get_user_id(username) points = int(self.get_points(username, new_entry=True)) rank = self.get_hs_rank(points) legend = "Legend" in rank points += amount sql_command = "UPDATE points SET amount = ? WHERE viewer_id = ?;" self.execute_command(sql_command, [points, viewer_id]) """Check for legend rank if user was not legend before.""" if not legend: rank = self.get_hs_rank(points) if "Legend" in rank: var = { "<USER>": bot.twitch.display_name(username), "<RANK>": rank } bot.write( replace_vars( bot.config.responses["ranking"]["msg_legend"]["msg"], var))
def run(self, bot, user, msg, tag_info): """Write out total or minute stats of an emote.""" self.responses = bot.config.responses["outputStats"] cmd = msg.strip().lower() if cmd.startswith("!total "): emote = self._second_word(msg) count = bot.ecount.get_total_count(emote) response = self.responses["total_reply"]["msg"] elif cmd.startswith("!minute "): emote = self._second_word(msg) count = bot.ecount.get_minute_count(emote) response = self.responses["minute_reply"]["msg"] elif cmd == "!tkp": emote = "Kappa" count = bot.ecount.get_total_count(emote) response = self.responses["total_reply"]["msg"] elif cmd == "!kpm": emote = "Kappa" count = bot.ecount.get_minute_count(emote) response = self.responses["minute_reply"]["msg"] else: return var = {"<EMOTE>": emote, "<AMOUNT>": count} bot.write(replace_vars(response, var))
def run(self, bot, user, msg, tag_info): """Calculate rank of user. 0-19: Rank 25, 20-39: Rank 24,..., 480-499: Rank 1 >= LEGENDP: Legend """ self.responses = bot.config.responses["Rank"] split_msg = msg.split(" ") if len(split_msg) == 2: # !rank <name> user = sanitize_user_name(split_msg[1]) else: # !rank user = sanitize_user_name(user) try: points = bot.ranking.get_points(user) var = { "<USER>": bot.twitch.display_name(user), "<RANK>": bot.ranking.get_hs_rank(points), "<POINTS>": points, } bot.write(replace_vars(self.responses["display_rank"]["msg"], var)) except UserNotFoundError: bot.write(self.responses["user_not_found"]["msg"])
def _winner_message(self, obj, user): var = { "<USER>": self.bot.twitch.display_name(user), "<MINION>": obj["name"], "<PRONOUN0>": self.bot.config.pronoun(user)[0].capitalize(), "<AMOUNT>": self.points, } return replace_vars(self.responses["winner_msg"]["msg"], var)
def delcommand(self, bot, cmd): """Delete an existing command from the list.""" entrycmd = cmd[len("!delcommand ") :] entrycmd.strip() if entrycmd in self.replies: del self.replies[entrycmd] with open(REPLIES_FILE.format(bot.root), "w", encoding="utf-8") as file: json.dump(self.replies, file, indent=4, ensure_ascii=False) bot.reload_commands() # Needs to happen to refresh the list. var = {"<COMMAND>": entrycmd} bot.write(replace_vars(self.responses["cmd_removed"]["msg"], var)) else: var = {"<COMMAND>": entrycmd} bot.write(replace_vars(self.responses["cmd_not_found"]["msg"], var))
def _set_hint(self, obj): self.statToSet = self.responses["setnames"]["msg"] if obj["set"] in self.statToSet: setname = self.statToSet[obj["set"]] else: setname = str(obj["set"]) var = {"<STAT>": setname} return replace_vars(self.responses["clue_set"]["msg"], var)
def run(self, bot, user, msg, tag_info): """Donate spampoints to the target and remove them from the initiator.""" bot.antispeech = True cmd = msg.lower().strip().split(" ") target = cmd[1].lower().strip() amount = int(cmd[2].lower().strip()) """Check when the user tipped last.""" if user in self.tiptimer.keys(): if (time.time() - self.tiptimer[user]) < self.tipcooldown: timer = int(self.tipcooldown - time.time() + self.tiptimer[user]) cooldown = int(self.tipcooldown / 60) var = { "<USER>": user, "<TIMER>": timer, "<COOLDOWN>": cooldown } bot.write(replace_vars(self.responses["cooldown"]["msg"], var)) return """Only allow integers between mintip and maxtip.""" if amount < self.mintip or amount > self.maxtip: var = {"<MINTIP>": self.mintip, "<MAXTIP>": self.maxtip} bot.write(replace_vars(self.responses["numbererror"]["msg"], var)) return """If the user has enough points transfer them to the target and set tiptimer.""" if bot.ranking.get_points(user) >= amount: bot.ranking.increment_points(user, -amount, bot) bot.ranking.increment_points(target, amount, bot) typeemote = self.get_type_emote(amount) var = { "<USER>": user, "<TARGET>": target, "<AMOUNT>": amount, "<TYPE>": typeemote, } bot.write(replace_vars(self.responses["tipsend"]["msg"], var)) self.tiptimer[user] = time.time() else: var = {"<USER>": user, "<AMOUNT>": amount} bot.write(replace_vars(self.responses["notenough"]["msg"], var))
def run(self, bot, user, msg, tag_info): """Generate a random number n when game gets first started. Afterwards, check if a message contains the emote n times.""" self.responses = bot.config.responses["KappaGame"] cmd = msg.strip() if not self.active: self.active = True self.n = random.randint(1, 25) self.answered = [] print("Kappas: "******"start_msg"]["msg"]) else: if msg == "!kstop" and bot.get_permission(user) not in [ Permission.User, Permission.Subscriber, ]: self.close(bot) bot.write(self.responses["stop_msg"]["msg"]) return i = self.count_emotes(cmd, "Kappa") if i == self.n: var = { "<USER>": bot.twitch.display_name(user), "<AMOUNT>": self.n } bot.write( replace_vars(self.responses["winner_msg"]["msg"], var)) bot.ranking.increment_points(user, self.kappa_game_points, bot) bot.game_running = False self.active = False self.answered = [] elif i != -1: if i not in self.answered: var = {"<AMOUNT>": i} bot.write( replace_vars(self.responses["wrong_amount"]["msg"], var)) self.answered.append(i)
def start_game(bot, user, msg, cmd): """Return whether a user can start a game. Takes off points if a non moderator wants to start a game. Also makes sure only one game is running at a time. """ responses = bot.config.responses["startGame"] if bot.game_running: return False elif (bot.get_permission(user) in [Permission.User, Permission.Subscriber] and msg == cmd): """Check if pleb_gametimer is not on cooldown.""" if (time.time() - bot.last_plebgame) > bot.config.pleb_gametimer: # The calling user is not a mod, so we subtract 5 points. if bot.ranking.get_points( user) > bot.config.config["points"]["game_start"]: bot.last_plebgame = time.time() # Set pleb_gametimer bot.ranking.increment_points( user, -int(bot.config.config["points"]["game_start"]), bot) bot.game_running = True return True else: var = { "<AMOUNT>": int(bot.config.config["points"]["game_start"]) } bot.write(replace_vars(responses["points_needed"]["msg"], var)) return False else: t = bot.config.pleb_gametimer - time.time() + bot.last_plebgame next_plebgame = "%8.0f" % t var = {"<COOLDOWN>": next_plebgame} bot.write( replace_vars(responses["plebgames_on_cooldown"]["msg"], var)) else: # The calling user is a mod, so we only check if the command is correct if msg == cmd: bot.game_running = True return msg == cmd
def run(self, bot, user, msg, tag_info): """Evaluate second part of message and write the result.""" self.responses = bot.config.responses["Calculator"] expr = msg.split(" ", 1)[1] try: result = self.nsp.eval(expr) # This condition fixes floating point errors, like 0.1 + 0.2 = 0.300...004 # Rounds to <PRECISION> digits after the first non zero digit # E.g.: 0.30000004 -> 0.3 # 0.000030030004 -> 0.00003003 if abs(result) < 1: dist = abs( int(math.log10(abs(result))) ) # How many zeroes are after ., before a non zero digit result = round(result, PRECISION + dist) else: result = round(result, PRECISION) reply = "{} = {}".format(expr, result) bot.write(reply) except ZeroDivisionError: var = {"<USER>": bot.twitch.display_name(user)} bot.write(replace_vars(self.responses["div_by_zero"]["msg"], var)) except OverflowError: var = {"<USER>": bot.twitch.display_name(user)} bot.write( replace_vars(self.responses["number_overflow"]["msg"], var)) except pyparsing.ParseException: var = {"<USER>": bot.twitch.display_name(user)} bot.write(replace_vars(self.responses["wrong_input"]["msg"], var)) except (TypeError, ValueError): # Not sure which Errors might happen here. var = { "<USER>": bot.twitch.display_name(user), "<EXPRESSION>": expr } bot.write(replace_vars(self.responses["default_error"]["msg"], var))
def run(self, bot, user, msg, tag_info): """Add or delete a mod.""" self.responses = bot.config.responses["EditCommandMods"] mod = msg.split(" ")[1].lower() if msg.startswith("!addmod "): if mod not in bot.config.trusted_mods: bot.config.trusted_mods.append(mod) bot.write(self.responses["mod_added"]["msg"]) else: var = {"<USER>": mod} bot.write( replace_vars(self.responses["already_mod"]["msg"], var)) elif msg.startswith("!delmod "): if mod in bot.config.trusted_mods: bot.config.trusted_mods.remove(mod) bot.write(self.responses["mod_deleted"]["msg"]) else: var = {"<USER>": mod} bot.write( replace_vars(self.responses["user_not_in_list"]["msg"], var)) bot.config.write_trusted_mods()
def run(self, bot, user, msg, tag_info): """Initalize the command on first run. Check for right emote for each new msg.""" self.responses = bot.config.responses["GuessEmoteGame"] cmd = msg.strip() if not self.active: self.active = True self.init_game(bot, msg) print("Right emote: " + self.emote) var = {"<MULTIEMOTES>": emote_list_to_string(self.emotes)} bot.write(replace_vars(self.responses["start_msg"]["msg"], var)) else: if cmd == "!estop" and bot.get_permission(user) not in [ Permission.User, Permission.Subscriber, ]: bot.write(self.responses["stop_msg"]["msg"]) self.close(bot) return if cmd == self.emote: var = { "<USER>": bot.twitch.display_name(user), "<EMOTE>": self.emote, "<PRONOUN0>": bot.config.pronoun(user)[0].capitalize(), "<AMOUNT>": self.emote_game_points, } bot.write( replace_vars(self.responses["winner_msg"]["msg"], var)) bot.ranking.increment_points(user, self.emote_game_points, bot) bot.game_running = False self.active = False elif cmd == "!emotes": var = {"<MULTIEMOTES>": emote_list_to_string(self.emotes)} bot.write(replace_vars(self.responses["emote_msg"]["msg"], var))
def game_winners(self, bot): """Announce game winners and give points.""" s = self.responses["game_over1"]["msg"] winners = self.monkalotparty.topranks() if winners is None: s += self.responses["game_over2"]["msg"] else: s += format_list(winners[0]) + " " for i in range(0, len(winners[0])): bot.ranking.increment_points(winners[0][i], winners[2], bot) var = {"<GAME_POINTS>": winners[1], "<USER_POINTS>": winners[2]} s += replace_vars(self.responses["game_over3"]["msg"], var) bot.write(s)
def incoming_raid(self, tags): """Send a message when channel got raided.""" # NOTE: raid seems to have no custom message currently, so msg is not passed # log raid? logging.warning("New raid detected") logging.warning(tags["system-msg"]) amount = int(tags["msg-param-viewerCount"]) channel = tags["msg-param-displayName"] if amount < self.config.raid_announce_treshold: return responses = self.config.responses["usernotice"]["raid"] var = {"<AMOUNT>": amount, "<CHANNEL>": channel} reply = replace_vars(responses["msg"], var) self.write(reply)
def match(self, bot, user, msg, tag_info): """Match if command is !tip <chatter>.""" if msg.lower().strip().startswith("!tip "): cmd = msg.split(" ") if len(cmd) == 3: target = cmd[1].lower().strip() tip_arg = cmd[2].lower().strip() """Check if tip_arg is an integer.""" try: int(tip_arg) except ValueError: var = {"<MINTIP>": self.mintip, "<MAXTIP>": self.maxtip} bot.write( replace_vars(self.responses["numbererror"]["msg"], var)) return False """Check if user is in chat and not trying to tip himself.""" if target in bot.users and target != user.lower(): return True return False
def handle_answer(self, cmd, user, bot): """Handle answer by user.""" if (self.answer not in bot.emotes.get_emotes() ): # If not an emote compare in lowercase. self.answer = self.answer.lower() cmd = cmd.lower() if cmd == self.answer: var = { "<USER>": bot.twitch.display_name(user), "<ANSWER>": self.answer, } bot.write(replace_vars(self.responses["winner_msg"]["msg"], var)) self.answer = "" bot.ranking.increment_points(user, 5, bot) self.monkalotparty.uprank(user) if len(self.monkalotparty.games) > 3: self.callID = reactor.callLater(6, self.select_game, bot) else: self.game_winners(bot) self.close(bot)
def run(self, bot, user, msg, tag_info): """Try to put/remove a user on/from the ignore list.""" bot.antispeech = True cmd = msg.lower().strip().split(" ") target = cmd[1].lower().strip() reply = {} if cmd[0].strip() == "!ignore": ignore_reply = self.responses["ignore"] # bot can ignore ANYONE, we just add the name to bot.config.ignored_users # IMPORTANT: ANYONE includes owner, mod and the bot itself, we do the checking here to prevent it if (target == bot.config.nickname) or any( target in coll for coll in (bot.config.owner_list, bot.config.trusted_mods) ): reply = ignore_reply["privileged"] elif target in bot.config.ignored_users: # already ignored reply = ignore_reply["already"] else: bot.config.ignored_users.append(target) reply = ignore_reply["success"] # To make the change temporary (before bot reboot) comment out next line bot.config.write_ignored_users() elif cmd[0].strip() == "!unignore": unignore_reply = self.responses["unignore"] if target in bot.config.ignored_users: bot.config.ignored_users.remove(target) reply = unignore_reply["success"] # To make the change temporary (before bot reboot) comment out next line bot.config.write_ignored_users() else: reply = unignore_reply["already"] var = {"<USER>": bot.config.twitch.display_name(target)} output = replace_vars(reply.get("msg"), var) bot.write(output)
def addcommand(self, bot, cmd): """Add a new command to the list, make sure there are no duplicates.""" tailcmd = cmd[len("!addcommand ") :] tailcmd.strip() """Add all commands in lower case, so no case-sensitive duplicates exist.""" entrycmd = tailcmd.split(" ", 1)[0].lower().strip() entryarg = tailcmd.split(" ", 1)[1].strip() """Check if the command is already in the list, if not add the command to the list""" if entrycmd in self.replies: bot.write(self.responses["cmd_already_exists"]["msg"]) else: self.replies[entrycmd] = entryarg with open(REPLIES_FILE.format(bot.root), "w", encoding="utf-8") as file: json.dump(self.replies, file, indent=4, ensure_ascii=False) bot.reload_commands() # Needs to happen to refresh the list. var = {"<COMMAND>": entrycmd} bot.write(replace_vars(self.responses["cmd_added"]["msg"], var))
def run(self, bot, user, msg, tag_info): """Output emote message if cmd matches.""" self.responses = bot.config.responses["EmoteReply"] if self.cmd == "!call": var = {"<EMOTE>": self.emote} s = replace_vars(self.responses["call_reply"]["msg"], var) parsetext = self.text.split(" ") for i in range(0, len(parsetext)): s += " " + parsetext[i].upper() + " " + self.emote elif self.cmd == "!any": parsetext = self.text.split(" ") s = self.emote for i in range(0, len(parsetext)): s += " " + parsetext[i].upper() + " " + self.emote elif self.cmd == "!word": parsetext = list(self.text) s = self.emote for i in range(0, len(parsetext)): s += " " + parsetext[i].upper() + " " + self.emote else: return bot.write(s)
def _rarity_hint(self, obj): var = {"<STAT>": obj["rarity"]} return replace_vars(self.responses["clue_rarity"]["msg"], var)
def _cardclass_hint(self, obj): var = {"<STAT>": str(obj["cardClass"]).lower()} return replace_vars(self.responses["clue_stat"]["msg"], var)
def _name_hint(self, obj): var = {"<STAT>": obj["name"][0]} return replace_vars(self.responses["clue_letter"]["msg"], var)
def _attack_hint(self, obj): var = {"<STAT>": obj["attack"]} return replace_vars(self.responses["clue_attackpower"]["msg"], var)
def _cost_hint(self, obj): var = {"<STAT>": obj["cost"]} return replace_vars(self.responses["clue_manacost"]["msg"], var)
def _health_hint(self, obj): if obj["health"] == 1: var = {"<STAT>": obj["health"], "<PLURAL>": ""} else: var = {"<STAT>": obj["health"], "<PLURAL>": "s"} return replace_vars(self.responses["clue_healthpoints"]["msg"], var)