async def reload_(self, ctx, *extensions): if "*" in extensions: title = "Reloading all extensions" elif len(extensions) > 1: title = "Reloading extensions" else: title = f"Reloading `{extensions[0]}`" embed = make_embed(color=colors.EMBED_INFO, title=title) m = await ctx.send(embed=embed) color = colors.EMBED_SUCCESS description = "" if "*" in extensions: extensions = get_extensions() for extension in extensions: try: self.bot.unload_extension("cogs." + extension) except commands.ExtensionNotLoaded: pass try: self.bot.load_extension("cogs." + extension) description += f"Successfully loaded `{extension}`.\n" except: color = colors.EMBED_ERROR description += f"Failed to load `{extension}`.\n" _, exc, _ = sys.exc_info() if not isinstance(exc, ImportError): await report_error(ctx, exc, *extensions) description += "Done." await m.edit(embed=make_embed(color=color, title=title.replace("ing", "ed"), description=description))
async def designate_channel(ctx, channel_name, current_channel, *, new_channel, setter, change_warning=''): if current_channel and new_channel.id == current_channel.id: await ctx.send(embed=make_embed( color=colors.EMBED_INFO, title=channel_name.capitalize(), description= f"{current_channel.mention} is already the {channel_name}.")) else: if current_channel is None: description = f"Set {new_channel.mention} as the {channel_name}?" else: description = f"Change the {channel_name} from {current_channel.mention} to {new_channel.mention}? {change_warning}" m = await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title=f"Set {channel_name}?", description=description)) response = await react_yes_no(ctx, m) if response == 'y': setter(new_channel) description = f"The {channel_name} is now {new_channel.mention}." else: description = None title = f"{channel_name.capitalize()} change {YES_NO_HUMAN_RESULT[response]}" await m.edit(embed=make_embed(color=YES_NO_EMBED_COLORS[response], title=title, description=description))
async def add_currency(self, ctx, currency_name: str, color: discord.Color, *aliases: str): """Create a new currency.""" game = get_game(ctx) currency_name = currency_name.lower() aliases = list(map(str.lower, aliases)) for s in [currency_name] + aliases: if game.get_currency(s): raise commands.UserInputError( f"Currency name '{s}' is already used.") description = f"Color: {format_discord_color(color)}" description += "\nAliases: " + (", ".join(f"`{a}`" for a in aliases) or "(none)") m = await ctx.send( embed=make_embed(color=colors.EMBED_ASK, title=f"Create currency '{currency_name}'?", description=description)) response = await react_yes_no(ctx, m) await m.edit( embed=make_embed(color=YES_NO_EMBED_COLORS[response], title=f"Currency '{currency_name}' established" if response else "Currency creation " + YES_NO_HUMAN_RESULT[response], description=description)) if response != 'y': return game.add_currency(currency_name, color=color, aliases=aliases)
async def shutdown_(self, ctx, noconfirm=False): if noconfirm: result = "y" else: m = await ctx.send(embed=make_embed( color=colors.EMBED_ASK, title="Shutdown?", description= "This action may be difficult to undo without phsyical or remote access to the host machine. Are you sure?", )) result = await react_yes_no(ctx, m) await (ctx.send if noconfirm else m.edit)(embed=make_embed( color={ "y": colors.EMBED_INFO if noconfirm else colors.EMBED_CONFIRM, "n": colors.EMBED_CANCEL, "t": colors.EMBED_TIMEOUT, }[result], title={ "y": "Shutting down...", "n": "Shutdown cancelled.", "t": "Shutdown timed out.", }[result], )) if result is "y": l.info( f"Shutting down at the command of {ctx.message.author.display_name}..." ) await self.bot.logout()
async def active_cutoff(self, ctx, new_cutoff: int = None): """Set or view the the active user cutoff time period. `new_cutoff` must be specified as an integer number of hours. """ game = get_game(ctx) description = f"The current active user cutoff is **{format_hour_interval(game.active_cutoff)}**." if new_cutoff is None: await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title="Active user cutoff time", description=description)) return description += f" Change it to **{format_hour_interval(new_cutoff)}**?" m = await ctx.send( embed=make_embed(color=colors.EMBED_ASK, title="Change active user cutoff time?", description=description)) response = await react_yes_no(ctx, m) if response == 'y': game.active_cutoff = new_cutoff game.save() await m.edit(embed=make_embed( color=YES_NO_EMBED_COLORS[response], title= f"Active user cutoff time change {YES_NO_HUMAN_RESULT[response]}"))
async def reload_(self, ctx, *extensions): if (utils.check_perms(ctx.author.id)): if "*" in extensions: title = "Reloading all extensions" elif len(extensions) > 1: title = "Reloading extensions" else: title = f"Reloading {extensions[0]}" embed = make_embed(author_url=[title, "", config.THUMBNAIL], color=config.EMBED_INFO) #, title=title m = await ctx.send(embed=embed) color = config.EMBED_SUCCESS fields = [[f"", '\u200b']] if "*" in extensions: extensions = list(self.bot.extensions.keys()) for extension in extensions: self.bot.unload_extension(extension) try: self.bot.load_extension(extension) #fields.append([f"+ {extension}", '\u200b']) fields[0][0] += f"+ {extension}\n" except: color = config.EMBED_ERROR #fields.append([f"- {extension}", '\u200b']) fields[0][0] += f"- {extension}\n" #description += "Done." await m.edit(embed=make_embed( author_url=[title.replace("ing", "ed"), "", config.THUMBNAIL], fields=fields, color=color)) #title=title.replace("ing", "ed")
async def edit_channel(self, ctx, value: str): settings = {'yes': 1, 'no': 0} value_num = settings.get(value.lower()) if value_num != None: db = sqltils.DbConn(db_file, ctx.guild.id, "setting") # if setting is already there old value needs to be deleted if len(db.search_table(value="edit_channel", column="setting") ) == 1: # there can only be one archive and log db.remove_line('"edit_channel"', column="setting") entry = ("edit_channel", "value_name", value_num, time.strftime("%Y-%m-%d %H:%M:%S"), config.VERSION_SQL) db.write_server_table(entry) emby = utils.make_embed( color=discord.Color.green(), name="Success", value=f"Channel Creator can edit channel-name: {value}") await ctx.send(embed=emby) else: emby = utils.make_embed( color=discord.Color.orange(), name="Missing argument", value=f"Please enter `yes` or `no` as argument.") await ctx.send(embed=emby)
async def undesignate_channel(ctx, channel_name, current_channel, *, deleter, remove_warning=''): if current_channel is None: await ctx.send(embed=make_embed( color=colors.EMBED_INFO, title=channel_name.capitalize(), description=f"There already is no {channel_name}.")) else: m = await ctx.send(embed=make_embed( color=colors.EMBED_ASK, title=f"Reset {channel_name}?", description= f"Are you sure you want to reset the {channel_name}? {remove_warning}" )) response = await react_yes_no(ctx, m) if response == 'y': deleter() await m.edit(embed=make_embed( color=YES_NO_EMBED_COLORS[response], title= f"{channel_name.capitalize()} reset {YES_NO_HUMAN_RESULT[response]}", description=f"There is now no {channel_name}" if response == 'y' else None))
async def remove_proposal(self, user, *proposal_nums, reason='', m=None): if m: human_proposals = f"{len(proposal_nums)} proposal{'s' * (len(proposal_nums) != 1)}" title = f"Removing {human_proposals}\N{HORIZONTAL ELLIPSIS}" await m.edit(embed=make_embed( color=colors.EMBED_INFO, title=title, description="Removing proposals\N{HORIZONTAL ELLIPSIS}" )) number_sequence = list(range(1, self.proposal_count + 1)) for proposal_num in proposal_nums: proposal = self.get_proposal(proposal_num) del self.proposals[str(proposal_num)] number_sequence.remove(proposal_num) nomic.logging.add_to_proposal_log(self.guild, event_name='remove_proposal', user_id=user.id, proposal_number=proposal_num, reason=reason, ) try: message = await self.proposal_channel.get_message(proposal.get('message')) await message.delete() except: pass self.proposal_count = len(number_sequence) if number_sequence: if m: await m.edit(embed=make_embed( color=colors.EMBED_INFO, title=title, description="Renumbering remaining proposals\N{HORIZONTAL ELLIPSIS}" )) moved_proposals = [] for i in range(len(number_sequence)): old_num = str(number_sequence[i]) new_num = str(i + 1) if old_num != new_num: self.proposals[new_num] = self.proposals[old_num] del self.proposals[old_num] self.proposals[new_num]['n'] = new_num moved_proposals.append(new_num) nomic.logging.add_to_proposal_log(self.guild, event_name='renumber', user_id=user.id, proposal_number=old_num, new_number=new_num, ) self.save() await self.refresh_proposal(*moved_proposals) else: self.save() if m: await m.edit(embed=make_embed( color=colors.EMBED_SUCCESS, title=f"Removed {human_proposals}" ))
async def channel_from_input( ctx, channel_type: str, channel_id: str) -> Union[Tuple[str, str], Tuple[None, None]]: """ Get the channel for a given channel id, send error message if no channel was found\n - checks if channel exists\n - checks if type matches the given setting\n :param ctx: discord.Context to send possible help message and to extract guild id :param channel_type: channel type searched, like log_channel. Matched with names from settings dict :param channel_id: channel id that was given :returns: (channel id as string, best way to mention / name channel) if found, else (None, None) """ set_channel = await Settings.validate_channel(ctx, channel_id) if set_channel is None: return None, None # check if channel type and wanted setting match if type(set_channel ) == discord.TextChannel and channel_type == "log_channel": return str(set_channel.id), set_channel.mention elif type( set_channel ) == discord.CategoryChannel and channel_type == "archive_category": return str(set_channel.id), set_channel.name elif type(set_channel) == discord.VoiceChannel and channel_type in [ 'public_channel', 'private_channel', 'static_channel' ]: # check if max for tracked channels is reached if not settings_db.is_track_limit_reached(ctx.guild.id, channel_type): return str(set_channel.id), set_channel.name await ctx.send(embed=utils.make_embed( color=utils.orange, name="Too many entries", value= f"Hey, you can't make me watch more than {CHANNEL_TRACK_LIMIT} channels for this setting\n" f"If you wanna change the channels I watch use `{PREFIX}ds [channel-id]` " f"to remove a channel from your settings")) return None, None else: # gained channel and required type didn't match await ctx.send(embed=utils.make_embed( name='Not a voice channel', value= f"The channel {set_channel.name} is no {channel_type.replace('_', ' ')}, " f"please enter a valid channel-id", color=utils.yellow)) return None, None
async def delete_settings(self, ctx: commands.Context, value: str): """ remove tracked channel from database :param ctx: command context :param value: id of the channel to be deleted """ if not value: emby = utils.make_embed( color=utils.orange, name="No input", value=f"This function requires exactly one input:\n" "`channel-id` please give a valid channel ID as argument to remove that channel from" "the list of watched channels.\n" f"You can get a list of all watched channels with `{PREFIX}gs`" ) await ctx.send(embed=emby) return # get channel setting or setting string setting_setting = settings.get(value, None) # name of setting value_setting = utils.extract_id_from_message( value) # id / value of setting if not (setting_setting or value_setting): emby = utils.make_embed( color=utils.orange, name="No valid setting", value="It seems like you didn't give me a valid channel ID or " "setting name to work with") await ctx.send(embed=emby) return # all checks passed - removing that entry if setting_setting: settings_db.del_setting_by_setting(ctx.guild.id, setting_setting) elif value_setting: # We don't know if the setting we aim for is located in channels-db (like static channel) # or in settings db like everything else, but that's okay, we just delete the setting in bot DBs channels_db.del_channel( value_setting) # here if it's a static channel settings_db.del_setting_by_value( ctx.guild.id, str(value_setting)) # here if it's something else # TODO: If a static channel is in use when deleted from db the text-channel will stay, how to fix this? # channel only applied to setting by value channel = ctx.guild.get_channel(value_setting) await ctx.send(embed=utils.make_embed( color=utils.green, name="Deleted", value=f"Removed " f"`{channel.name if channel else setting_setting}` from settings"))
async def set_archive(self, ctx, setting: str, value: str): # possible settings switch -returns same value but nothing if key isn't valid settings = { "archive": utils.get_chan(ctx.guild, value), "log": utils.get_chan(ctx.guild, value) } # trying to get a corresponding channel / id value = settings.get(setting) # if value is "None" this means that there is no such setting or no such value for it # checking if keyword matches the entered channel type # -> ensures that the process of getting a correct setting has worked # set channels if value is not None and value.type == discord.ChannelType.text and setting == "log" \ or value.type == discord.ChannelType.category and setting == "archive": # connecting to db - creating a new one if there is none yet db = sqltils.DbConn(db_file, ctx.guild.id, "setting") # Settings won't be stored if max watched channels are reached # -> searching for amount of matching entries if len(db.search_table(value=setting, column="setting") ) >= 1: # there can only be one archive and log text = f"Hey, you can only have one archive and log at once\n \ If you wanna change those settings use `{config.PREFIX}ds [channel-id]` to remove a channel from your settings" emby = utils.make_embed(color=discord.Color.orange(), name="Too many entries", value=text) await ctx.send(embed=emby) # writing entry to db - the way things should go else: entry = (setting, "value_name", value.id, time.strftime("%Y-%m-%d %H:%M:%S"), config.VERSION_SQL) db.write_server_table(entry) emby = utils.make_embed(color=discord.Color.green(), name="Success", value="Setting saved") await ctx.send(embed=emby) # when false inputs were given else: value = ("Please ensure that you've entered a valid setting \ and channel-id or role-id for that setting.") emby = utils.make_embed(color=discord.Color.orange(), name="Can't get setting", value=value) await ctx.send(embed=emby)
async def break_out_rooms(self, ctx: discord.ext.commands.Context, *split: str): # check if invoker is in a channel if not ctx.author.voice: await hp.send_embed(ctx, embed=utils.make_embed("You're not in a VoiceChannel", discord.Color.orange(), value="Please enter a Voice Channel and try again")) return # ensuring members var is given and is int try: # check can fail when typecast fails or number too small if int(split[0]) > 0: split = int(split[0]) # saving integer in variable # number too small else: await hp.send_embed(ctx, embed=utils.make_embed("Number must be greater than 0", discord.Color.orange(), value=f"Try again :wink:\nExample: `{config.PREFIX}opro 4`")) return # type conversion failed except (IndexError, ValueError): await hp.send_embed(ctx, embed=utils.make_embed("Wrong argument", discord.Color.orange(), value=f"Please enter the amount of members per channel\n" f"Example: `{config.PREFIX}opro 4`")) return # channel invoker is based in base_vc: discord.VoiceChannel = ctx.author.voice.channel # making a copy of members in channel - used to move members members: List[discord.Member] = base_vc.members.copy() members.remove(ctx.author) # invoker should not be moved to break-out-room random.shuffle(members) # shuffling list overwrites = base_vc.category.overwrites # settings for new channels mv_channel = None # temp var for holding current created channel for i in range(len(members)): """ Iterating trough members of VC Creating new channel when last one filled Moving members """ if i % split == 0: mv_channel, _ = await make_channel(ctx.author.voice, members[i], overwrites, vc_name=f"Breakout Room {i // split + 1}", tc_name=f"Breakout Room {i // split + 1}", channel_type="brout") if members[i].voice is None: continue await members[i].move_to(mv_channel, reason="Moved to breakout room")
async def _submit_proposal(self, ctx, content): game = get_game(ctx) m = await ctx.send(embed=make_embed(color=colors.EMBED_ASK, title="Submit new proposal?", description=content)) response = await react_yes_no(ctx, m) if ctx.channel.id == game.proposal_channel.id: await m.delete() else: await m.edit(embed=make_embed( color=YES_NO_EMBED_COLORS[response], title=f"Proposal submission {YES_NO_HUMAN_RESULT[response]}")) if response != 'y': return await game.submit_proposal(ctx, content.strip())
async def create(self, ctx, name, *, times=""): """Create an event. If no times are given, the event will only be able to be triggered manually. Otherwise, you can schedule triggers, like `3 hours later` or `12:30PM`, seperated by commas. If you have a time added with the `time` command, your timezone will be assumed. Otherwise, UTC will be used unless you specify a timezone like `at 12:30PM EST`. Example usage: `event create game in two minutes, 6PM` """ if name.lower() in event_config: await show_error(ctx, "That event already exists.") event = Event(name, [], [], ctx.author.id, []) await self.parse_times(ctx, event, times) event_config[name.lower()] = event saving = {} for event in event_config: saving[event] = list(event_config[event]) save_json(paths.EVENT_SAVES, saving) await ctx.send( embed=make_embed( title="Added event", description="Your event was created successfully.", color=colors.EMBED_SUCCESS, ) )
async def update(self, ctx): """Runs `git pull` to update the bot.""" subproc = await asyncio.create_subprocess_exec('git', 'pull', stdout=PIPE) embed = make_embed( color=colors.EMBED_INFO, title="Running `git pull`" ) m = await ctx.send(embed=embed) returncode = await subproc.wait() embed.color = colors.EMBED_ERROR if returncode else colors.EMBED_SUCCESS stdout, stderr = await subproc.communicate() fields = [] if stdout: embed.add_field( name="Stdout", value=f"```\n{stdout.decode('utf-8')}\n```", inline=False ) if stderr: embed.add_field( name="Stderr", value=f"```\n{stderr.decode('utf-8')}\n```", inline=False ) if not (stdout or stderr): embed.description = "`git pull` completed." await m.edit(embed=embed) await self.reload_(ctx, '*')
async def get_settings(self, ctx): """ prints set channels """ db = sqltils.DbConn(db_file, ctx.guild.id, "setting") results = db.read_server_table() #gets a list ob entry objects pub = "__Public Channels:__\n" priv = "__Private Channels:___\n" log = "__Log Channel__\n" archive = "__Archive Category__\n" for i in range(len(results)): #building strings if results[i].setting == "pub-channel": pub += f"`{ctx.guild.get_channel(results[i].value_id)}` with ID `{results[i].value_id}`\n" elif results[i].setting == "priv-channel": priv += f"`{ctx.guild.get_channel(results[i].value_id)}` with ID `{results[i].value_id}`\n" elif results[i].setting == "log": log += f"`{ctx.guild.get_channel(results[i].value_id)}` with ID `{results[i].value_id}`\n" elif results[i].setting == "archive": archive += f"`{ctx.guild.get_channel(results[i].value_id)}` with ID `{results[i].value_id}`\n" emby = utils.make_embed(color=discord.Color.green(), name="Server Settings", value=f"\n{pub}\n {priv}\n{archive}\n{log}") await ctx.send(embed=emby)
async def on_error(function, *args, **kwargs): """ Overwriting error handler from discord.py """ # exception type exception = sys.exc_info()[1] # traceback text tb_text = f'Error in: {function}\n{exception}\nTraceback (most recent call last):\n' \ + "".join(traceback.format_tb(sys.exc_info()[2])) + f"{exception}" print("----------\n") print(tb_text) logging.error(tb_text) # sending message to member when channel creation process fails if function == "on_voice_state_update" and isinstance( exception, discord.errors.Forbidden): member = args[0] # member is first arg that is passed in # check if it's the error we expect if tb_text.find("create_voice_channel") != -1: await member.send(embed=utils.make_embed( name=f"Something's wrong with my permissions", value="I can't prepare (create & edit) a channel for you " "or I can't move you.\n" "Please check whether a channel was created and inform the server team " "about that problem.\n I'm sorry! :confused:", color=discord.Color.red()))
async def do_it(include_url): description = '\n'.join( map(lambda m: self.get_proposal_message(ctx, m, include_url), proposal_nums)) await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title="Proposal information", description=description))
async def list(self, ctx, event: Event): await ctx.send(embed=make_embed( title="Managers", description="\n".join( [self.bot.get_user(x).mention for x in event.managers]), color=colors.EMBED_SUCCESS, ))
async def about(self, ctx): await ctx.send(embed=make_embed( title=f"About {info.NAME}", description=info.ABOUT_TEXT, fields=[("Author", f"[{info.AUTHOR}]({info.AUTHOR_LINK})", True), ("GitHub Repository", info.GITHUB_LINK, True)], footer_text=f"{info.NAME} v{info.VERSION}"))
async def validate_toggle( ctx: commands.Context, decision: str) -> Union[Tuple[str, str], Tuple[None, None]]: """ Check if value matches yes_or_no toggle dict\n Send error if mapping doesn't work :param ctx: context of the command, used to send a possible message :param decision: 'boolean' input of the user to map to True or False :returns: ('1' or '0', descriptive string if user can or can't edit) if its valid, else (None, None) """ yes_or_no = yes_no_dict.get(decision, None) if yes_or_no is not None: return str( yes_or_no ), "creator can edit" if yes_or_no else " creator can _not_ edit" await ctx.send(embed=utils.make_embed( name="Can't extract your decision", value="You needed to either enter `yes` or `no` as valid state.\n" "I can' figure out whether you want to allow or disable " "that the channel creator can edit the created channel.\n" "Use `yes` to allow it or `no` to prohibit.", color=utils.yellow, footer= "It's only recommend to enable that feature if you trust your members " "and / or have an active moderation team!")) return None, None
async def refresh_proposal(self, ctx, *proposal_nums: int): """Refresh one or more proposal messages. This is mostly useful for fixing minor glitches, or if voting rules have changed. """ if not proposal_nums: await invoke_command_help(ctx) return game = get_game(ctx) proposal_nums = dedupe(proposal_nums) succeeded, failed = await game.refresh_proposal(*proposal_nums) description = '' if succeeded: if len(succeeded) == 1: description += f"Proposal {succeeded[0]} succeessfully refreshed.\n" else: description += f"{len(succeeded)}/{len(proposal_nums)} proposal messages succeessfully refreshed.\n" if failed: description += f"Proposal{'' if len(failed) == 1 else 's'} {human_list(map(str, failed))} could not be refreshed.\n" m = await ctx.send(embed=make_embed( color=colors.EMBED_ERROR if failed else colors.EMBED_SUCCESS, title="Refreshed proposal messages", description=description)) await game.wait_delete_if_illegal(ctx.message, m)
async def restart_bot(self, ctx): if (utils.check_perms(ctx.author.id)): m = await ctx.send( embed=make_embed(title="Restarting...", color=0xff00ff)) msg_id = m.id msg_channel_id = m.channel.id msg_guild_id = m.guild.id print(m) print(m.id) print(m.channel.id) print(m.guild.id) #print(await self.bot.get_guild(msg_guild_id).get_channel(msg_channel_id).fetch_message(msg_id).edit("this")) # channel = self.bot.get_guild(msg_guild_id).get_channel(msg_channel_id) #channel = guild.get_channel(msg_channel_id) # msg = await channel.fetch_message(msg_id) # await msg.edit(embed=make_embed(title="Restarted.", color=0xff00ff)) with open('restart.dat', 'w+') as f: f.write(f"True\n{m.guild.id}\n{m.channel.id}\n{m.id}") #f.write('\n') #f.write(str(m)) #pickle.dump(m, f, -1) #await ctx.send("Hi!") await self.bot.logout() await self.bot.close()
async def prefix_validation( ctx: commands.Context, new_prefix: str) -> Union[Tuple[str, str], Tuple[None, None]]: """ Validate that a prefix fit the set criteria, send error message if it does not match\n Criteria:\n - shorter or equal to env variable MAX_PREFIX_LENGTH\n - ends on a non-word character like '!' :param ctx: context of the command, used to send a possible message :param new_prefix: string to validate :returns: (prefix, prefix) if its valid, else (None, None) """ example_prefix = f"A valid prefix would be `{'f' * (MAX_PREFIX_LENGTH - 1)}!` or just `?`" # validate length and scheme # example: r"^\w{0,3}\W$" (the upper limit can be changed dynamically) pattern = re.compile(r"^\w{0," + str(MAX_PREFIX_LENGTH - 1) + r"}\W$") if re.search(pattern, new_prefix) is not None: return new_prefix, new_prefix # prefix shall be entered to db and be sent in message await ctx.send(embed=utils.make_embed( name="Prefix should be short and easy to remember", value= f"The length limit for this bot is {MAX_PREFIX_LENGTH - 1} letters or digits, " f"followed by one 'non-word' character like !, ?, ~\n" f"{example_prefix}", color=utils.yellow)) return None, None
async def list_active_users(self, ctx, *users: discord.Member): """List only users that are active (or those specified).""" game = get_game(ctx) description = '' users = list(users) if not users: for user_id in game.player_last_seen: try: user = ctx.guild.get_member(int(user_id)) if self.is_active(ctx, user): users.append(user) except: pass users = sorted( sorted(users, key=member_sort_key(ctx)), key=lambda member: self.last_seen_diff(ctx, member) or 1e9) for user in users: last_seen = self.last_seen_diff(ctx, user) if last_seen is None: last_seen_text = "never" elif last_seen < 2: last_seen_text = "very recently" else: last_seen_text = f"about {format_hour_interval(self.last_seen_diff(ctx, user))} ago" description += f"{user.mention} was last seen **{last_seen_text}**" if self.is_active(ctx, user): description += " _(active)_" else: description += " _(inactive)_" description += "\n" await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title=f"Active users ({len(users)})", description=description))
async def submit_proposal(self, ctx, content): self.proposal_count += 1 m = await self.proposal_channel.send(embed=make_embed( color=colors.EMBED_INFO, title=f"Preparing proposal #{self.proposal_count}\N{HORIZONTAL ELLIPSIS}" )) timestamp = datetime.utcnow() mutset(self.guild_data, ['proposals', str(self.proposal_count)], { 'n': self.proposal_count, 'author': ctx.author.id, 'content': content, 'message': m.id, # 'status' can be 'voting', 'passed', or 'failed' 'status': 'voting', 'votes': { 'for': {}, 'against': {}, 'abstain': {}, }, 'timestamp': timestamp.timestamp(), }) self.save() nomic.logging.add_to_proposal_log(self.guild, timestamp=timestamp, event_name='submit', user_id=ctx.author.id, proposal_number=self.proposal_count ) await self.refresh_proposal(self.proposal_count)
async def transact(self, transaction, reason=''): amount = transaction['amount'] currency_name = transaction['currency_name'] user_id = transaction['user_id'] user_agent_id = transaction['user_agent_id'] timestamp = datetime.utcnow() currency = self.get_currency(currency_name) user_agent = self.guild.get_member(user_agent_id) mutget(currency, ['players', str(user_id)], 0) currency['players'][str(user_id)] += amount m = await self.transaction_channel.send(embed=make_embed( color=int(currency.get('color')[1:], 16) or colors.EMBED_INFO, description=self.format_transaction(transaction, include_total=True), footer_text=f"Authorized at {timestamp.strftime(TIME_FORMAT)} by {user_agent.name}#{user_agent.discriminator}" )) self.transaction_messages.append(m.id) self.save() nomic.logging.add_to_transaction_log(self.guild, timestamp=timestamp, currency=currency_name, agent_id=user_agent_id, recipient_id=user_id, amt=amount, reason=reason, )
async def edit(self, ctx, id: int, *, new_blurb): try: entry = self.hub_entries[str(id)] except KeyError: return await show_error(ctx, "That ID isn't a hub entry.") await self.bot.http.edit_message(channels.HUB_CHANNEL, id, embed=make_embed( title=entry["name"], url=entry["url"], description=new_blurb).to_dict()) entry["blurb"] = new_blurb await ctx.send(embed=make_embed(title="Success", description="Edited hub entry.", color=colors.EMBED_SUCCESS)) self.save()
async def quote(self, ctx, message_id: int = None): """Quote a previous message.""" if not message_id: quote_message = await ctx.send(embed=make_embed( title="No message", description=f"React to a message with {emoji.QUOTE}.", )) try: payload = await self.bot.wait_for( "raw_reaction_add", check=lambda m: m.guild_id == ctx.guild.id and m.emoji == emoji.QUOTE, timeout=60, ) except asyncio.TimeoutError: await quote_message.edit(embed=make_embed( title="No message", description=f"No reaction given.")) return channel = ctx.guild.get_channel(payload.channel_id) message = await channel.fetch_message(payload.message_id) else: message = await get_message_guild(ctx.guild, message_id, ctx.channel) if not message: await ctx.send(embed=make_embed( title="No message", description="Bad message ID given.")) quote_message = ctx.channel embed = make_embed( description=message.content, timestamp=message.edited_at or message.created_at, footer_text="#" + message.channel.name, ) embed.set_author(name=message.author.name, icon_url=message.author.avatar_url) if message.attachments: filename = message.attachments[0].filename if (filename.endswith(".png") or filename.endswith(".jpg") or filename.endswith(".jpeg")): embed.set_image(url=message.attachments[0].url) if hasattr(quote_message, "send"): await quote_message.send(embed=embed) else: await quote_message.edit(embed=embed)