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 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 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_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 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 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 _cancel_expired_duels(self): 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) challenged = User.find_by_id(db_session, self.duel_requests[source.id]) if source is not None and challenged is not None: self.bot.whisper( source, f"{challenged} didn't accept your duel request in time, so the duel has been cancelled. Ditched pepeLaugh", ) del self.duel_targets[self.duel_requests[source.id]] del self.duel_requests[source.id] del self.duel_request_price[source.id] del self.duel_begin_time[source.id]
def user_profile_connections(): 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 user_connection = UserConnections._from_twitch_id( db_session, user.id) 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 } user_connection = UserConnections._from_twitch_id( db_session, user.id) if user_connection: return render_template( "connections_unlink.html", user=user, data=user_connection.jsonify(), twitch_user=session["user"], returnUrl=f"/connections", ) return render_template("connections.html", user=user, data=data, returnUrl=f"/connections", pair_failed=False)
def cancel_duel(self, bot, source, **rest): """ Cancel any duel requests you've sent. How to use: !cancelduel """ if source.id not in self.duel_requests: bot.whisper(source, "You have not sent any duel requests") return with DBManager.create_session_scope() as db_session: challenged = User.find_by_id(db_session, self.duel_requests[source.id]) bot.whisper(source, f"You have cancelled the duel vs {challenged}") del self.duel_targets[challenged.id] del self.duel_request_price[source.id] del self.duel_begin_time[source.id] del self.duel_requests[source.id]
def on_message(self, ws, message): msg = json.loads(message) if msg["type"].lower() == "pong": self.sent_ping = False elif msg["type"].lower() == "reconnect": ScheduleManager.execute_now(self.reset) elif msg["type"].lower() == "message": if msg["data"][ "topic"] == "channel-bits-events-v2." + self.bot.streamer_user_id: messageR = json.loads(msg["data"]["message"]) user_id_of_cheer = str(messageR["data"]["user_id"]) bits_cheered = str(messageR["data"]["bits_used"]) with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, user_id_of_cheer) if user is not None: HandlerManager.trigger("on_cheer", True, user=user, bits_cheered=bits_cheered)
def on_message(self, message): msg = json.loads(message) if msg["type"].lower() == "pong": self.sent_ping = False return elif msg["type"].lower() == "reconnect": self.reset() return elif msg["type"].lower() == "message": if msg["data"][ "topic"] == "channel-bits-events-v2." + self.bot.streamer_user_id: messageR = json.loads(msg["data"]["message"]) user_id_of_cheer = str(messageR["data"]["user_id"]) bits_cheered = str(messageR["data"]["bits_used"]) with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, user_id_of_cheer) if user is not None: HandlerManager.trigger("on_cheer", True, user=user, bits_cheered=bits_cheered) return try: message_message = json.loads(msg["data"]["message"]) if message_message["type"] == "reward-redeemed": userDict = message_message["data"]["redemption"]["user"] HandlerManager.trigger( "on_redeem", redeemer=UserBasics(userDict["id"], userDict["login"], userDict["display_name"]), redeemed_id=message_message["data"]["redemption"] ["reward"]["id"], user_input=message_message["data"]["redemption"].get( "user_input", ""), ) except Exception as e: log.error(e) elif msg["type"].lower() == "response": if not msg["error"]: return log.warning(msg)
def decline_duel(self, bot, source, **options): """ 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 False with DBManager.create_session_scope() as db_session: requestor = User.find_by_id(db_session, self.duel_targets[source.id]) 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 user_profile_connections_unpair(): 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 saved_data = db_session.query(UserConnections).filter_by( twitch_id=session["user"]["id"]).one_or_none() if not saved_data: return render_template("errors/403.html"), 403 redis = RedisManager.get() unlinked_accounts = redis.get("unlinks-subs-discord") if unlinked_accounts is None: unlinked_accounts = {} else: unlinked_accounts = json.loads(unlinked_accounts) unlinked_accounts[saved_data.twitch_id] = saved_data.jsonify() unlinked_accounts = redis.set("unlinks-subs-discord", json.dumps(unlinked_accounts)) saved_data._remove(db_session) db_session.commit() return redirect(f"/connections/")
async def check_discord_roles(self): if not self.guild: return tier2_role = self.guild.get_role(int(self.settings["tier2_role"])) tier3_role = self.guild.get_role(int(self.settings["tier3_role"])) notify_role = self.guild.get_role(int(self.settings["notify_role"])) ignore_role = self.guild.get_role(int(self.settings["ignore_role"])) roles_allocated = { "tier2_role": tier2_role, "tier3_role": tier3_role, "notify_role": notify_role, "ignore_role": ignore_role, } quick_dict_twitch = {} quick_dict_discord = {} subs_to_return = {} queued_subs = json.loads(self.redis.get("queued-subs-discord")) unlinkinfo = json.loads(self.redis.get("unlinks-subs-discord")) messages_add = [] messages_remove = [] messages_other = [] with DBManager.create_session_scope() as db_session: for twitch_id in unlinkinfo: unlinks = unlinkinfo[twitch_id] member = self.guild.get_member(int(unlinks["discord_user_id"])) if member: if tier3_role is not None and tier3_role in member.roles: await self.remove_role(member, tier3_role) if tier2_role is not None and tier2_role in member.roles: await self.remove_role(member, tier2_role) user = User.find_by_id(db_session, twitch_id) steam_id = unlinks["steam_id"] tier = unlinks["discord_tier"] if self.settings["notify_on_unsub"] and tier > 1 and self.settings[f"notify_on_tier{tier}"]: discord = await self.get_discord_string(unlinks["discord_user_id"]) messages_other.append( f"\n\nAccount Data Unlinked: Tier {tier} sub removal notification:\nTwitch: {user} (<https://twitch.tv/{user.login}>){discord}\nSteam: <https://steamcommunity.com/profiles/{steam_id}>" ) self.redis.set("unlinks-subs-discord", json.dumps({})) all_connections = db_session.query(UserConnections).all() for connection in all_connections: user = connection.twitch_user member = self.guild.get_member(int(connection.discord_user_id)) discord = await self.get_discord_string(connection.discord_user_id) steam_id = connection.steam_id if ( not user or discord == "" ): # Discord doesnt exist or Somehow the twitch doesnt exist in our database so we prune connection._remove(db_session) continue quick_dict_twitch[connection.twitch_id] = connection quick_dict_discord[connection.discord_user_id] = connection if not connection.twitch_login: connection._update_twitch_login(db_session, user.login) if connection.twitch_login != user.login: if connection.tier > 1: if self.settings["notify_on_name_change"] and self.settings[f"notify_on_tier{connection.tier}"]: messages_other.append( f"\n\nTwitch login changed for a tier {connection.tier} sub\nSteam: <https://steamcommunity.com/profiles/{connection.steam_id}>\nOld Twitch: {connection.twitch_login}\nNew Twitch: {user.login}" ) connection._update_twitch_login(db_session, user.login) if member and member.display_name + "#" + member.discriminator != connection.discord_username: connection._update_discord_username(db_session, member.display_name + "#" + member.discriminator) db_session.commit() if not ignore_role or member and ignore_role not in member.roles: role = roles_allocated[f"tier{user.tier}_role"] if user.tier and user.tier > 1 else None if user.tier == connection.tier: if role and role not in member.roles: await self.add_role(member, role) else: if user.tier and user.tier > 1: if ( self.settings["notify_on_unsub"] and connection.tier > 1 and self.settings[f"notify_on_tier{connection.tier}"] ): messages_remove.append( f"\n\nTier {connection.tier} sub removal notification:\nTwitch: {user} (<https://twitch.tv/{user.login}>){discord}\nSteam: <https://steamcommunity.com/profiles/{steam_id}>" ) connection._update_tier(db_session, user.tier) if role: if ( self.settings["notify_on_new_sub"] and user.tier > 1 and self.settings[f"notify_on_tier{user.tier}"] ): messages_add.append( f"\n\nTier {user.tier} sub notification:\nTwitch: {user} (<https://twitch.tv/{user.login}>){discord}\nSteam: <https://steamcommunity.com/profiles/{steam_id}>" ) await self.add_role(member, role) connection._update_tier(db_session, user.tier) db_session.commit() if not self.settings["pause_bot"]: if connection.twitch_id not in subs_to_return and not self.settings["pause_bot"]: if connection.tier != user.tier: if connection.tier != 0 and (not user.tier or user.tier == 0): subs_to_return[connection.twitch_id] = str( utils.now() + timedelta(days=int(self.settings["grace_time"])) ) else: subs_to_return[connection.twitch_id] = str(utils.now()) if not self.settings["pause_bot"]: for sub in queued_subs: # sub "twitch_id" : date_to_be_removed connection = quick_dict_twitch[sub] time = queued_subs[sub] user = connection.twitch_user if user.tier == connection.tier or ( not user.tier and connection.tier == 0 ): # they resubbed before grace ended continue if ":" in time[-5:]: time = f"{time[:-5]}{time[-5:-3]}{time[-2:]}" if datetime.strptime(time, "%Y-%m-%d %H:%M:%S.%f%z") < utils.now(): # must be run now member = self.guild.get_member(int(connection.discord_user_id)) if connection.tier > 1: role = roles_allocated[f"tier{connection.tier}_role"] if member and role and role in member.roles: await self.remove_role(member, role) if self.settings["notify_on_unsub"] and self.settings[f"notify_on_tier{connection.tier}"]: discord = await self.get_discord_string(connection.discord_user_id) messages_remove.append( f"\n\nTier {connection.tier} sub removal notification:\nTwitch: {user} (<https://twitch.tv/{user.login}>){discord}\nSteam: <https://steamcommunity.com/profiles/{connection.steam_id}>" ) connection._update_tier(db_session, user.tier) else: subs_to_return[sub] = queued_subs[sub] db_session.commit() for tier in [2, 3]: role = roles_allocated[f"tier{tier}_role"] if not role: continue for member in role.members: if ignore_role is None or ignore_role not in member.roles: if str(member.id) not in quick_dict_discord: if not self.settings["pause_bot"]: await self.remove_role(member, role) else: connection = quick_dict_discord[str(member.id)] if connection.tier != tier: await self.remove_role(member, role) if notify_role: for member in notify_role.members: return_message = "" for message in messages_other: if len(return_message) + len(message) > 1300: await self.private_message(member, return_message) return_message = "" return_message += message if return_message != "": await self.private_message(member, return_message) return_message = "" for message in messages_remove: if len(return_message) + len(message) > 1300: await self.private_message(member, return_message) return_message = "" return_message += message if return_message != "": await self.private_message(member, return_message) return_message = "" for message in messages_add: if len(return_message) + len(message) > 1300: await self.private_message(member, return_message) return_message = "" return_message += message if return_message != "": await self.private_message(member, return_message) return_message = "" self.redis.set("queued-subs-discord", json.dumps(subs_to_return))
def initiate_duel(self, bot, source, message, **rest): """ Initiate a duel with a user. You can also bet points on the winner. By default, the maximum amount of points you can spend is 420. How to use: !duel USERNAME POINTS_TO_BET """ if message is None: bot.whisper(source, f"Invalid Usage !duel USERNAME POINTS_TO_BET") return False msg_split = message.split() input = msg_split[0] with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, input) if user is None: # No user was found with this username bot.whisper( source, f"The user, {input}, has never typed in chat before FailFish" ) return False if user == source: bot.whisper(source, f"You cannot duel yourself") return False duel_price = 0 if len(msg_split) > 1: try: duel_price = int(msg_split[1]) if duel_price < 0: return False except ValueError: pass if source.id in self.duel_requests: currently_duelling = User.find_by_id( db_session, self.duel_requests[source.id]) if currently_duelling is None: del self.duel_requests[source.id] return False bot.whisper( source, f"You already have a duel request active with {currently_duelling}. Type !cancelduel to cancel your duel request.", ) return False if user.last_active is None or ( utils.now() - user.last_active) > timedelta(minutes=5): bot.whisper( source, "This user has not been active in chat within the last 5 minutes. Get them to type in chat before sending another challenge", ) return False if not user.can_afford(duel_price) or not source.can_afford( duel_price): bot.whisper( source, f"You or your target do not have more than {duel_price} points, therefore you cannot duel for that amount.", ) return False if user.id in self.duel_targets: challenged_by = User.find_by_id(db_session, self.duel_requests[user.id]) bot.whisper( source, f"This person is already being challenged by {challenged_by}. Ask them to answer the offer by typing !deny or !accept", ) return False self.duel_targets[user.id] = source.id self.duel_requests[source.id] = user.id self.duel_request_price[source.id] = duel_price self.duel_begin_time[source.id] = utils.now() bot.whisper( user, f"You have been challenged to a duel by {source} for {duel_price} points. You can either !accept or !deny this challenge.", ) bot.whisper(source, f"You have challenged {user} for {duel_price} points")
def accept_duel(self, bot, source, **rest): """ Accepts any active duel requests you've received. How to use: !accept """ if source.id not in self.duel_targets: bot.whisper(source, "You are not being challenged to a duel by anyone.") return with DBManager.create_session_scope() as db_session: requestor = User.find_by_id(db_session, self.duel_targets[source.id]) duel_price = self.duel_request_price[self.duel_targets[source.id]] if not source.can_afford(duel_price) or not requestor.can_afford( duel_price): bot.whisper( source, f"Your duel request with {requestor} was cancelled due to one of you not having enough points.", ) bot.whisper( requestor, f"Your duel request with {source} was cancelled due to one of you not having enough points.", ) del self.duel_requests[requestor.id] del self.duel_request_price[requestor.id] del self.duel_begin_time[requestor.id] del self.duel_targets[source.id] return False source.points -= duel_price requestor.points -= duel_price participants = [source, requestor] winner = random.choice(participants) participants.remove(winner) loser = participants.pop() winner.points += duel_price * 2 # Persist duel statistics winner.duel_stats.won(duel_price) loser.duel_stats.lost(duel_price) arguments = { "winner": winner.name, "loser": loser.name, "total_pot": duel_price, "extra_points": duel_price, } if duel_price > 0: message = self.get_phrase("message_won_points", **arguments) if duel_price >= 500 and self.settings["show_on_clr"]: bot.websocket_manager.emit( "notification", {"message": f"{winner} won the duel vs {loser}"}) else: message = self.get_phrase("message_won", **arguments) bot.say(message) del self.duel_requests[requestor.id] del self.duel_request_price[requestor.id] del self.duel_begin_time[requestor.id] del self.duel_targets[source.id] HandlerManager.trigger("on_duel_complete", winner=winner, loser=loser, points_won=duel_price, points_bet=duel_price)