Пример #1
0
    async def transfer(self, ctx: AceContext, tag_name: TagEditConverter(), *,
                       new_owner: disnake.Member):
        '''Transfer ownership of a tag to another member.'''

        tag_name, record = tag_name

        if new_owner.bot:
            raise commands.CommandError('Can\'t transfer tag to bot.')

        if record.get('user_id') == new_owner.id:
            raise commands.CommandError('User already owns tag.')

        prompt = ctx.prompt(
            title='Tag transfer request',
            prompt=
            f'{ctx.author.mention} wants to transfer ownership of the tag \'{tag_name}\' to you.\n\nDo you accept?',
            user_override=new_owner)

        if not await prompt:
            raise commands.CommandError('Tag transfer aborted.')

        res = await self.db.execute('UPDATE tag SET user_id=$1 WHERE id=$2',
                                    new_owner.id, record.get('id'))

        if res == 'UPDATE 1':
            await ctx.send('Tag \'{}\' transferred to \'{}\''.format(
                record.get('name'), new_owner.display_name))
        else:
            raise commands.CommandError('Unknown error occured.')
Пример #2
0
	async def get_xkcd_json(self, url):
		async with self.bot.aiohttp.get(url) as resp:
			if resp.status == 404:
				raise commands.CommandError('Comic does not exist.')
			if resp.status != 200:
				raise commands.CommandError('Request failed.')
			return await resp.json()
Пример #3
0
    async def _on_unstar(self, board, starrer, star_channel, message,
                         star_message, record):
        if record:
            result = await self.db.execute(
                'DELETE FROM starrers WHERE star_id=$1 AND user_id=$2',
                record.get('id'), starrer.id)

            # if nothing was deleted, the star message doesn't need to be updated
            if result == 'DELETE 0':
                raise commands.CommandError(
                    'You have not previously starred this, or you are the original starrer.'
                )

            # otherwise we need to update the star count
            starrer_count = await self.db.fetchval(
                'SELECT COUNT(*) FROM starrers WHERE star_id=$1',
                record.get('id'))

            if star_message is not None:
                await self.update_star(record.get('message_id'), star_message,
                                       starrer_count + 1)

        else:
            raise commands.CommandError(
                'This message has not previously been starred.')
Пример #4
0
    async def test(self, ctx):
        '''Test your welcome command.'''

        entry = await self.config.get_entry(ctx.guild.id, construct=False)

        if entry is None:
            raise WELCOME_NOT_SET_UP_ERROR

        channel = entry.channel

        if channel is None:
            if entry.channel_id is None:
                raise commands.CommandError(
                    'You haven\'t set up a welcome channel yet.\nSet up with `welcome channel [channel]`'
                )
            else:
                raise commands.CommandError(
                    'Welcome channel previously set but not found.\nPlease set again using `welcome channel [channel]`'
                )

        if entry.enabled is False:
            raise commands.CommandError(
                'Welcome messages are disabled.\nEnable with `welcome enable`')

        if entry.content is None:
            raise commands.CommandError(
                'No welcome message set.\nSet with `welcome message <message>`'
            )

        await self.on_member_join(ctx.author)
Пример #5
0
    async def convert(self, ctx, mult):
        try:
            mult = float(mult)
        except ValueError:
            raise commands.CommandError('Argument has to be float.')

        if mult < 1.0:
            raise commands.CommandError('Unit must be more than 1.')

        return mult
Пример #6
0
 async def create_tag(self, ctx, tag_name, content):
     try:
         await self.db.execute(
             'INSERT INTO tag (name, guild_id, user_id, created_at, content) VALUES ($1, $2, $3, $4, $5)',
             tag_name, ctx.guild.id, ctx.author.id, datetime.utcnow(),
             content)
     except asyncpg.UniqueViolationError:
         raise commands.CommandError('Tag already exists.')
     except Exception:
         raise commands.CommandError(
             'Failed to create tag for unknown reasons.')
