async def on_member_unban(guild, user): if self.redis: donator_profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) unban_related_accounts = await get_guild_value(guild, ["unbanRelatedAccounts", DEFAULTS.get("unbanRelatedAccounts")]) if donator_profile.features.get("premium"): if unban_related_accounts: try: account, accounts = await get_user(author=user, guild=guild) except UserNotVerified: pass else: accounts = set(accounts) if account: #FIXME: temp until primary accounts are saved to the accounts array accounts.add(account.id) for roblox_id in accounts: discord_ids = (await self.r.db("bloxlink").table("robloxAccounts").get(roblox_id).run() or {}).get("discordIDs") or [] for discord_id in discord_ids: discord_id = int(discord_id) if discord_id != user.id: try: ban_entry = await guild.fetch_ban(Object(discord_id)) except (NotFound, Forbidden): pass else: try: await guild.unban(ban_entry.user, reason=f"unbanRelatedAccounts is enabled - alt of {user} ({user.id})") except (Forbidden, HTTPException): pass else: await post_event(guild, None, "moderation", f"{ban_entry.user.mention} is an alt of {user.mention} and has been `unbanned`.", RED_COLOR)
async def clearbetween(self, ctx, before_message_id: int, after_message_id: int): """Clear all messages (up to 2000) in the channel the command is run in between 2 given message ids""" await ctx.channel.purge(limit=2000, before=Object(id=before_message_id), after=Object(id=after_message_id))
def get_server_priority(servers,get_priority): servers = Array(servers) servers_parsed = [] used = [] priority = 0 done = False while priority < PRIORITY_LIMIT and done == False: servers_p = get_priority(priority) if len(servers_p) > 0: servers_r = [] for server in servers_p: id = server.get('server_id') if servers.search(id) > -1: if not id in used: used.append(id) servers_r.append(Object(id)) servers_parsed.append(servers_r) else: done = True priority += 1 servers_r = [] for server in servers: if not server.id in used: servers_r.append(Object(server.id)) servers_parsed.append(servers_r) return servers_parsed
def __init__(self, prefix: str, start_time: int, colour: int, logger, session_manager: SessionManager, db: DatabaseController, error_log: int, feedback_log: int): """ Init the instance of HahaNoUR. :param prefix: the bot prefix. :param start_time: the bot start time. :param colour: the colour used for embeds. :param logger: the logger. :param session_manager: the SessionManager instance. :param db: the MongoDB data controller. :param error_log: the channel id for error log. """ super().__init__(prefix) self.prefix = prefix self.colour = colour self.start_time = start_time self.logger = logger self.help_general = None self.all_help = None self.db = db self.session_manager = session_manager # FIXME remove type casting after library rewrite self.error_log = Object(str(error_log)) self.feedbag_log = Object(str(feedback_log))
async def caps_on_message(self, message): if not message.guild: return if message.author.bot: return if not message.content: return number_of_caps = sum(1 for letter in message.content if letter.isupper()) if (number_of_caps >= len(message.content) * 0.6) and (len( message.content.split()) > 1): query = "SELECT toggle FROM caps WHERE guild_id = $1" caps = await self.bot.pool.fetchval(query, message.guild.id) if not caps: return proxy_ctx = Object(id=None) proxy_ctx.guild = message.guild proxy_ctx.author = message.author proxy_ctx.bot = self.bot if not await checks.has_level(proxy_ctx, "mod"): try: await message.delete() except Forbidden: pass self.bot.dispatch("member_strike", message.author, "caps", message.content)
async def is_admin_in_guild(self, guild): proxy_ctx = Object(id=None) proxy_ctx.guild = guild proxy_ctx.author = guild.get_member(int(session["user"]["id"])) if not proxy_ctx.author: return False proxy_ctx.bot = self.bot if await checks.has_level(proxy_ctx, "admin"): return True
def proxy_user(user_id: str) -> Object: try: user_id = int(user_id) except ValueError: raise BadArgument user = Object(user_id) user.mention = user.id user.avatar_url_as = lambda static_format: None return user
def proxy_user(user_id: str) -> Object: """Create a proxy user for the provided user_id for situations where a Member or User object cannot be resolved.""" try: user_id = int(user_id) except ValueError: raise BadArgument user = Object(user_id) user.mention = user.id user.avatar_url_as = lambda static_format: None return user
async def check_old_premium(self, guild=None, user=None): if not user: user = Object(guild.owner_id) prem_data = await get_user_value(user, "premium") or {} if prem_data.get("transferFrom"): user = Object(int(prem_data["transferFrom"])) return await self.has_patreon_premium( user) or await self.has_selly_premium(user) or DonatorProfile( user=user)
async def _check_registered_sub_status(self): """Periodic task to check if and users have unsubbed or resubbed. If they have unsubbed, they will be removed from the whitelist but the discord -> mc account relationship will be preserved. If they have resubbed, the stored relationship will be restored to the whitelist. """ await self.bot.wait_until_ready() # list of tuples of (mc_uuid, mc_name, resubbed/unsubbed) status_changed_list = [] # type: List[Tuple[str, str, bool, Member]] # TODO: hopefully won't exceed 100 guilds async for guild in self.bot.fetch_guilds(): # type: Guild # have to get complete guild as fetch_guild just gives basic info guild = self.bot.get_guild(guild.id) if guild is None: print('Unable to retrieve guild') return # TODO: log ban_list = await guild.bans() banned_user_ids = set(str(be[1].id) for be in ban_list) for disc_id, wl_entry in self._working_discord_mc_mapping.items(): mc_uuid_str = wl_entry['uuid'] mc_uuid = uuid.UUID(mc_uuid_str) if disc_id in banned_user_ids: status_changed_list.append( (mc_uuid, wl_entry['name'], False, member)) continue member = guild.get_member(int(disc_id)) # type: Member if member is None: print(f'User {disc_id} could not be retrieved') continue # TODO: log # if the uuid is not in the whitelist if mc_uuid not in self._whitelisted_uuids: # check if the user has resubbed if any(r.name in self._allowed_roles for r in member.roles): status_changed_list.append( (mc_uuid, wl_entry['name'], True, member)) continue # if user has none of the allowed roles, they have lost sub if all(r.name not in self._allowed_roles for r in member.roles): status_changed_list.append( (mc_uuid, wl_entry['name'], False, member)) for mc_user_uuid, mc_username, resubbed, member in status_changed_list: if resubbed: # add resubbed users back to whitelist await self._add_user_to_whitelist(mc_user_uuid, mc_username) await member.add_roles(Object(self._managed_role_id), reason='Resub') else: removal_reason = 'Banned' if str( member.id) in banned_user_ids else 'Unsub' await self._remove_user_from_whitelist(mc_user_uuid) await member.remove_roles(Object(self._managed_role_id), reason=removal_reason)
def parse_user_at(text, serverid): if text.startswith('<@!'): id = text[3:-1] elif text.startswith('<@'): id = text[2:-1] elif text.startswith('<'): id = text[1:-1] else: raise RuntimeError('No user found') user = Object(id) user.server = Object(serverid) return user
async def notify_admin(self, message: str, filename: str = None): admin_channel = self._config["discord"]["admin_channel"] if admin_channel: if not filename: await self.send_message(Object(admin_channel), message) else: try: await self.send_file(Object(admin_channel), filename, content=message) except Exception as e: self.send_message( Object(admin_channel), "An exception occurred while trying to upload {}:\n\n```{}```\n\nAttached message: {}".format(filename, e, message) )
async def on_reaction_add(self, reaction, user): if reaction.emoji != "✨": return if reaction.message.guild.id != 250309924096049164: return proxy_ctx = Object(id=None) proxy_ctx.guild = reaction.message.guild proxy_ctx.author = user proxy_ctx.bot = self.bot if not await checks.has_level(proxy_ctx, "mod"): return role = reaction.message.guild.get_role(346083000372428811) await self.bot.error_hook.send(role) await reaction.message.author.add_roles(role)
def setUp(self): super().setUp() self.ctx = Object(id=0) self.bot = Object(id=0) self.message = Object(id=TEST_MESSAGE_ID) self.message.content = TEST_RESPONSE self.user = Object(id=TEST_USER_ID) self.message.author = self.user self.bot.wait_for = get_mock_coro(self.message) self.channel = Object(id=TEST_CHANNEL_ID) self.channel.send = get_mock_coro(None) self.ctx.bot = self.bot self.ctx.channel = self.channel self.ctx.guild = None self.ctx.author = self.user
async def catchup(self, ctx, after_id: int = None): emote = self.get_emote() if after_id is None: after = None else: after = Object(after_id).created_at messages = [] for chl in ctx.guild.text_channels: print(chl.name) async for msg in chl.history(limit=None, after=after, oldest_first=True): for react in msg.reactions: if react.emoji == emote: # Check if there's a self-react self_react_check = msg.author in await react.users( ).flatten() # If self-react, subtract 1 from counter react_num = react.count - self_react_check if react_num >= self.threshold: print(react_num) # TODO: Need to put into more permanent structure messages.append((int(msg.created_at.timestamp()), msg, react_num)) break print(f"{len(messages)} messages total") for _, msg, react_num in sorted(messages, key=lambda item: item[0]): await self.make_new(msg, react_num)
async def _deactivate_infraction( self, infraction_object: Dict[str, Union[str, int, bool]]) -> None: """ A co-routine which marks an infraction as inactive on the website. This co-routine does not cancel or un-schedule an expiration task. """ guild: Guild = self.bot.get_guild(constants.Guild.id) user_id = infraction_object["user"] infraction_type = infraction_object["type"] await self.bot.api_client.patch('bot/infractions/' + str(infraction_object['id']), json={"active": False}) if infraction_type == "mute": member: Member = guild.get_member(user_id) if member: # remove the mute role self.mod_log.ignore(Event.member_update, member.id) await member.remove_roles(self._muted_role) else: log.warning(f"Failed to un-mute user: {user_id} (not found)") elif infraction_type == "ban": user: Object = Object(user_id) try: await guild.unban(user) except NotFound: log.info( f"Tried to unban user `{user_id}`, but Discord does not have an active ban registered." )
async def on_typing(channel, user, when): if isinstance(user, Member): guild = user.guild donator_profile, _ = await get_features( Object(id=guild.owner_id), guild=guild) if donator_profile.features.get("premium"): if await cache_get(f"channel_typing:{guild.id}:{user.id}", primitives=True): return options = await get_guild_value( guild, ["persistRoles", DEFAULTS.get("persistRoles")], ["magicRoles", {}]) persist_roles = options["persistRoles"] magic_roles = options["magicRoles"] if persist_roles: await cache_set(f"channel_typing:{guild.id}:{user.id}", True, expire=7200) if not has_magic_role(user, magic_roles, "Bloxlink Bypass"): try: await guild_obligations(user, guild, join=True, dm=False, event=False) except CancelCommand: pass
async def unban(self, ctx, id: int, *, reason: str = None): '''Unban a member. `reason` will show up in the audit log\n **Example:```yml\n♤unban 608367259123187741 being a good boi```** ''' await ctx.message.delete() try: ban = await ctx.guild.fetch_ban(Object(id=id)) user = ban.user except discord.NotFound: return await ctx.send(f'**`{id}`** has not been banned.', delete_after=5) await ctx.guild.unban(user, reason=reason) embed = Embed( description=f'**{Emoji.hammer} {user} has been unbanned.**', color=utils.Color.green) embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url) if reason: embed.add_field(name='Reason', value=f'*{reason}*') msg = await ctx.send(embed=embed) await self._log(user, ctx.guild, msg, 'unban', reason if reason else '') await self.bot.cogs['Logging'].on_member_unban(ctx.author, user, reason)
async def subscribe_command( self, ctx: Context, *_) -> None: # We don't actually care about the args """Subscribe to announcement notifications by assigning yourself the role.""" has_role = False for role in ctx.author.roles: if role.id == Roles.announcements: has_role = True break if has_role: await ctx.send(f"{ctx.author.mention} You're already subscribed!") return log.debug( f"{ctx.author} called !subscribe. Assigning the 'Announcements' role." ) await ctx.author.add_roles(Object(Roles.announcements), reason="Subscribed to announcements") log.trace(f"Deleting the message posted by {ctx.author}.") await ctx.send( f"{ctx.author.mention} Subscribed to <#{Channels.announcements}> notifications.", )
async def global_ban(self, ctx, users: commands.Greedy[int], *, reason: str = None): """ Bans a user from every server Jerbot is deployed in. Dangerous. Owner only. Only use in the event that a user is a known raider or part of a spambot. """ edit_message = await ctx.send( "Global ban in progress. This may take a long time.") if len(users) == 0 and len(ctx.message.attachments) > 0: for attachment in ctx.message.attachments: if attachment.filename.endswith(".txt"): userlist = requests.get(attachment.url).text.split(" ") for line in userlist: line = line.split(" ") for user in line: users.append(int(user)) for guild in self.bot.guilds: try: for user in users: await guild.ban( Object(id=user), reason= f"Global ban: {reason if reason else 'No reason provided.'}" ) except discord.Forbidden: pass await edit_message.edit( content=f'Global ban successfully processed on {len(users)} users.' )
async def deregister(self, ctx: Context): """Deregister a user from the minecraft whitelist, allowing a new mc username to be registered. Also deletes entry in discord -> mc map. Args: ctx (Context): the discordpy context for this message """ if ctx.channel.name != self._monitor_channel: return author_id = str(ctx.message.author.id) if author_id not in self._working_discord_mc_mapping: fmt = '<@!{}> You not currently have a Minecraft account reigstered.' await ctx.channel.send(fmt.format(author_id)) return registered_uuid = uuid.UUID( self._working_discord_mc_mapping[author_id]['uuid']) await self._remove_user_from_whitelist(registered_uuid) # always remove entry from dc mc map self._working_discord_mc_mapping.pop(author_id) async with aiofiles.open(self._discord_mc_map_file_path, 'w') as dc_mc_map: await dc_mc_map.write( json.dumps(self._working_discord_mc_mapping, indent=4)) # demote user to lower mc role # remove managed role await ctx.message.author.remove_roles(Object(self._managed_role_id), reason='Deregister') # reload whitelist await self._send_to_minecraft_console('whitelist reload') # inform user deregister was successful fmt = '<@!{}> Minecraft account successfully deregistered.' await ctx.channel.send(fmt.format(author_id))
async def _deactivate_infraction(self, infraction_object): """ A co-routine which marks an infraction as inactive on the website. This co-routine does not cancel or un-schedule an expiration task. :param infraction_object: the infraction in question """ guild: Guild = self.bot.get_guild(constants.Guild.id) user_id = int(infraction_object["user"]["user_id"]) infraction_type = infraction_object["type"] if infraction_type == "mute": member: Member = guild.get_member(user_id) if member: # remove the mute role self.mod_log.ignore(Event.member_update, member.id) await member.remove_roles(self._muted_role) else: log.warning(f"Failed to un-mute user: {user_id} (not found)") elif infraction_type == "ban": user: Object = Object(user_id) await guild.unban(user) await self.bot.http_session.patch(URLs.site_infractions, headers=self.headers, json={ "id": infraction_object["id"], "active": False })
async def violate(self, v: Violation): # deterining current punishment punish_info = v.bucket["PUNISHMENT"] t = punish_info["TYPE"] self.bot.dispatch('spam_violation', v) key = f"{v.guild.id}-{v.member.id}-{v.bucket['TYPE']}" a = self.get_extra_actions(key) a.count += v.count # Punish and Clean GearbotLogging.log_key(v.guild.id, 'spam_violate', user=Utils.clean_user(v.member), user_id=v.member.id, check=v.check.upper(), friendly=v.friendly, channel=v.channel.mention, punishment_type=t) await self.punishments[t](v) if v.bucket.get("CLEAN", True): to_clean = AntiSpam._process_bucket_entries(v.offending_messages) by_channel = {} for (chan, msg) in to_clean: by_channel.setdefault(chan, []).append(msg) for (chan, msgs) in by_channel.items(): guild_chan = v.guild.get_channel(int(chan)) msgs = [Object(id=x) for x in msgs] if guild_chan is not None: # Ensure we only delete 100 at a time. Probably not necessary but you never know with people for group in Utils.chunks(msgs, 100): try: await guild_chan.delete_messages(group) except NotFound: pass await asyncio.sleep(v.bucket["SIZE"]["PERIOD"]) a = self.get_extra_actions(key) a.count -= v.count
async def ban(self, ctx, member: Option(Member, 'Ban a member of the server.', required=False), userid: Option(str, 'Ban any user by id.', required=False)): if not ctx.user.guild_permissions.ban_members: await ctx.respond(conf.get_string(ctx.user, 'insufficientUserPermissions'), ephemeral=True) return if not member and not userid: await ctx.respond('Must enter at least one of member or userid.', ephemeral=True) if member: user = member else: user = Object(userid) try: await ctx.guild.ban(user) except NotFound: await ctx.respond(f'User {user.id} could not be found', ephemeral=True) return except Forbidden: log.exception( f'Could not ban {user.id} from server {ctx.guild.id}. Check permissions.' ) await ctx.respond( 'Ban failed due to permissions issue. Do I have the "ban" permission?', ephemeral=True) return await ctx.respond(f'Banned user {user.id}.')
async def handle_exp(self, message, server_information): # TODO: multiplier for certain roles # get the required information and set exp exp_amount = server_information.get("exp_amount", bot_settings.default_exp["exp_amount"]) cooldown = server_information.get("exp_cooldown", bot_settings.default_exp["exp_cooldown"]) roles = server_information.get("exp_level_roles", bot_settings.default_exp["exp_level_roles"]) roles_blacklisted = server_information.get("exp_blacklist_roles", bot_settings.default_exp["exp_blacklist_roles"]) user_roles = [i.id for i in message.author.roles] if [i for i in roles_blacklisted if i["role_id"] in user_roles]: return await self.cache.set(f"{message.author.id} - {message.guild.id}exp", 1, expire=cooldown) result = await self.udb.set_setting_local( user_id=message.author.id, server_id=message.guild.id, query={"$inc": {"exp_amount": exp_amount}} ) cur_exp = result.get("exp_amount", 0) if roles: # filters out all roles which the user already has, and which they should get roles = [i for i in roles if i["value"] <= cur_exp and i["role_id"] not in user_roles] # creates a object for every role with the attribute id to use the edit function with it new_roles = utils._unique(Object(id=r["role_id"]) for r in roles) try: await message.author.add_roles(*new_roles, reason="Leveled roles") except errors.Forbidden: self.bot.logger.info(f"No permission to add leveled role in server " f"{server_information.get('server_id')}") except Exception as error: self.bot.logger.info(f"Error while adding exp role in server {server_information.get('server_id')}: " f"{error}") finally: return
async def transfer(self, ctx, member: Member, amount=None): """ Transfer x amout of credits to another member :param ctx: the discord context :param member: the target member for the transfer :param amount: the amout for the transfer """ localize = self.bot.localize(ctx) try: amount = round(float(amount)) if amount <= 0: raise ValueError except (ValueError, TypeError): await self.bot.say(localize['currency_bad_num']) else: try: await self.bot.say( await transfer( self.bot.data_manager, ctx.message.author, member, amount, localize ) ) if self.bot.config['API keys']['discordtel'] \ and member.id == '224662505157427200': await self.bot.send_message( Object('329013929890283541'), f'{ctx.message.author.id} sends {amount} credits.' ) except LowBalanceError as e: await self.bot.say(localize['low_balance'].format(str(e))) return
async def accept(self, ctx: Context, *_): # We don't actually care about the args """ Accept our rules and gain access to the rest of the server """ await ctx.author.add_roles(Object(VERIFIED_ROLE), reason="Accepted the rules") await ctx.message.delete()
async def accept_command(self, ctx: Context, *_): # We don't actually care about the args """ Accept our rules and gain access to the rest of the server """ log.debug( f"{ctx.author} called !accept. Assigning the 'Developer' role.") await ctx.author.add_roles(Object(Roles.verified), reason="Accepted the rules") try: await ctx.author.send(WELCOME_MESSAGE) except Exception: # Catch the exception, in case they have DMs off or something log.exception( f"Unable to send welcome message to user {ctx.author}.") log.trace(f"Deleting the message posted by {ctx.author}.") try: self.mod_log.ignore(Event.message_delete, ctx.message.id) await ctx.message.delete() except NotFound: log.trace( "No message found, it must have been deleted by another bot.")
async def unsubscribe_command( self, ctx: Context, *_) -> None: # We don't actually care about the args """Unsubscribe from announcement notifications by removing the role from yourself.""" has_role = False for role in ctx.author.roles: if role.id == Roles.announcements: has_role = True break if not has_role: await ctx.send(f"{ctx.author.mention} You're already unsubscribed!" ) return log.debug( f"{ctx.author} called !unsubscribe. Removing the 'Announcements' role." ) await ctx.author.remove_roles(Object(Roles.announcements), reason="Unsubscribed from announcements") log.trace(f"Deleting the message posted by {ctx.author}.") await ctx.send( f"{ctx.author.mention} Unsubscribed from <#{Channels.announcements}> notifications." )
async def update_message(bot, message_id, content): if is_cache_enabled(bot) and not Object( message_id).created_at <= datetime.utcfromtimestamp(time.time() - 5 * 60): await bot.redis_pool.hmset_dict(message_id, content=content) LoggedMessage.update(content=content).where( LoggedMessage.messageid == message_id).execute()