def status_duel(self, bot, source, **rest): """ Whispers you the current status of your active duel requests/duel targets How to use: !duelstatus """ with DBManager.create_session_scope() as db_session: msg = [] if source.id in self.duel_requests: duelling = User.find_by_id(db_session, self.duel_requests[source.id]) msg.append( f"You have a duel request for {self.duel_request_price[source.id]} points by {duelling}" ) if source.id in self.duel_targets: challenger = User.find_by_id(db_session, self.duel_targets[source.id]) msg.append( f"You have a pending duel request from {challenger} for {self.duel_request_price[self.duel_targets[source.id]]} points" ) if len(msg) > 0: bot.whisper(source, ". ".join(msg)) else: bot.whisper( source, "You have no duel request or duel target. Type !duel USERNAME POT to duel someone!" )
def _cancel_expired_duels(self) -> None: if self.bot is None: log.warn( "_cancel_expired_duels of DuelModule failed because bot is None" ) return now = utils.now() for source_id, started_at in self.duel_begin_time.items(): duel_age = now - started_at if duel_age <= timedelta(minutes=self.settings["max_duel_age"]): # Duel is not too old continue with DBManager.create_session_scope() as db_session: source = User.find_by_id(db_session, source_id) if source is None: continue target_id = self.duel_requests[source.id] del self.duel_targets[target_id] del self.duel_requests[source.id] del self.duel_request_price[source.id] del self.duel_begin_time[source.id] challenged = User.find_by_id(db_session, target_id) if challenged is None: continue self.bot.whisper( source, f"{challenged} didn't accept your duel request in time, so the duel has been cancelled." )
def _handle_command(self, bot, source, message, event, format_cb, message_method): from_input, to_input = self._parse_message(message) with DBManager.create_session_scope(expire_on_commit=False) as db_session: if from_input is not None: from_user = User.find_or_create_from_user_input(db_session, bot.twitch_helix_api, from_input) if from_user is None: bot.execute_now( bot.send_message_to_user, source, f'User "{from_input}" could not be found', event, method=self.settings["action_followage"], ) return else: from_user = source if to_input is None: to_input = bot.streamer # TODO make bot.streamer a User() instance? to_user = User.find_or_create_from_user_input(db_session, bot.twitch_helix_api, to_input) if to_user is None: bot.execute_now( bot.send_message_to_user, source, f'User "{to_input}" could not be found', event, method=message_method, ) return follow_since = bot.twitch_helix_api.get_follow_since(from_user.id, to_user.id) # is_self = source == from_user to_user = to_user.name.replace("AdmiralBulldog", "AdmeralllBuldogg") if follow_since is not None: # Following suffix = f"been following {to_user} {format_cb(follow_since)}" # if is_self: # message = "You have " + suffix # else: message = from_user.name + " has " + suffix else: # Not following suffix = f"not following {to_user}" # if is_self: # message = "You are " + suffix # else: message = from_user.name + " is " + suffix bot.execute_now(bot.send_message_to_user, source, message, event, method=message_method)
def _handle_command(self, bot, source, message, event, format_cb, message_method): from_input, to_input = self._parse_message(message) with DBManager.create_session_scope(expire_on_commit=False) as db_session: if from_input is not None: from_user = User.find_or_create_from_user_input(db_session, bot.twitch_helix_api, from_input) if from_user is None: bot.execute_now( bot.send_message_to_user, source, f'User "{from_input}" could not be found', event, method=self.settings["action_followage"], ) return else: from_user = source if to_input is None: to_input = bot.streamer.login to_user = User.find_or_create_from_user_input(db_session, bot.twitch_helix_api, to_input) if to_user is None: bot.execute_now( bot.send_message_to_user, source, f'User "{to_input}" could not be found', event, method=message_method, ) return follow_since = bot.twitch_helix_api.get_follow_since(from_user.id, to_user.id) is_self = source == from_user if follow_since is not None: # Following suffix = f"been following {to_user} {format_cb(follow_since)}" if is_self: message = f"You have {suffix}" else: message = f"{from_user.name} has {suffix}" else: # Not following suffix = f"not following {to_user}" if is_self: message = f"You are {suffix}" else: message = f"{from_user.name} is {suffix}" bot.execute_now(bot.send_message_to_user, source, message, event, method=message_method)
def on_event(self, data): if data["type"] == "donation": try: sub_data = data["message"][0] amount = float(str(sub_data["amount"])) username = str(sub_data["from"]) currency = str(sub_data["currency"]) c = CurrencyConverter() log.info(username) amount = round(c.convert(amount, currency, "USD"), 2) with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if user is not None: log.info(f"User {user} donated ${amount}") HandlerManager.trigger("on_donate", user=user, amount=amount) except Exception as e: log.error(e) if "message" in data and "event" in data["message"]: if data["message"][ "event"] == "pop": # new song request pause : pause spotify username = data["message"]["media"]["action_by"] title = data["message"]["media"]["media_title"] HandlerManager.trigger("pause_spotify", title=title) elif data["message"]["event"] == "play": # on resume: HandlerManager.trigger("change_state", state=False) elif data["message"]["event"] == "pause": # on pause: HandlerManager.trigger("change_state", state=True) elif (data["message"]["event"] == "next" and data["message"]["media"] is None): # no new songs requested : resume spotify HandlerManager.trigger("resume_spotify")
def updatePoints(self, usdPoints, args): if args[0]["type"] != "donation": return False detailedArgs = args[0]["message"][0] if "historical" in detailedArgs: return False with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, detailedArgs["name"]) if user is None: return False usdAmount = self.currencyConverter.convert( float(detailedArgs["amount"]), detailedArgs["currency"], "USD") finalValue = int(usdAmount * int(usdPoints)) user.points = user.points + finalValue self.bot.whisper( user, f"You have been given {finalValue} points due to a donation in your name" )
def decline_duel(self, bot: Bot, source: User, **options: Any) -> None: """ Declines any active duel requests you've received. How to use: !decline """ if source.id not in self.duel_targets: bot.whisper(source, "You are not being challenged to a duel") return with DBManager.create_session_scope() as db_session: requestor = User.find_by_id(db_session, self.duel_targets[source.id]) if not requestor: bot.whisper( source, "Your challenge never existed, don't ask me what happened!" ) return bot.whisper(source, f"You have declined the duel vs {requestor}") bot.whisper(requestor, f"{source} declined the duel challenge with you.") del self.duel_targets[source.id] del self.duel_requests[requestor.id] del self.duel_request_price[requestor.id] del self.duel_begin_time[requestor.id]
def on_usernotice(self, chatconn, event): tags = { tag["key"]: tag["value"] if tag["value"] is not None else "" for tag in event.tags } if event.target != self.channel: return id = tags["user-id"] login = tags["login"] name = tags["display-name"] with DBManager.create_session_scope( expire_on_commit=False) as db_session: source = User.from_basics(db_session, UserBasics(id, login, name)) if event.arguments and len(event.arguments) > 0: msg = event.arguments[0] else: msg = None # e.g. user didn't type an extra message to share with the streamer with new_message_processing_scope(self): HandlerManager.trigger("on_usernotice", source=source, message=msg, tags=tags) if msg is not None: self.parse_message(msg, source, event, tags)
def on_clearchat(self, chatconn, event): tags = {tag["key"]: tag["value"] if tag["value"] is not None else "" for tag in event.tags} # Ignore "Chat has been cleared by a moderator" messages if "target-user-id" not in tags: return target_user_id = tags["target-user-id"] with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, target_user_id) if user is None: # User is not otherwise known, we won't store their timeout (they need to type first) # We could theoretically also do an API call here to figure out everything about that user, # but that could easily overwhelm the bot when lots of unknown users are banned quickly (e.g. bots). return if "ban-duration" in tags: # timeout ban_duration = int(tags["ban-duration"]) user.timeout_end = utils.now() + datetime.timedelta(seconds=ban_duration) else: # permaban # this sets timeout_end to None user.timed_out = False
def request_function(self, video_id, requested_by, queue=None): if not self.module_state["enabled"]: return False with DBManager.create_session_scope() as db_session: requested_by = User.find_by_user_input(db_session, requested_by) if not requested_by: return False requested_by_id = requested_by.id song_info = SongRequestSongInfo._create_or_get( db_session, video_id, self.youtube) if not song_info: log.error("There was an error!") return False skip_after = (self.settings["max_song_length"] if song_info.duration > self.settings["max_song_length"] else None) song = SongrequestQueue._create(db_session, video_id, skip_after, requested_by_id) if queue: song._move_song(queue) db_session.commit() current_song = SongrequestQueue._from_id(db_session, self.current_song_id) if not current_song or not current_song.requested_by: self.load_song() return True
def end_raffle(self): if not self.raffle_running: return False self.raffle_running = False if len(self.raffle_users) == 0: self.bot.me("Wow, no one joined the raffle DansGame") return False with DBManager.create_session_scope() as db_session: winner_id = random.choice(list(self.raffle_users)) winner = User.find_by_id(db_session, winner_id) if winner is None: return False self.raffle_users = set() if self.settings["show_on_clr"]: self.bot.websocket_manager.emit( "notification", { "message": f"{winner} {format_win(self.raffle_points)} points in the raffle!" }) self.bot.me( f"The raffle has finished! {winner} {format_win(self.raffle_points)} points! PogChamp" ) winner.points += self.raffle_points HandlerManager.trigger("on_raffle_win", winner=winner, points=self.raffle_points)
def points_reset(bot, source, message, **options): if message is None or len(message) == 0: return username = message.split(" ")[0] if len(username) < 2: return with DBManager.create_session_scope() as db_session: victim = User.find_by_user_input(db_session, username) if victim is None: bot.whisper(source, "This user does not exist FailFish") return if victim.points >= 0: bot.whisper(source, f"{victim} doesn't have negative points FailFish") return if victim.points <= -1: old_points = victim.points victim.points = 0 bot.whisper( source, f"You changed the points for {victim} from {old_points} to {victim.points} points" )
def set_points(self, bot, source, message, **rest): if not message: return False msg_split = message.split(" ") if len(msg_split) < 2: # The user did not supply enough arguments bot.whisper(source, f"Usage: !{self.command_name} USERNAME POINTS") return False username = msg_split[0] if len(username) < 2: # The username specified was too short. ;-) return False try: num_points = int(msg_split[1]) except (ValueError, TypeError): # The user did not specify a valid integer for points bot.whisper(source, f"Invalid amount of points. Usage: !{self.command_name} USERNAME POINTS") return False with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if not user: bot.whisper(source, "This user does not exist FailFish") return False user.points = num_points bot.whisper(source, f"Successfully set {user}'s points to {num_points}.")
def unpermaban_command(self, bot, source, message, **rest): if not message: return username = message.split(" ")[0] with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if not user: bot.whisper(source, "No user with that name found.") return False if user.banned is False: bot.whisper(source, "User is not permabanned.") return False user.banned = False log_msg = f"{user} is no longer permabanned" bot.whisper(source, log_msg) AdminLogManager.add_entry("Permaban remove", source, log_msg) if self.settings["unban_from_chat"] is True: bot.unban(user) if self.settings["enable_send_timeout"] is True: bot.timeout( user, 1, self.settings["timeout_reason"].format(source=source), once=True)
def permaban_command(bot, source, message, **rest) -> bool: if not message: return False username = message.split(" ")[0] with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if not user: bot.whisper(source, "No user with that name found.") return False if user.banned: bot.whisper(source, "User is already permabanned.") return False user.banned = True bot.ban( user, reason= f"User has been added to the {bot.bot_user.login} banlist. Contact a moderator level 1000 or higher for unban.", ) log_msg = f"{user} has been permabanned" bot.whisper(source, log_msg) AdminLogManager.add_entry("Permaban added", source, log_msg) return True
def edit_points(self, bot, source, message, **rest): if not message: return False msg_split = message.split(" ") if len(msg_split) < 2: # The user did not supply enough arguments bot.whisper(source, f"Usage: !{self.command_name} USERNAME POINTS") return False username_input = msg_split[0] try: num_points = int(msg_split[1]) except (ValueError, TypeError): # The user did not specify a valid integer for points bot.whisper(source, f"Invalid amount of points. Usage: !{self.command_name} USERNAME POINTS") return False with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username_input) if not user: bot.whisper(source, "This user does not exist FailFish") return False user.points += num_points if num_points >= 0: bot.whisper(source, f"Successfully gave {user} {num_points} points.") else: bot.whisper(source, f"Successfully removed {abs(num_points)} points from {user}.")
def long_timeout(self, bot, message, source, **options): errorString = "Invalid usage. !longtimeout user days" daysDuration = 0 if not message or len(message.split(" ")) < 2: bot.whisper(source, errorString) return False splitMsg = message.split(" ") try: daysDuration = int(splitMsg[1]) timeoutDuration = daysDuration * 86400 if timeoutDuration > 1209600: timeoutDuration = 1209600 nowTime = utils.now() endTime = nowTime + timedelta(days=daysDuration) with DBManager.create_session_scope() as db_session: badPerson = User.find_or_create_from_user_input( db_session, bot.twitch_helix_api, splitMsg[0]) if badPerson.moderator: bot.whisper(source, "You can't timeout mods") return False if badPerson.level >= 420: bot.whisper( source, f"{badPerson}'s level is too high, you can't time them out." ) return False if db_session.query(LongTimeout).filter( LongTimeout.user_id == badPerson.id).count() != 0: bot.whisper(source, f"{badPerson} already exists in the database") return False longtimeout = LongTimeout(user_id=badPerson.id, timeout_start=nowTime, timeout_end=endTime, timeout_author=source.name) db_session.add(longtimeout) bot._timeout( badPerson, timeoutDuration, f"Timed out by {source} for {daysDuration} days total") bot.whisper( source, f"Timed out {badPerson} for {daysDuration} days, per your !longtimeout" ) except ValueError: bot.whisper(source, errorString) return False except Exception as e: log.error(e)
def get(id): with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, id) if user is None: return {"error": "Not found"}, 404 # the aliases like above are not needed here since this API endpoint is new since version 1.38 return user.jsonify()
def on_pubmsg(self, chatconn, event): tags = { tag["key"]: tag["value"] if tag["value"] is not None else "" for tag in event.tags } id = tags["user-id"] login = event.source.user name = tags["display-name"] if event.source.user == self.nickname: return False if self.streamer == "forsen": if "zonothene" in login: self._ban(login) return True raw_m = event.arguments[0].lower() if raw_m.startswith("!lastseen forsen"): if len(raw_m) > len("!lastseen forsen2"): if raw_m[16] == " ": return True else: return True if raw_m.startswith("!lastseen @forsen"): if len(raw_m) > len("!lastseen @forsen2"): if raw_m[17] == " ": return True else: return True if self.streamer == "nymn": if "hades_k" in login: self.timeout_login(login, 3600, reason="Bad username") return True if "hades_b" in login: self.timeout_login(login, 3600, reason="Bad username") return True with DBManager.create_session_scope( expire_on_commit=False) as db_session: source = User.from_basics(db_session, UserBasics(id, login, name)) with new_message_processing_scope(self): res = HandlerManager.trigger("on_pubmsg", source=source, message=event.arguments[0], tags=tags) if res is False: return False self.parse_message(event.arguments[0], source, event, tags=tags)
def give_points(self, bot, source, message, **rest): if message is None or len(message) == 0: # The user did not supply any arguments return False msg_split = message.split(" ") if len(msg_split) < 2: # The user did not supply enough arguments bot.whisper(source, f"Usage: !{self.command_name} USERNAME POINTS") return False input = msg_split[0] try: num_points = utils.parse_points_amount(source, msg_split[1]) except InvalidPointAmount as e: bot.whisper(source, f"{e}. Usage: !{self.command_name} USERNAME POINTS") return False if num_points <= 0: # The user tried to specify a negative amount of points bot.whisper(source, "You cannot give away negative points admiralCute") return True # elif num_points < 250: # bot.whisper(source, "You must give 250 points or more :) Be charitable :)") # return True if not source.can_afford(num_points): # The user tried giving away more points than he owns bot.whisper(source, f"You cannot give away more points than you have. You have {source.points} points.") return False with DBManager.create_session_scope() as db_session: target = User.find_by_user_input(db_session, input) if target is None: # The user tried donating points to someone who doesn't exist in our database bot.whisper(source, "This user does not exist FailFish") return False if target == "admiralbulldog": bot.whisper(source, "But why?") return False if target == source: # The user tried giving points to themselves bot.whisper(source, "You can't give points to yourself Bruh") return True if self.settings["target_requires_sub"] is True and target.subscriber is False: # Settings indicate that the target must be a subscriber, which he isn't bot.whisper(source, "Your target must be a subscriber.") return False source.points -= num_points target.points += num_points bot.whisper(source, f"Successfully gave away {num_points} points to {target}") bot.whisper(target, f"{source} just gave you {num_points} points! You should probably thank them ;-)")
def user_from_access_token(self, access_token, twitch_helix_api, db_session): resp = self.get("", authorization=" ", headers={"Authorization": "OAuth " + access_token}) if "token" in resp: return User.find_or_create_from_user_input( db_session, twitch_helix_api, resp["token"]["user_name"]) return None
def on_redeem(self, redeemer, redeemed_id, user_input): if user_input is not None and redeemed_id == self.settings[ "redeemed_id"]: with DBManager.create_session_scope() as db_session: user = User.from_basics(db_session, redeemer) points = random.randint(self.settings["lower_points"], self.settings["upper_points"]) user.points += points self.bot.whisper(user, f"You have been given {points}")
def skip_function(self, skipped_by): with DBManager.create_session_scope() as db_session: skipped_by = User.find_by_user_input(db_session, skipped_by) if not skipped_by: return skipped_by_id = skipped_by.id if not self.enabled and self.current_song_id: return False self.load_song(skipped_by_id) return True
def get_user_value(self, key, extra={}): try: with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, extra["argument"]) if user is not None: return getattr(user, key) except: log.exception("Caught exception in get_source_value") return None
def on_whisper(self, chatconn, event): tags = {tag["key"]: tag["value"] if tag["value"] is not None else "" for tag in event.tags} id = tags["user-id"] login = event.source.user name = tags["display-name"] with DBManager.create_session_scope(expire_on_commit=False) as db_session: source = User.from_basics(db_session, UserBasics(id, login, name)) self.parse_message(event.arguments[0], source, event, tags, whisper=True)
def user_profile_connections_pair(): with DBManager.create_session_scope() as db_session: if "user" not in session: return redirect(f"/login?n=/connections/") user = User.find_by_id(db_session, session["user"]["id"]) if user is None: return render_template("no_user.html"), 404 if user.offcd: discord = None steam = None if ("discord_id" in session and "discord_username" in session and session["discord_id"] is not None and session["discord_username"] is not None): discord = { "id": session["discord_id"], "username": session["discord_username"] } if "steam_id" in session and session["steam_id"] is not None: steam = {"id": session["steam_id"]} data = { "steam": steam, "discord": discord, "twitch": session["user"], "offcd": user.offcd } try: if discord is not None and steam is not None: UserConnections._create( db_session, twitch_id=session["user"]["id"], twitch_login=user.login, discord_user_id=session["discord_id"], discord_username=session["discord_username"], steam_id=session["steam_id"], ) user._setcd(db_session) db_session.commit() return redirect(f"/connections/") else: return render_template("connections.html", user=user, data=data, returnUrl=f"/connections", pair_failed=True) except Exception as e: log.error(e) return render_template("connections.html", user=user, data=data, returnUrl=f"/connections", pair_failed=True) else: return render_template("errors/403.html"), 403
def get(login): # add ?user_input=true to query user more fuzzily query_by_user_input = request.args.get("user_input") == "true" with DBManager.create_session_scope() as db_session: if query_by_user_input: user = User.find_by_user_input(db_session, login) else: user = User.find_by_login(db_session, login) if user is None: return {"error": "Not found"}, 404 # these are provided for legacy purposes - so we don't break the API interface. json = user.jsonify() json["username_raw"] = json["name"] json["username"] = json["login"] json["nl_rank"] = json["num_lines_rank"] json["minutes_in_chat_online"] = int(json["time_in_chat_online"] / 60) json["minutes_in_chat_offline"] = int(json["time_in_chat_offline"] / 60) return json
def step_end(self): if self.question is not None: self.winstreak = 0 self.bot.safe_me( f'MingLee No one could answer the trivia! The answer was "{self.question["answer"]}" MingLee' ) self.question = None self.step = 0 self.last_question = utils.now() with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, "datguy1") user.points = user.points + 1
def update_subs(self, subs): """ subs is a list of usernames """ with DBManager.create_session_scope() as db_session: subs = set(subs) for user in db_session.query(User).filter(User.username.in_(subs)): try: subs.remove(user.username) except: pass user.subscriber = True for username in subs: # New user! user = User(username=username) user.subscriber = True db_session.add(user)
def unban(bot, source, message, event, args): if not message: bot.say(f"That user was not found in the user database") return False input = message.split(" ")[0] with DBManager.create_session_scope(expire_on_commit=False) as db_session: user = User.find_by_user_input(db_session, input) if user is None: bot.say(f"That user was not found in the user database") bot.execute_delayed(1, bot.unban, user)