Пример #7
0
    async def _on_star_event_meta(self, event, board, message, starrer):
        # get the starmessage record if it exists
        row = await self.db.fetchrow(
            'SELECT * FROM star_msg WHERE guild_id=$1 AND (message_id=$2 OR star_message_id=$2)',
            message.guild.id, message.id)

        if message.channel.id == board.channel_id:
            # if it's from the starboard itself, it *has* to be a message from ourself
            if message.author != self.bot.user:
                raise commands.CommandError(
                    'Can\'t star messages from the starboard.')

            # if the reaction event happened in the starboard channel we already have a reference
            # to both the channel and starred message
            star_channel = message.channel
            star_message = message
            message = None
        else:

            # get the star channel. this raises commanderror on failure
            star_channel = await self._get_star_channel(message.guild,
                                                        board=board)

            star_message_id = None if row is None else row.get(
                'star_message_id')

            # we should also find the starred message
            if star_message_id is None:
                # if row is none, this is a new star - and no starred message exists
                star_message = None
            else:
                # if we have the record we catch fetch the starred message
                try:
                    star_message = await star_channel.fetch_message(
                        star_message_id)
                except disnake.HTTPException:
                    raise SB_STAR_MSG_NOT_FOUND_ERROR

        # stop if attempted star is too old
        # if star_message was not found (star is new) then use the original messages timestamp
        # if a star_message *was* found, use that instead as it's the new basis of "star message age"
        if disnake.utils.utcnow() - STAR_CUTOFF > (star_message
                                                   or message).created_at:
            raise commands.CommandError(
                'Stars can\'t be added or removed from messages older than a week.'
            )

        # trigger event
        # message and star_message can be populated, or *one* of them can be None
        await event(board, starrer, star_channel, message, star_message, row)
Пример #8
0
	async def weather(self, ctx, *, location: str):
		'''Check the weather at a location.'''

		if APIXU_KEY is None:
			raise commands.CommandError('The host has not set up an API key.')

		url = 'http://api.weatherstack.com/current'

		params = {
			'access_key': APIXU_KEY,
			'query': location
		}

		async with ctx.channel.typing():
			try:
				async with ctx.http.get(url, params=params) as resp:
					if resp.status != 200:
						raise QUERY_ERROR
					data = await resp.json()
			except asyncio.TimeoutError:
				raise QUERY_ERROR

			if data.get('success', True) is False:
				raise commands.CommandError('Unable to find a location match.')

			location = data['location']
			current = data['current']

			observation_time = datetime.strptime(current['observation_time'], '%I:%M %p').time()

			e = disnake.Embed(
				title='Weather for {}, {} {}'.format(location['name'], location['region'], location['country'].upper()),
				description='*{}*'.format(' / '.join(current["weather_descriptions"])),
				timestamp=datetime.combine(date.today(), observation_time),
			)

			e.set_footer(text='Observed')

			if current['weather_icons']:
				e.set_thumbnail(url=current['weather_icons'][0])

			e.add_field(name='Temperature', value='{}°C'.format(current['temperature']))
			e.add_field(name='Feels Like', value='{}°C'.format(current['feelslike']))
			e.add_field(name='Precipitation', value='{} mm'.format(current['precip']))
			e.add_field(name='Humidity', value='{}%'.format(current['humidity']))
			e.add_field(name='Wind Speed', value='{} kph'.format(current['wind_speed']))
			e.add_field(name='Wind Direction', value=current['wind_dir'])

			await ctx.send(embed=e)
Пример #9
0
    async def version(self, ctx):
        '''Get changelog and download for the latest AutoHotkey_L version.'''

        url = 'https://api.github.com/repos/Lexikos/AutoHotkey_L/releases'

        async with ctx.http.get(url) as resp:
            if resp.status != 200:
                raise commands.CommandError('Query failed.')

            js = await resp.json()

        latest = js[0]
        asset = latest['assets'][0]

        content = self.h2m_version.convert(latest['body'])

        e = disnake.Embed(description=content, color=disnake.Color.green())

        e.set_author(name='AutoHotkey_L ' + latest['name'],
                     icon_url=latest['author']['avatar_url'])

        e.add_field(name='Release page',
                    value=f"[Click here]({latest['html_url']})")
        e.add_field(name='Installer download',
                    value=f"[Click here]({asset['browser_download_url']})")
        e.add_field(name='Downloads', value=asset['download_count'])

        await ctx.send(embed=e)
