diff --git a/infochannel/infochannel.py b/infochannel/infochannel.py index 278a6d7..86d0df4 100644 --- a/infochannel/infochannel.py +++ b/infochannel/infochannel.py @@ -1,15 +1,18 @@ +import asyncio + import discord from redbot.core import Config, checks, commands from redbot.core.bot import Red from redbot.core.commands import Cog - # Cog: Any = getattr(commands, "Cog", object) # listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad # if listener is None: # def listener(name=None): # return lambda x: x +RATE_LIMIT_DELAY = 60 * 10 # If you're willing to risk rate limiting, you can decrease the delay + class InfoChannel(Cog): """ @@ -19,6 +22,7 @@ class InfoChannel(Cog): """ def __init__(self, bot: Red): + super().__init__() self.bot = bot self.config = Config.get_conf( self, identifier=731101021116710497110110101108, force_registration=True @@ -35,6 +39,8 @@ class InfoChannel(Cog): self.config.register_guild(**default_guild) + self._critical_section_wooah_ = 0 + @commands.command() @checks.admin() async def infochannel(self, ctx: commands.Context): @@ -70,9 +76,13 @@ class InfoChannel(Cog): return if channel is None: - await self.make_infochannel(guild) + try: + await self.make_infochannel(guild) + except discord.Forbidden: + await ctx.send("Failure: Missing permission to create voice channel") + return else: - await self.delete_infochannel(guild, channel) + await self.delete_all_infochannels(guild) if not await ctx.tick(): await ctx.send("Done!") @@ -83,6 +93,8 @@ class InfoChannel(Cog): """ Toggle different types of infochannels """ + if not ctx.invoked_subcommand: + pass @infochannelset.command(name="botcount") async def _infochannelset_botcount(self, ctx: commands.Context, enabled: bool = None): @@ -92,7 +104,10 @@ class InfoChannel(Cog): guild = ctx.guild if enabled is None: enabled = not await self.config.guild(guild).bot_count() + await self.config.guild(guild).bot_count.set(enabled) + await self.make_infochannel(ctx.guild) + if enabled: await ctx.send("InfoChannel for bot count has been enabled.") else: @@ -106,7 +121,10 @@ class InfoChannel(Cog): guild = ctx.guild if enabled is None: enabled = not await self.config.guild(guild).online_count() + await self.config.guild(guild).online_count.set(enabled) + await self.make_infochannel(ctx.guild) + if enabled: await ctx.send("InfoChannel for online user count has been enabled.") else: @@ -120,25 +138,49 @@ class InfoChannel(Cog): guild.me: discord.PermissionOverwrite(manage_channels=True, connect=True), } + # Remove the old info channel first + channel_id = await self.config.guild(guild).channel_id() + if channel_id is not None: + channel: discord.VoiceChannel = guild.get_channel(channel_id) + if channel: + await channel.delete(reason="InfoChannel delete") + + # Then create the new one channel = await guild.create_voice_channel( - "Placeholder", reason="InfoChannel make", overwrites=overwrites + "Total Humans:", reason="InfoChannel make", overwrites=overwrites ) await self.config.guild(guild).channel_id.set(channel.id) if botcount: + # Remove the old bot channel first + botchannel_id = await self.config.guild(guild).botchannel_id() + if channel_id is not None: + botchannel: discord.VoiceChannel = guild.get_channel(botchannel_id) + if botchannel: + await botchannel.delete(reason="InfoChannel delete") + + # Then create the new one botchannel = await guild.create_voice_channel( - "Placeholder", reason="InfoChannel botcount", overwrites=overwrites + "Bots:", reason="InfoChannel botcount", overwrites=overwrites ) await self.config.guild(guild).botchannel_id.set(botchannel.id) if onlinecount: + # Remove the old online channel first + onlinechannel_id = await self.config.guild(guild).onlinechannel_id() + if channel_id is not None: + onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id) + if onlinechannel: + await onlinechannel.delete(reason="InfoChannel delete") + + # Then create the new one onlinechannel = await guild.create_voice_channel( - "Placeholder", reason="InfoChannel onlinecount", overwrites=overwrites + "Online:", reason="InfoChannel onlinecount", overwrites=overwrites ) await self.config.guild(guild).onlinechannel_id.set(onlinechannel.id) await self.update_infochannel(guild) - async def delete_infochannel(self, guild: discord.Guild, channel: discord.VoiceChannel): + async def delete_all_infochannels(self, guild: discord.Guild): guild_data = await self.config.guild(guild).all() botchannel_id = guild_data["botchannel_id"] onlinechannel_id = guild_data["onlinechannel_id"] @@ -151,6 +193,7 @@ class InfoChannel(Cog): await botchannel.delete(reason="InfoChannel delete") if onlinechannel_id is not None: await onlinechannel.delete(reason="InfoChannel delete") + await self.config.guild(guild).clear() async def update_infochannel(self, guild: discord.Guild): @@ -162,23 +205,23 @@ class InfoChannel(Cog): # bots = lambda x: x.bot # def bots(x): return x.bot - num = len([m for m in guild.members if m.bot]) - bot_msg = f"Bots: {num}" + bot_num = len([m for m in guild.members if m.bot]) + # bot_msg = f"Bots: {num}" # Gets count of online users members = guild.member_count offline = len(list(filter(lambda m: m.status is discord.Status.offline, guild.members))) - num = members - offline - online_msg = f"Online: {num}" + online_num = members - offline + # online_msg = f"Online: {num}" # Gets count of actual users total = lambda x: not x.bot - num = len([m for m in guild.members if total(m)]) - human_msg = f"Total Humans: {num}" + human_num = len([m for m in guild.members if total(m)]) + # human_msg = f"Total Humans: {num}" channel_id = guild_data["channel_id"] if channel_id is None: - return + return False botchannel_id = guild_data["botchannel_id"] onlinechannel_id = guild_data["onlinechannel_id"] @@ -188,28 +231,53 @@ class InfoChannel(Cog): onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id) if guild_data["member_count"]: - name = "{} ".format(human_msg) + name = f"{channel.name.split(':')[0]}: {human_num}" + await channel.edit(reason="InfoChannel update", name=name) if botcount: - name = "{} ".format(bot_msg) + name = f"{botchannel.name.split(':')[0]}: {bot_num}" await botchannel.edit(reason="InfoChannel update", name=name) if onlinecount: - name = "{} ".format(online_msg) + name = f"{onlinechannel.name.split(':')[0]}: {online_num}" await onlinechannel.edit(reason="InfoChannel update", name=name) + async def update_infochannel_with_cooldown(self, guild): + """My attempt at preventing rate limits, lets see how it goes""" + if self._critical_section_wooah_: + if self._critical_section_wooah_ == 2: + print("Already pending, skipping") + return # Another one is already pending, don't queue more than one + print("Queuing another update") + self._critical_section_wooah_ = 2 + + while self._critical_section_wooah_: + await asyncio.sleep( + RATE_LIMIT_DELAY // 4 + ) # Max delay ends up as 1.25 * RATE_LIMIT_DELAY + + print("Issuing queued update") + return await self.update_infochannel_with_cooldown(guild) + + print("Entering critical") + self._critical_section_wooah_ = 1 + await self.update_infochannel(guild) + await asyncio.sleep(RATE_LIMIT_DELAY) + self._critical_section_wooah_ = 0 + print("Exiting critical") + @Cog.listener() async def on_member_join(self, member: discord.Member): - await self.update_infochannel(member.guild) + await self.update_infochannel_with_cooldown(member.guild) @Cog.listener() async def on_member_remove(self, member: discord.Member): - await self.update_infochannel(member.guild) + await self.update_infochannel_with_cooldown(member.guild) @Cog.listener() async def on_member_update(self, before: discord.Member, after: discord.Member): onlinecount = await self.config.guild(after.guild).online_count() if onlinecount: if before.status != after.status: - await self.update_infochannel(after.guild) + await self.update_infochannel_with_cooldown(after.guild)