From 67a50cb9958cdf2a4593299d92d986c81f48e04c Mon Sep 17 00:00:00 2001 From: Taaku18 <45324516+Taaku18@users.noreply.github.com> Date: Wed, 13 Feb 2019 22:02:42 -0800 Subject: [PATCH 1/4] Status command --- CHANGELOG.md | 9 +++ bot.py | 36 ---------- cogs/utility.py | 187 ++++++++++++++++++++++++++++++++++++++++-------- core/config.py | 9 +-- 4 files changed, 170 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dccfe4306..ac73b49576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [Unreleased] + +### Added +- New `status` command, change the bot's status to `online`, `idle`, `dnd`, `invisible`, or `offline`. + - To remove the status (change it back to default), use `status clear`. + +### Changed +- The internals for `activity` has drastically changed to accommodate the new `status` command. + # 2.13.5 ### Added diff --git a/bot.py b/bot.py index 0590b7f21b..70a789dd7d 100644 --- a/bot.py +++ b/bot.py @@ -34,7 +34,6 @@ from types import SimpleNamespace import discord -from discord.enums import ActivityType from discord.ext import commands from discord.ext.commands.view import StringView @@ -401,41 +400,6 @@ async def on_ready(self): # Wait until config cache is populated with stuff from db await self.config.wait_until_ready() - # activities - activity_type = self.config.get('activity_type', -1) - message = self.config.get('activity_message', '') - - try: - activity_type = ActivityType(activity_type) - except ValueError: - activity_type = -1 - - if activity_type >= 0 and message: - normalized_message = message.strip() - if activity_type == ActivityType.listening: - if message.lower().startswith('to '): - # Must be listening to... - normalized_message = message[3:].strip() - else: - normalized_message = '' - - if normalized_message: - if activity_type == ActivityType.streaming: - url = self.config.get('twitch_url', - 'https://www.twitch.tv/discord-modmail/') - else: - url = None - - activity = discord.Activity(type=activity_type, - name=normalized_message, - url=url) - await self.change_presence(activity=activity) - # TODO: Trim message - logger.info(info('Activity set to: ' - f'{activity_type.name} {message}.')) - else: - logger.info(info(f'No activity message set.')) - # closures closures = self.config.closures.copy() logger.info(info(f'There are {len(closures)} thread(s) ' diff --git a/cogs/utility.py b/cogs/utility.py index bd972973ee..9b79364e1b 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -1,4 +1,5 @@ import inspect +import logging import os import traceback from contextlib import redirect_stdout @@ -10,7 +11,7 @@ import discord from discord import Embed, Color, Activity -from discord.enums import ActivityType +from discord.enums import ActivityType, Status from discord.ext import commands from aiohttp import ClientResponseError @@ -20,7 +21,9 @@ from core.decorators import github_access_token_required, trigger_typing from core.models import Bot, InvalidConfigError from core.paginator import PaginatorSession, MessagePaginatorSession -from core.utils import cleanup_code +from core.utils import cleanup_code, info, error + +logger = logging.getLogger('Modmail') class Utility: @@ -244,7 +247,7 @@ async def debug(self, ctx): with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../temp/logs.log'), 'r+') as f: - logs = f.read().strip(' \n\r') + logs = f.read().strip() if not logs: embed = Embed( @@ -299,7 +302,7 @@ async def hastebin(self, ctx): """Upload logs to hastebin.""" with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../temp/logs.log'), 'r+') as f: - logs = f.read().strip(' \n\r') + logs = f.read().strip() try: async with self.bot.session.post('https://hastebin.com/documents', @@ -431,10 +434,10 @@ async def activity(self, ctx, activity_type: str, *, message: str = ''): it must be followed by a "to": "listening to..." """ if activity_type == 'clear': - await self.bot.change_presence(activity=None) self.bot.config['activity_type'] = None self.bot.config['activity_message'] = None await self.bot.config.update() + await self.set_presence(log=False) embed = Embed( title='Activity Removed', color=self.bot.main_color @@ -445,42 +448,168 @@ async def activity(self, ctx, activity_type: str, *, message: str = ''): raise commands.UserInputError try: - activity_type = ActivityType[activity_type.lower()] - except KeyError: + activity, msg = (await self.set_presence( + activity_identifier=activity_type, + activity_by_key=True, + activity_message=message, + log=False + ))['activity'] + except ValueError: raise commands.UserInputError - if activity_type == ActivityType.listening: - if not message.lower().startswith('to '): - # Must be listening to... - raise commands.UserInputError - normalized_message = message[3:].strip() - else: - # Discord does not allow leading/trailing spaces anyways - normalized_message = message.strip() + self.bot.config['activity_type'] = activity.type.value + self.bot.config['activity_message'] = message + await self.bot.config.update() - if activity_type == ActivityType.streaming: - url = self.bot.config.get('twitch_url', - 'https://www.twitch.tv/discord-Modmail/') - else: - url = None + embed = Embed( + title='Activity Changed', + description=msg, + color=self.bot.main_color + ) + return await ctx.send(embed=embed) - activity = Activity(type=activity_type, - name=normalized_message, - url=url) - await self.bot.change_presence(activity=activity) + @commands.command() + @checks.has_permissions(administrator=True) + async def status(self, ctx, *, status_type: str): + """ + Set a custom status for the bot. + + Possible status types: + - `online` + - `idle` + - `dnd` + - `do_not_disturb` or `do not disturb` + - `invisible` or `offline` + - `clear` - self.bot.config['activity_type'] = activity_type - self.bot.config['activity_message'] = message + When status type is set to `clear`, the current status is removed. + """ + if status_type == 'clear': + self.bot.config['status'] = None + await self.bot.config.update() + await self.set_presence(log=False) + embed = Embed( + title='Status Removed', + color=self.bot.main_color + ) + return await ctx.send(embed=embed) + status_type = status_type.replace(' ', '_') + + try: + status, msg = (await self.set_presence( + status_identifier=status_type, + status_by_key=True, + log=False + ))['status'] + except ValueError: + raise commands.UserInputError + + self.bot.config['status'] = status.value await self.bot.config.update() - desc = f'Current activity is: {activity_type.name} {message}.' embed = Embed( - title='Activity Changed', - description=desc, + title='Status Changed', + description=msg, color=self.bot.main_color ) return await ctx.send(embed=embed) + async def set_presence(self, *, + status_identifier=None, + status_by_key=True, + activity_identifier=None, + activity_by_key=True, + activity_message=None, + log=True): + + activity = status = None + if status_identifier is None: + status_identifier = self.bot.config.get('status', None) + status_by_key = False + + try: + if status_by_key: + status = Status[status_identifier] + else: + status = Status(status_identifier) + except (KeyError, ValueError): + if status_identifier is not None: + msg = f'Invalid status type: {status_identifier}' + if log: + logger.warning(error(msg)) + else: + raise ValueError(msg) + + if activity_identifier is None: + if activity_message is not None: + raise ValueError('activity_message must be None ' + 'if activity_identifier is None.') + activity_identifier = self.bot.config.get('activity_type', None) + activity_by_key = False + + try: + if activity_by_key: + activity_type = ActivityType[activity_identifier] + else: + activity_type = ActivityType(activity_identifier) + except (KeyError, ValueError): + if activity_identifier is not None: + msg = f'Invalid activity type: {activity_identifier}' + if log: + logger.warning(error(msg)) + else: + raise ValueError(msg) + else: + url = None + activity_message = ( + activity_message or + self.bot.config.get('activity_message', '') + ).strip() + + if activity_type == ActivityType.listening: + if activity_message.lower().startswith('to '): + # The actual message is after listening to [...] + # discord automatically add the "to" + activity_message = activity_message[3:].strip() + elif activity_type == ActivityType.streaming: + url = self.bot.config.get( + 'twitch_url', 'https://www.twitch.tv/discord-modmail/' + ) + + if activity_message: + activity = Activity(type=activity_type, + name=activity_message, + url=url) + else: + msg = 'You must supply an activity message to use custom activity.' + if log: + logger.warning(error(msg)) + else: + raise ValueError(msg) + + await self.bot.change_presence(activity=activity, status=status) + + presence = {'activity': None, 'status': None} + if activity is not None: + # TODO: Trim message + to = 'to ' if activity.type == ActivityType.listening else '' + msg = f'Activity set to: {activity.type.name.capitalize()} ' + msg += f'{to}{activity.name}.' + if log: + logger.info(info(msg)) + presence['activity'] = (activity, msg) + if status is not None: + msg = f'Status set to: {status.value}.' + if log: + logger.info(info(msg)) + presence['status'] = (status, msg) + return presence + + async def on_ready(self): + # Wait until config cache is populated with stuff from db + await self.bot.config.wait_until_ready() + await self.set_presence() + @commands.command() @trigger_typing @checks.has_permissions(administrator=True) diff --git a/core/config.py b/core/config.py index 4926889574..df7ee54abd 100644 --- a/core/config.py +++ b/core/config.py @@ -25,8 +25,8 @@ class ConfigManager(ConfigManagerABC): } internal_keys = { - # activity - 'activity_message', 'activity_type', + # activity + status + 'activity_message', 'activity_type', 'status', # moderation 'blocked', # threads @@ -38,10 +38,7 @@ class ConfigManager(ConfigManagerABC): protected_keys = { # Modmail 'modmail_api_token', 'modmail_guild_id', 'guild_id', 'owners', - # logs - 'log_url', - # database - 'mongo_uri', + 'log_url', 'mongo_uri', # bot 'token', # GitHub From 0b4f400ac4ff58803bbf920dc4d9ff49e3770302 Mon Sep 17 00:00:00 2001 From: Taaku18 <45324516+Taaku18@users.noreply.github.com> Date: Wed, 13 Feb 2019 22:08:07 -0800 Subject: [PATCH 2/4] Updated CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac73b49576..0c56fb55bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - New `status` command, change the bot's status to `online`, `idle`, `dnd`, `invisible`, or `offline`. - To remove the status (change it back to default), use `status clear`. - + - This also introduces a new internal configuration variable: `status`. Possible values are `online`, `idle`, `dnd`, `invisible`, and `offline`. ### Changed - The internals for `activity` has drastically changed to accommodate the new `status` command. From 18fabdcbf3c825f343ffc07c4cb96b314d4d00be Mon Sep 17 00:00:00 2001 From: Taaku18 <45324516+Taaku18@users.noreply.github.com> Date: Wed, 13 Feb 2019 22:16:14 -0800 Subject: [PATCH 3/4] Change set_presence logging behavior --- cogs/utility.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cogs/utility.py b/cogs/utility.py index 9b79364e1b..269d3af83b 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -589,26 +589,25 @@ async def set_presence(self, *, await self.bot.change_presence(activity=activity, status=status) - presence = {'activity': None, 'status': None} + presence = {'activity': (None, 'No activity has been set.'), + 'status': (None, 'No status has been set.')} if activity is not None: # TODO: Trim message to = 'to ' if activity.type == ActivityType.listening else '' msg = f'Activity set to: {activity.type.name.capitalize()} ' msg += f'{to}{activity.name}.' - if log: - logger.info(info(msg)) presence['activity'] = (activity, msg) if status is not None: msg = f'Status set to: {status.value}.' - if log: - logger.info(info(msg)) presence['status'] = (status, msg) return presence async def on_ready(self): # Wait until config cache is populated with stuff from db await self.bot.config.wait_until_ready() - await self.set_presence() + presence = await self.set_presence() + logger.info(info(presence['activity'][1])) + logger.info(info(presence['status'][1])) @commands.command() @trigger_typing From 30df631323b5cba2a4b573195d854d5e0a7272ee Mon Sep 17 00:00:00 2001 From: Taaku18 <45324516+Taaku18@users.noreply.github.com> Date: Thu, 14 Feb 2019 22:01:49 -0800 Subject: [PATCH 4/4] Bump version to 2.13.6 --- CHANGELOG.md | 2 +- bot.py | 2 +- core/config.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c56fb55bd..1304665940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [Unreleased] +# 2.13.6 ### Added - New `status` command, change the bot's status to `online`, `idle`, `dnd`, `invisible`, or `offline`. diff --git a/bot.py b/bot.py index 70a789dd7d..f8783a0142 100644 --- a/bot.py +++ b/bot.py @@ -22,7 +22,7 @@ SOFTWARE. """ -__version__ = '2.13.5' +__version__ = '2.13.6' import asyncio import logging diff --git a/core/config.py b/core/config.py index df7ee54abd..abfd95f086 100644 --- a/core/config.py +++ b/core/config.py @@ -25,7 +25,7 @@ class ConfigManager(ConfigManagerABC): } internal_keys = { - # activity + status + # bot presence 'activity_message', 'activity_type', 'status', # moderation 'blocked',