Пример #10
0
    async def cmd_docs(self, ctx: commands.Context, *, query: str = None):
        '''Search the AutoHotkey documentation. Enter multiple queries by separating with commas.'''

        if query is None:
            await ctx.send(DOCS_FORMAT.format(''))
            return

        spl = dict.fromkeys(sq.strip() for sq in query.lower().split(','))

        if len(spl) > 3:
            raise commands.CommandError('Maximum three different queries.')

        embeds = []
        for subquery in spl.keys():
            name = self.search_docs(subquery, k=1)[0]
            result = await self.get_doc(self._docs_id[name],
                                        entry=True,
                                        syntax=True)

            if not result:
                if len(spl.keys()) == 1:
                    raise DOCS_NO_MATCH
                else:
                    continue

            embeds.append(self.craft_docs_page(result, force_name=name))

        await ctx.send(embeds=embeds)
Пример #11
0
	async def ask(self, ctx):
		'''Responds with the currently open for claiming channels.'''

		controller: Controller = self.controllers.get(ctx.message.guild.id, None)
		if not controller:
			return

		channels = controller.open_channels(forecast=False)

		if not channels:
			raise commands.CommandError('No help channels are currently open for claiming. Please wait for a channel to become available.')

		mentions = [c.mention for c in channels]
		mention_count = len(mentions)

		if mention_count < 3:
			ment = ' and '.join(mentions)
		else:
			mentions[-1] = 'and ' + mentions[-1]
			ment = ', '.join(mentions)

		text = (
			'If you\'re looking for scripting help you should ask in an open help channel.\n\n'
			'The currently available help channels are {0}.'
		).format(ment)

		await ctx.send(text)
Пример #12
0
	async def choose(self, ctx, *choices: commands.clean_content):
		'''Pick a random item from a list separated by spaces.'''

		choose_prompts = (
			'I have chosen',
			'I have a great feeling about',
			'I\'ve decided on',
			'Easy choice',
			'I think',
		)

		if len(choices) < 2:
			raise commands.CommandError('At least two choices are necessary.')

		selected = choice(choices)

		e = disnake.Embed(
			description=selected
		)

		e.set_author(name=choice(choose_prompts), icon_url=self.bot.user.display_avatar.url)

		msg = await ctx.send(':thinking:')

		await asyncio.sleep(3)
		await msg.edit(content=None, embed=e)
Пример #13
0
	async def convert(self, ctx, argument):
		argument = await super().convert(ctx, argument)

		if argument in (role.emoji for role in ctx.head.selector.roles):
			raise commands.CommandError('This emoji already exists in this selector.')

		return argument
Пример #14
0
	async def bill(self, ctx, *, query: str = None):
		'''Get a random Bill Wurtz video from his website, with optional search.'''

		async with ctx.typing():
			if query is None:
				picked = choice(list(self.bill_cache.keys()))

			elif query.startswith('latest'):
				picked = self.bill_latest

			else:
				picked, score, junk = process.extractOne(query, self.bill_cache.keys())

				if score < 50:
					raise commands.CommandError(
						"Couldn't match that search with certainty.\n"
						f"Closest match: '{picked.strip()}'"
					)

		href, bill_date = self.bill_cache[picked]

		await ctx.send(
			f"{bill_date}: "
			f"*{disnake.utils.escape_markdown(picked)}* \n"
			f"{BILL_WURTZ_URL + href}"
		)
Пример #15
0
    async def nato(self, ctx, count: int = 3):
        '''Learn the NATO phonetic alphabet.'''

        if count < 1:
            raise commands.CommandError('Please pick a length larger than 0.')

        if count > 16:
            raise commands.CommandError(
                'Sorry, please pick lengths lower or equal to 16.')

        lets = sample(LETTERS, k=count)

        await ctx.send(f'**{"".join(lets).upper()}**?')

        def check(m):
            return m.channel == ctx.channel and m.author == ctx.author

        try:
            msg = await self.bot.wait_for('message', check=check, timeout=60.0)
        except asyncio.TimeoutError:
            await ctx.send(f'Sorry {ctx.author.mention}, time ran out!')
            return

        answer = msg.content.lower().split()

        async def failed():
            right = []
            for let in lets:
                asd = NATO[let]
                right.append(asd[0] if isinstance(asd, tuple) else asd)
            await ctx.send(
                f'Sorry, that was wrong! The correct answer was `{" ".join(right).upper()}`'
            )

        if len(answer) != len(lets):
            return await failed()

        for index, part in enumerate(answer):
            answer = NATO[lets[index]]
            if isinstance(answer, tuple):
                if part not in answer:
                    return await failed()
            else:
                if part != answer:
                    return await failed()

        await ctx.send('Correct! ✅')
