async def remove_guild_currency(self, context, currency_name: str): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return currency = GuildModelInterface.get_currency_for_guild_or_none( guild=guild, name=currency_name, ) if currency is None: await context.channel.send(f'That currency does not exist in ' f'this guild.') return await context.channel.send(f'Are you absolutely sure that you ' f'want to remove that currency from ' f'this guild? This action cannot be ' f'undone, and users of this guild ' f'will lose their holdings. Type ' f'"Yes" to confirm, type anything ' f'else to cancel.') def check(msg): return (msg.author == context.message.author and msg.channel == context.channel) message = await self.bot.wait_for('message', check=check) if message.content == 'Yes': CurrencyModelInterface.delete_instance(currency) await context.channel.send(f'The currency {currency_name} ' f'was removed from this guild.') else: await context.channel.send(f'Cancelled.')
async def list_guild_currencies(self, context): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return embed = Embed(title=f'Currencies in {guild.guild_name}', color=0x358022) for currency in GuildModelInterface.get_all_currencies_for_guild( guild): embed.add_field(name=currency.symbol, value=currency.name, inline=False) await context.channel.send(embed=embed)
async def set_cooldown_type(self, context, type: str): valid_types = ['global', 'perpasta'] if type not in valid_types: return guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return if type == 'global': guild.cooldown_type = guild.GLOBAL else: guild.cooldown_type = guild.PER_RESPONSE GuildModelInterface.save_instance(guild) await context.channel.send(f'Guild cooldown type changed to {type}') print( f'Guild {guild.guild_id} ({guild.guild_name}) triggered response ' f'cooldown type updated to {type}')
async def add_triggered_image(self, context, trigger: str, url: str): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return image_headers = [ 'image/png', 'image/jpg', 'image/jpeg', 'image/webp', ] async with aiohttp.ClientSession() as session: response, _ = \ TriggeredResponseModelInterface.get_or_create( guild=guild, trigger=trigger, defaults={ 'type': TriggeredResponseModelInterface.IMAGE, 'image': None } ) async with session.get(url) as resp: if resp.headers.get('Content-Type', '') not in \ image_headers: return await context.channel.send( f'Only PNG, JPG images are supported.') if resp.status != 200: return await context.message.channel.send( f'Could not find image.') image_bytes = await resp.read() response.image = image_bytes TriggeredResponseModelInterface.save_instance(response) data = io.BytesIO(image_bytes) await context.message.channel.send( file=discord.File(data, 'image.jpg'))
async def _set_cooldown(context, cooldown_type: int, cooldown: int): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return if cooldown_type == TriggeredResponseModelInterface.TEXT: old_cooldown = guild.triggered_text_cooldown guild.triggered_text_cooldown = cooldown else: old_cooldown = guild.triggered_image_cooldown guild.triggered_image_cooldown = cooldown GuildModelInterface.save_instance(guild) await context.channel.send(f'Guild cooldown changed from ' f'{old_cooldown} to {cooldown}') print( f'Guild {guild.guild_id} ({guild.guild_name}) triggered response ' f'cooldown (type {cooldown_type}) updated from {old_cooldown} to ' f'{cooldown}')
async def create_guild_currency(self, context, currency_name: str, symbol: str = None): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return currency = GuildModelInterface.get_currency_for_guild_or_none( guild=guild, name=currency_name, ) if currency is not None: await context.channel.send(f'That currency already exists in ' f'this guild.') return GuildModelInterface.create_currency_for_guild( guild=guild, name=currency_name, symbol=symbol, ) await context.channel.send(f'Currency "{currency_name}" created.')
async def _remove_triggered_response(context, trigger: str, type: int): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return triggered_response = TriggeredResponseModelInterface.get_or_none( type=type, guild=guild, trigger=trigger) if triggered_response is None: return TriggeredResponseModelInterface.delete_instance(triggered_response) await context.channel.send(f'Removed pasta "{trigger}" from the ' f'guild.') print(f'{context.message.author.id} removed a copy-pasta in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'Trigger: {trigger}')
async def change_guild_currency_name(self, context, currency_name: str, new_currency_name: str = None): if new_currency_name is None: await context.channel.send( f'Supply a new name for the currency if ' f'you want to change its name.') return guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return currency = GuildModelInterface.get_currency_for_guild_or_none( guild=guild, name=currency_name) if currency is None: await context.channel.send(f'There is no currency by the ' f'name "{currency_name}" in ' f'this guild.') return currency.name = new_currency_name CurrencyModelInterface.save_instance(currency) await context.channel.send(f'Currency "{currency_name}" ' f'has been updated to ' f'"{new_currency_name}".')
async def unban_word(self, context, word: str): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return banned_word = BannedWordModelInterface.get_or_none( guild=guild, word=word ) if banned_word is None: return BannedWordModelInterface.delete_instance(banned_word) await context.channel.send(f'That word is now un-banned ' f'in this guild.') print(f'{context.message.author.id} unbanned a word in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'word: {word}')
async def ban_word(self, context, word: str): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return banned_word, created = BannedWordModelInterface.get_or_create( guild=guild, word=word.lower(), ) if created: print(f'{context.message.author.id} banned a word in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'word: {word}') else: print(f'{context.message.author.id} tried to ban word in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'word: {word}, but it is already banned.') await context.channel.send(f'That word is now banned in this ' f'guild.')
async def add_triggered_text(self, context, trigger: str, response: str): guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return triggered_response, created = \ TriggeredResponseModelInterface.get_or_create( guild=guild, trigger=trigger, defaults={ 'type': TriggeredResponseModelInterface.TEXT, 'response': response } ) if created: print(f'{context.message.author.id} created a copy-pasta in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'Trigger: {trigger}, Response: {response}') else: triggered_response.response = response TriggeredResponseModelInterface.save_instance(triggered_response) print(f'{context.message.author.id} updated the copy-pasta in ' f'the guild: {guild.guild_id} ({guild.guild_name}). ' f'Trigger: {trigger}, New Response: {response}') await context.channel.send(response)
async def on_message(message): # We don't want the bot replying to itself. if message.author == bot.user: return print(f'Message received, author: {message.author}, ' f'content: {message.content}, ' f'cleaned content: {message.clean_content}') profile, profile_created = DiscordProfileModelInterface.get_or_create( id=message.author.id, defaults={'display_name': message.author.name}) guild, guild_created = GuildModelInterface.get_or_create( guild_id=message.guild.id, defaults={'guild_name': message.guild.name}) if not guild_created and guild.guild_name != message.guild.name: print(f'Guild {guild.guild_id} has had its name updated and is being ' f'updated in the database.') guild.guild_name = message.guild.name GuildModelInterface.save_instance(guild) if profile_created: print(f'Discord profile {message.author.id} has been added ' f'to the database.') elif profile.display_name != message.author.name: print(f'User {message.author.id} updated their username and is being' f'updated in the database.') profile.display_name = message.author.name DiscordProfileModelInterface.save_instance(profile) words_in_message = message.content.lower().split(' ') first_word = words_in_message[0] punctuation_removal_translation = str.maketrans('', '', string.punctuation) if not (first_word.startswith('$') and first_word[1:] in Environment.instance().BOT_COMMANDS): # Local cache of words so we don't have to hit the database for # repeated words, like if a message is "bot bot bot bot bot dead" # it won't do a query for "bot" 5 times. checked_words = [] for word in words_in_message: # Remove all punctuation and symbols. word = word.translate(punctuation_removal_translation) if word in checked_words: continue # Delete messages if they contain banned words. banned_word = BannedWordModelInterface.get_or_none(guild=guild, word=word) if banned_word is not None: await message.delete() # Send a warning DM to the sender. await message.author.send(f'Don\'t be saying that stuff.') break checked_words.append(word) response = TriggeredResponseModelInterface.get_allowed_or_none( user=profile.user, guild=guild, trigger=word, ) if response is not None: print(f'{message.author.id} triggered the "{word}" ' f'triggered response (type {response.type}).') if response.type == TriggeredResponseModelInterface.TEXT: await message.channel.send(response.response) else: data = io.BytesIO(response.image) await message.channel.send( file=discord.File(data, 'image.jpg')) # Only 1 triggered response per message. break await bot.process_commands(message)
async def give_currency(self, context, recipient: str, currency: str, amount: int): if not recipient.startswith('<@'): await context.channel.send(f'You need to mention a user in order ' f'to give to them.') return if not isinstance(amount, int): await context.channel.send(f'You can only send whole number ' f'amounts.') return guild = GuildModelInterface.get_or_none(guild_id=context.guild.id) if guild is None: return # Mentions work like: <@!150816670493966336> so ignore extra characters. recipient_id = recipient[2:-1] if recipient.startswith('<@!'): recipient_id = recipient_id[1:] sender_profile = DiscordProfileModelInterface.get_or_none( id=context.message.author.id) if sender_profile is not None: sender = sender_profile.user else: print(f'Something went wrong and the sender DiscordProfile has ' f'no user.') return receiver = DiscordProfileModelInterface.get_or_none(id=recipient_id) if receiver is not None: receiver = receiver.user else: print(f'Something went wrong and the receiver DiscordProfile has ' f'no user.') return if receiver is None: await context.channel.send(f'Looks like that user is not an ' f'active member of the guild. You ' f'can only give to active members.') return guild_currency = \ GuildModelInterface.get_currency_for_guild_or_none( guild=guild, name=currency ) if guild_currency is None: await context.channel.send(f'That is not a currency.') return sender_has_infinite_money = context.channel \ .permissions_for(context.message.author) \ .manage_messages receiver_has_infinite_money = context.channel \ .permissions_for(context.guild.get_member(int(recipient_id))) \ .manage_messages if not sender_has_infinite_money: try: UserModelInterface.pay( user=sender, currency=guild_currency, amount=amount, ) except InsufficientFundsError as exc: await context.channel.send(exc.detail) return if not receiver_has_infinite_money: UserModelInterface.receive( user=receiver, currency=guild_currency, amount=amount, ) await context.channel.send(f'Gave {amount} {currency} to ' f'{receiver.display_name}.')