def handle(bot: Bot, event: events.TextMessage, match: typing.Match): if event.uid not in Config.whitelist_admin: return try: json = fetch_api("account", api_key=match.group(1)) account = models.Account.get_by_api_info( bot.session, guid=json.get("id"), name=json.get("name") ) # Account does not exist if not account: logging.info("User was not registered.") bot.send_message(event.id, "account_unknown", account=json.get("name")) return # Get previous identity previous_identity: typing.Optional[ models.LinkAccountIdentity ] = account.valid_identities.one_or_none() # Remove previous links account.invalidate(bot.session) if previous_identity: # Get cldbid and sync groups try: cldbid = bot.exec_( "clientgetdbidfromuid", cluid=previous_identity.identity.guid )[0]["cldbid"] result = sync_groups(bot, cldbid, account, remove_all=True) logging.info( "%s (%s) marked previous links of %s as ignored", event.name, event.uid, account.name, ) bot.send_message( event.id, "groups_revoked", amount="1", groups=result["removed"], ) except ts3.TS3Error: # User might not exist in the db logging.info("Failed to remove groups from user", exc_info=True) else: bot.send_message(event.id, "groups_revoked", amount="0", groups=[]) except InvalidKeyException: logging.info("This seems to be an invalid API key.") bot.send_message(event.id, "invalid_token") except ApiErrBadData: bot.send_message(event.id, "error_api")
def handle(bot: Bot, event: events.TextMessage, _match: typing.Match): if event.uid not in Config.whitelist_admin: return i18n.set("locale", "en") message = i18n.t("available_commands") for _ in bot.commands: if _.USAGE != USAGE: message += "\n - {}".format(_.USAGE) bot.send_message(event.id, message, is_translation=False)
def handle(bot: Bot, event: events.TextMessage, match: Match) -> None: cldbid = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] user_groups = bot.exec_("servergroupsbyclientid", cldbid=cldbid) allowed = False if event.uid in Config.whitelist_admin: allowed = True else: for group in user_groups: if group["name"] in Config.whitelist_group_list: allowed = True break # User doesn't have any whitelisted groups if not allowed: return groups = bot.exec_("servergrouplist") group = None search_group = match.group(1).strip() for _ in groups: if _["type"] != "1": # Regular type, neither template nor query continue if _["name"] == search_group: group = _ break # Group not found if group is None: bot.send_message(event.id, "list_not_found") return members = bot.exec_("servergroupclientlist", "names", sgid=group["sgid"]) members = sorted(members, key=lambda x: cast(str, _.get("client_nickname", ""))) if len(members) >= 50: bot.send_message(event.id, "list_50_users") return text_groups = [""] index = 0 for member in members: member_text = ( f"\n- [URL=client://0/{member['client_unique_identifier']}]" f"{member['client_nickname']}[/URL]") if len(text_groups[index]) + len(bytes(member_text, "utf-8")) >= 1024: index += 1 text_groups.append("") text_groups[index] += member_text bot.send_message(event.id, "list_users", amount=len(members), group=group["name"]) for _ in text_groups: bot.send_message(event.id, _, is_translation=False)
def handle(bot: Bot, event: events.TextMessage, match: typing.Match): try: account = fetch_api("account", api_key=match.group(1)) server = enums.World(account.get("world")) guilds = ( bot.session.query(models.Guild) .filter(models.Guild.guid.in_(account.get("guilds", []))) .filter(models.Guild.group_id.isnot(None)) .options(load_only(models.Guild.name)) ) bot.send_message( event.id, "info_world", user=account.get("name"), world=server.proper_name, guilds=", ".join([_.name for _ in guilds]), ) except InvalidKeyException: logging.info("This seems to be an invalid API key.") bot.send_message(event.id, "invalid_token") except (requests.RequestException, RateLimitException, ApiErrBadData): logging.exception("Error during API call") bot.send_message(event.id, "error_api")
def handle(bot: Bot, event: events.TextMessage, match: typing.Match): if event.uid not in Config.whitelist_admin: return # Grab client_uid try: user = bot.exec_("clientgetnamefromdbid", cldbid=match.group(1)) client_uid = user[0]["cluid"] except ts3.query.TS3QueryError: bot.send_message(event.id, "user_not_found") return try: json = fetch_api("account", api_key=match.group(2)) account = models.Account.get_or_create(bot.session, json, match.group(2)) identity: models.Identity = models.Identity.get_or_create( bot.session, client_uid) # Save api key in account account.api_key = match.group(2) account.is_valid = True bot.session.commit() transfer_registration( bot, account, event, is_admin=True, target_identity=identity, target_dbid=match.group(1), ) except InvalidKeyException: logging.info("This seems to be an invalid API key.") bot.send_message(event.id, "invalid_token") return except (RateLimitException, RequestException, ApiErrBadData): bot.send_message(event.id, "error_api")
def handle(bot: Bot, event: events.TextMessage, match: typing.Match): cldbid = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] # Grab user's account account = models.Account.get_by_identity(bot.session, event.uid) if not account or not account.is_valid: bot.send_message(event.id, "missing_token") return on_join_hours_timeout = Config.getfloat("verify", "on_join_hours") # Saved account is older than x hours or has no guilds if ( timedelta_hours(datetime.datetime.today() - account.last_check) >= on_join_hours_timeout or account.guilds.count() == 0 ): bot.send_message(event.id, "account_updating") try: account.update(bot.session) # Sync groups in case the user has left a guild or similar changes sync_groups(bot, cldbid, account) except InvalidKeyException: # Invalidate link account.invalidate(bot.session) sync_groups(bot, cldbid, account, remove_all=True) logging.info("Revoked user's permissions.") bot.send_message(event.id, "invalid_token_admin") return except (requests.RequestException, RateLimitException, ApiErrBadData): logging.exception("Error during API call") bot.send_message(event.id, "error_api") # User requested guild removal if match.group(1) and match.group(1).lower() == "remove": # Get active guilds has_active_guilds: int = ( account.guilds.join(models.Guild) .filter(models.Guild.group_id.isnot(None)) .filter(models.LinkAccountGuild.is_active.is_(True)) .count() ) # There are no active guilds, no need to remove anything if not has_active_guilds: bot.send_message(event.id, "guild_already_removed") return # Remove guilds account.guilds.filter(models.LinkAccountGuild.is_active.is_(True)).update( {"is_active": False} ) bot.session.commit() # Sync groups changes = sync_groups(bot, cldbid, account) if len(changes["removed"]) > 0: bot.send_message(event.id, "guild_removed") else: bot.send_message(event.id, "guild_error") return available_guilds = account.guilds.join(models.Guild).filter( models.Guild.group_id.isnot(None) ) # No guild specified if not match.group(1): available_guilds = available_guilds.all() if len(available_guilds) > 0: bot.send_message( event.id, "guild_selection", guilds="\n- ".join([_.guild.tag for _ in available_guilds]), ) else: bot.send_message(event.id, "guild_unknown") else: guild = match.group(1).lower() selected_guild: typing.Optional[ models.LinkAccountGuild ] = available_guilds.filter(models.Guild.tag.ilike(guild)).one_or_none() # Guild not found or user not in guild if not selected_guild: bot.send_message( event.id, "guild_invalid_selection", timeout=on_join_hours_timeout ) return # Toggle guild if selected_guild.is_active: selected_guild.is_active = False else: selected_guild.is_active = True # Remove other guilds if only one is allowed if not Config.getboolean("guild", "allow_multiple_guilds"): account.guilds.filter( models.LinkAccountGuild.id != selected_guild.id ).update({"is_active": False}) bot.session.commit() # Sync groups changes = sync_groups(bot, cldbid, account) if selected_guild.is_active and len(changes["added"]): bot.send_message(event.id, "guild_set", guild=selected_guild.guild.name) elif not selected_guild.is_active and len(changes["removed"]): bot.send_message( event.id, "guild_removed_one", guild=selected_guild.guild.name ) else: bot.send_message(event.id, "guild_error")
def handle(bot: Bot, event: events.TextMessage, match: Match) -> None: sheet_channel_id = Config.get("teamspeak", "sheet_channel_id") if sheet_channel_id == 0: return current_state: CommandingDict = { "EBG": [], "Red": [], "Green": [], "Blue": [] } if match.group(1) == "help" and event.uid in Config.whitelist_admin: bot.send_message( event.id, "!sheet <ebg,red,green,blue,remove,reset>\n!sheet set <ebg,red,green,blue,remove> <name>", is_translation=False, ) return if match.group(1) == "reset" and event.uid in Config.whitelist_admin: pass # Don't load the current file, just use the defaults elif match.group(1) == "set" and event.uid in Config.whitelist_admin: # Force-set an entry _match = re.match( "!sheet set (ebg|red|green|blue|r|g|b|remove) (.*)", event.message.strip(), ) if not _match: bot.send_message(event.id, "invalid_input") return if STATE_FILE.exists(): current_state = cast(CommandingDict, json.loads(STATE_FILE.read_text())) if _match.group(1) == "remove": current_state = _remove_lead(current_state, name_field=_match.group(2)) else: # Add new entry new_state = _add_lead( current_state, wvw_map=_match.group(1), note="", name=_match.group(2), ) if not new_state: bot.send_message(event.id, "sheet_map_full") return current_state = new_state elif match.group(1) in [ "ebg", "red", "green", "blue", "r", "g", "b", "remove" ]: if STATE_FILE.exists(): current_state = json.loads(STATE_FILE.read_text()) if match.group(1) == "remove": current_state = _remove_lead(current_state, uid=event.uid) else: new_state = _add_lead( current_state, wvw_map=match.group(1), note=match.group(2), uid=event.uid, name=event.name, ) if not new_state: bot.send_message(event.id, "sheet_map_full") return current_state = new_state else: bot.send_message(event.id, "invalid_input") return # Build new table desc = "[table][tr][td] | Map | [/td][td] | Lead | [/td][td] | Note | [/td][td] | Date | [/td][/tr]" for _map, leads in cast(IterType, current_state.items()): if len(leads) == 0: desc += f"[tr][td]{_map}[/td][td]-[/td][td]-[/td][td]-[/td][/tr]" continue for lead in leads: desc += ( f"[tr][td]{_map}[/td][td]{lead['lead']}[/td][td]{_encode(lead['note'])}[/td]" f"[td]{lead['date']}[/td][/tr]") desc += ( f"[/table]\n[hr]Last change: {_tidy_date()}\n\n" f"Link to bot: [URL=client://0/{bot.own_uid}]{Config.get('bot_login', 'nickname')}[/URL]\n" # Add link to self "Usage:\n" "- !sheet red/green/blue (note)\t—\tRegister your lead with an optional note (20 characters).\n" "- !sheet remove\t—\tRemove the lead") bot.exec_("channeledit", cid=sheet_channel_id, channel_description=desc) bot.send_message(event.id, "sheet_changed") STATE_FILE.write_text(json.dumps(current_state))
def handle(bot: Bot, event: events.TextMessage, match: Match) -> None: if event.uid not in Config.whitelist_admin: return # Grab cluid try: if match.group(1).isdigit(): # DB id user = bot.exec_("clientgetnamefromdbid", cldbid=match.group(1)) cldbid = match.group(1) cluid = user[0]["cluid"] else: user = bot.exec_("clientgetnamefromuid", cluid=match.group(1)) cldbid = user[0]["cldbid"] cluid = match.group(1) except ts3.query.TS3QueryError: bot.send_message(event.id, "user_not_found") return # Grab user's account account = models.Account.get_by_identity(bot.session, cluid) if not account: bot.send_message(event.id, "verify_no_token") return try: bot.send_message(event.id, "account_updating") result = account.update(bot.session) if result["transfer"]: old_world: enums.World = result["transfer"][0] new_world: enums.World = result["transfer"][1] bot.send_message( event.id, "verify_transferred", old_world=old_world.proper_name, new_world=new_world.proper_name, ) guilds_joined, guilds_left = result["guilds"] if len(guilds_joined) > 0 or len(guilds_left) > 0: bot.send_message( event.id, "verify_guild_change", guilds_joined=guilds_joined, guilds_left=guilds_left, ) # Sync user's groups sync_groups(bot, cldbid, account) bot.send_message( event.id, "verify_valid_world", user=account.name, world=account.world.proper_name, ) except InvalidKeyException: bot.send_message(event.id, "invalid_token") # Invalidate link account.invalidate(bot.session) changes = sync_groups(bot, cldbid, account) bot.send_message(event.id, "groups_removed", groups=str(changes["removed"])) except (requests.RequestException, ApiErrBadData): bot.send_message(event.id, "error_api")
def handle(bot: Bot, event: events.TextMessage, match: Match) -> None: key = match.group(1) # Check with ArenaNet's API try: account_info = fetch_api("account", api_key=key) # Check if one of the guilds is in the alliance is_part_of_alliance = (bot.session.query(models.Guild).filter( models.Guild.guid.in_(account_info.get("guilds", []))).filter( models.Guild.is_part_of_alliance.is_(True)).count() > 0) # One of the guilds is in the alliance if is_part_of_alliance: account: models.Account = models.Account.get_or_create( bot.session, account_info, key) identity: models.Identity = models.Identity.get_or_create( bot.session, event.uid) # Check if account is registered to anyone linked_identity: Optional[ models. LinkAccountIdentity] = account.valid_identities.one_or_none() # Account is already linked if linked_identity: # Account is linked to another guid if linked_identity.identity.guid != event.uid: try: # Get user's DB id cldbid: str = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] except ts3.TS3Error: LOG.error("Failed to get user's dbid", exc_info=True) bot.send_message(event.id, "error_critical") return force_key_name = f"ts3bot-{cldbid}" # Fetch token info token_info = fetch_api("tokeninfo", api_key=key) # Override registration, same as !register if token_info.get("name", "") == force_key_name: ts3bot.transfer_registration(bot, account, event) LOG.info( "%s (%s) transferred permissions of %s onto themselves.", event.name, event.uid, account_info.get("name"), ) return LOG.warning( "%s (%s) tried to use an already registered API key/account. (%s)", event.name, event.uid, account_info.get("name"), ) bot.send_message(event.id, "token_in_use", api_name=force_key_name) else: # Account is linked to current guid LOG.info( "User %s (%s) tried to register a second time for whatever reason using %s", event.name, event.uid, account_info.get("name", "Unknown account"), ) # Save new API key if account.api_key != key: account.api_key = key account.is_valid = True bot.session.commit() bot.send_message(event.id, "registration_exists") return # Same API key supplied, last check was over 12 minutes ago if (ts3bot.timedelta_hours(datetime.datetime.today() - account.last_check) >= 0.2): # Update saved account info if same API key was posted again with a reasonable time frame account.update(bot.session) try: # Get user's DB id cldbid = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] # Sync groups ts3bot.sync_groups(bot, cldbid, account) bot.send_message(event.id, "registration_details_updated") except ts3.TS3Error: # User might not exist in the db LOG.error("Failed to sync user", exc_info=True) else: # Too early bot.send_message(event.id, "registration_too_early") else: # Otherwise account is not yet linked and can be used # Save API key account.api_key = key account.is_valid = True bot.session.commit() # Get user's DB id cldbid = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] # Unlink previous account from identity current_account = models.Account.get_by_identity( bot.session, event.uid) if current_account: LOG.info("Delinking %s from cldbid:%s", current_account, cldbid) current_account.invalidate(bot.session) # Register link between models bot.session.add( models.LinkAccountIdentity(account=account, identity=identity)) bot.session.commit() # Add all known guilds to user if enabled if Config.getboolean( "guild", "assign_on_register") and Config.getboolean( "guild", "allow_multiple_guilds"): cast(AppenderQuery, account.guilds).filter( models.LinkAccountGuild.id.in_( bot.session.query(models.LinkAccountGuild.id).join( models.Guild).filter( models.Guild.group_id.isnot( None)).subquery())).update( {"is_active": True}, synchronize_session="fetch") bot.session.commit() # Sync groups sync_groups(bot, cldbid, account) LOG.info( "Assigned alliance permissions to %s (%s) using %s", event.name, event.uid, account_info.get("name", "Unknown account"), ) # Was registered with other account previously if current_account: bot.send_message(event.id, "registration_update", account=account.name) else: bot.send_message(event.id, "welcome_registered") # Tell user about !guild if it's enabled if Config.getboolean("commands", "guild"): if Config.getboolean( "guild", "assign_on_register") and Config.getboolean( "guild", "allow_multiple_guilds"): bot.send_message(event.id, "welcome_registered_3") else: bot.send_message(event.id, "welcome_registered_2") else: bot.send_message( event.id, "invalid_world", world=enums.World(account_info.get("world")).proper_name, ) except InvalidKeyException: LOG.info("This seems to be an invalid API key.") bot.send_message(event.id, "invalid_token_retry") except (RateLimitException, RequestException, ApiErrBadData): bot.send_message(event.id, "error_api")
def transfer_registration( bot: ts3_bot.Bot, account: models.Account, event: events.TextMessage, is_admin: bool = False, target_identity: typing.Optional[models.Identity] = None, target_dbid: typing.Optional[str] = None, ): """ Transfers a registration and server/guild groups to the sender of the event or the target_guid :param bot: The current bot instance :param account: The account that should be re-registered for the target user :param event: The sender of the text message, usually the one who gets permissions :param is_admin: Whether the sender is an admin :param target_identity: To override the user who gets the permissions :param target_dbid: The target's database id, usually sourced from the event :return: """ # Get identity from event if necessary if not target_identity: target_identity: models.Identity = models.Identity.get_or_create( bot.session, event.uid) # Get database id if necessary if not target_dbid: try: target_dbid: str = bot.exec_("clientgetdbidfromuid", cluid=event.uid)[0]["cldbid"] except ts3.TS3Error: # User might not exist in the db logging.exception("Failed to get database id from event's user") bot.send_message(event.id, "error_critical") return # Get current guild groups to save them for later use guild_groups = account.guild_groups() # Get previous identity previous_identity: typing.Optional[ models.LinkAccountIdentity] = account.valid_identities.one_or_none() # Remove previous identities, also removes guild groups account.invalidate(bot.session) # Account is currently registered, sync groups with old identity if previous_identity: # Get cldbid and sync groups try: cldbid = bot.exec_( "clientgetdbidfromuid", cluid=previous_identity.identity.guid)[0]["cldbid"] result = sync_groups(bot, cldbid, account, remove_all=True) logging.info( "Removed previous links of %s as ignored during transfer to %s", account.name, target_identity.guid, ) if is_admin: bot.send_message(event.id, "groups_revoked", amount="1", groups=result["removed"]) except ts3.TS3Error: # User might not exist in the db logging.info("Failed to remove groups from user", exc_info=True) # Invalidate target identity's link, if it exists other_account = models.Account.get_by_identity(bot.session, target_identity.guid) if other_account: other_account.invalidate(bot.session) # Transfer roles to new identity bot.session.add( models.LinkAccountIdentity(account=account, identity=target_identity)) # Add guild group if guild_groups: bot.session.query(models.LinkAccountGuild).filter( models.LinkAccountGuild.id.in_([g.id for g in guild_groups ])).update({"is_active": True}) bot.session.commit() # Sync group sync_groups(bot, target_dbid, account) logging.info("Transferred groups of %s to cldbid:%s", account.name, target_dbid) bot.send_message( event.id, "registration_transferred", account=account.name, )