Пример #16
0
	async def get_xkcd_comic(self, ctx, id):
		url = f'https://xkcd.com/{id}/info.0.json'
		async with ctx.http.get(url) as resp:
			if resp.status != 200:
				raise commands.CommandError('Request failed.')
			comic_json = await resp.json()
		e = self.make_xkcd_embed(comic_json)
		return e
Пример #17
0
	async def convert(self, ctx, argument):
		lowered = argument.lower()
		if lowered in ('yes', 'y', 'true', 't', '1', 'enable', 'on'):
			return True
		elif lowered in ('no', 'n', 'false', 'f', '0', 'disable', 'off'):
			return False
		else:
			raise commands.CommandError('Input could not be interpreted as boolean.')
Пример #18
0
	async def _multiprompt(self, ctx, msg, preds):
		outs = list()

		def pred(message):
			return message.author.id == ctx.author.id and ctx.channel.id == ctx.channel.id

		def new_embed(question):
			e = disnake.Embed(description=question)
			e.set_footer(text=EDIT_FOOTER)
			return e

		for question, conv in preds:
			try:
				await msg.edit(embed=new_embed(question))
			except disnake.HTTPException:
				raise commands.CommandError('Could not replace the message embed. Did the message get deleted?')

			while True:
				try:
					message = await self.bot.wait_for('message', check=pred, timeout=60.0)
					await message.delete()
				except asyncio.TimeoutError:
					return None

				if message.content.lower() == 'exit':
					return None

				try:
					value = await conv.convert(ctx, message.content)
				except commands.CommandError as exc:
					if not msg.embeds:
						try:
							await msg.delete()
						except disnake.HTTPException:
							pass
						raise commands.CommandError('Embed seems to have been removed, aborting.')

					e = msg.embeds[0]
					e.set_footer(text='NOTE: ' + str(exc) + ' ' + RETRY_MSG)
					await msg.edit(embed=e)
					continue

				outs.append(value)
				break

		return outs
Пример #19
0
    async def about(self, ctx, *, command: str = None):
        '''Show info about the bot or a command.'''

        if command is None:
            await self._about_bot(ctx)
        else:
            cmd = self.bot.get_command(command)
            if cmd is None or cmd.hidden:
                raise commands.CommandError('No command with that name found.')
            await self._about_command(ctx, cmd)
Пример #20
0
    async def clear(self,
                    ctx,
                    message_count: int,
                    user: MaybeMemberConverter = None):
        '''Simple purge command. Clear messages, either from user or indiscriminately.'''

        if message_count < 1:
            raise commands.CommandError(
                'Please choose a positive message amount to clear.')

        if message_count > 100:
            raise commands.CommandError(
                'Please choose a message count below 100.')

        def all_check(msg):
            if msg.id == RULES_MSG_ID:
                return False
            return True

        def user_check(msg):
            return msg.author.id == user.id and all_check(msg)

        try:
            await ctx.message.delete()
        except disnake.HTTPException:
            pass

        try:
            deleted = await ctx.channel.purge(
                limit=message_count,
                check=all_check if user is None else user_check)
        except disnake.HTTPException:
            raise commands.CommandError(
                'Failed deleting messages. Does the bot have the necessary permissions?'
            )

        count = len(deleted)

        log.info('%s cleared %s messages in %s', po(ctx.author), count,
                 po(ctx.guild))

        await ctx.send(f'Deleted {count} message{"s" if count > 1 else ""}.',
                       delete_after=5)
Пример #21
0
    async def disable(self, ctx):
        '''Disable welcome messages.'''

        entry = await self.config.get_entry(ctx.guild.id)

        if entry.enabled is False:
            raise commands.CommandError('Welcome messages already disabled.')

        await entry.update(enabled=False)
        await ctx.send('Welcome messages disabled.')
Пример #22
0
    async def channel(self, ctx, *, channel: disnake.TextChannel = None):
        '''Set or view welcome message channel.'''

        entry = await self.config.get_entry(ctx.guild.id)

        if channel is None:
            if entry.channel_id is None:
                raise commands.CommandError('Welcome channel not yet set.')

            channel = entry.channel
            if channel is None:
                raise commands.CommandError(
                    'Channel previously set but not found, try setting a new one.'
                )

        else:
            await entry.update(channel_id=channel.id)

        await ctx.send(f'Welcome channel set to {channel.mention}')
Пример #23
0
    async def convert(self, ctx, code):
        if code.startswith('https://p.ahkscript.org/'):
            url = code.replace('?p=', '?r=')
            async with ctx.http.get(url) as resp:
                if resp.status == 200 and str(resp.url) == url:
                    code = await resp.text()
                else:
                    raise commands.CommandError(
                        'Failed fetching code from pastebin.')

        return code
Пример #24
0
	async def convert(self, ctx, argument):
		try:
			role = await super().convert(ctx, argument)
		except commands.CommandError as exc:
			raise commands.CommandError(str(exc))

		if role == ctx.guild.default_role:
			raise commands.CommandError('The *everyone* role is not allowed.')

		if role.id in (other_role.role_id for selector in ctx.head.selectors for other_role in selector.roles):
			raise commands.CommandError('This role already exists somewhere else.')

		if ctx.author != ctx.guild.owner and role >= ctx.author.top_role:
			raise commands.CommandError('Sorry, you can\'t add roles higher than your top role.')

		config = await ctx.bot.config.get_entry(ctx.guild.id)
		if role == config.mod_role:
			raise commands.CommandError('Can\'t add moderation role to selector.')

		return role.id
Пример #25
0
    async def reminders(self, ctx):
        '''List your reminders in this guild.'''

        res = await self.db.fetch(
            'SELECT * FROM remind WHERE guild_id=$1 AND user_id=$2 ORDER BY id DESC',
            ctx.guild.id, ctx.author.id)

        if not len(res):
            raise commands.CommandError('Couldn\'t find any reminders.')

        await RemindPager(ctx, res, per_page=3).go()
Пример #26
0
    async def random(self, ctx):
        '''Show a random starred message.'''

        entry = await self.db.fetchrow(
            'SELECT * FROM star_msg WHERE star_message_id IS NOT NULL ORDER BY random() LIMIT 1'
        )

        if entry is None:
            raise commands.CommandError('No starred messages to pick from.')

        await ctx.invoke(self.show, message=entry)
Пример #27
0
    async def threshold(self, ctx, *, threshold: int = None):
        '''Starred messages with fewer than `threshold` stars will be removed from the starboard after a week has passed. To disable auto-cleaning completely, leave argument blank.'''

        board = await self.get_board(ctx.guild.id)

        if threshold is None:
            if board.threshold is None:
                raise commands.CommandError(
                    'Auto-cleaning is already disabled.')
            await board.update(threshold=None)
            await ctx.send('Starboard auto-cleaning disabled.')
        else:
            if not 0 < threshold < 32767:
                raise commands.CommandError(
                    'Auto-clean star threshold has to be between 0 and 32767.')

            await board.update(threshold=threshold)
            await ctx.send(
                'Starred messages with fewer than {} stars after a week will now be removed from the starboard.'
                .format(threshold))
Пример #28
0
    async def post_star(self, star_channel, message, starrer_count):
        try:
            star_message = await star_channel.send(
                self.get_header(message.id, starrer_count),
                embed=self.get_embed(message, starrer_count))
            await star_message.add_reaction(STAR_EMOJI)
        except disnake.HTTPException:
            raise commands.CommandError(
                'Failed posting to starboard.\nMake sure the bot has permissions to post there.'
            )

        return star_message
Пример #29
0
    async def message(self, ctx, *, message: str):
        '''Set a new welcome message.'''

        if len(message) > 1024:
            raise commands.CommandError(
                'Welcome message has to be shorter than 1024 characters.')

        # make sure an entry for this exists...
        entry = await self.config.get_entry(ctx.guild.id)
        await entry.update(content=message)

        await ctx.send('Welcome message updated. Do `welcome test` to test.')
Пример #30
0
    async def delete(self, ctx, *, tag_name: TagEditConverter(allow_mod=True)):
        '''Delete a tag.'''

        if not await ctx.prompt(
                title='Are you sure?',
                prompt='This will delete the tag permanently.'):
            raise commands.CommandError('Tag deletion aborted.')

        tag_name, record = tag_name
        await self.db.execute('DELETE FROM tag WHERE id=$1', record.get('id'))

        await ctx.send(f"Tag \'{record.get('name')}\' deleted.")