11import inspect
2+ import logging
23import os
34import traceback
45from contextlib import redirect_stdout
1011
1112import discord
1213from discord import Embed , Color , Activity
13- from discord .enums import ActivityType
14+ from discord .enums import ActivityType , Status
1415from discord .ext import commands
1516
1617from aiohttp import ClientResponseError
2021from core .decorators import github_access_token_required , trigger_typing
2122from core .models import Bot , InvalidConfigError
2223from core .paginator import PaginatorSession , MessagePaginatorSession
23- from core .utils import cleanup_code
24+ from core .utils import cleanup_code , info , error
25+
26+ logger = logging .getLogger ('Modmail' )
2427
2528
2629class Utility :
@@ -244,7 +247,7 @@ async def debug(self, ctx):
244247
245248 with open (os .path .join (os .path .dirname (os .path .abspath (__file__ )),
246249 '../temp/logs.log' ), 'r+' ) as f :
247- logs = f .read ().strip (' \n \r ' )
250+ logs = f .read ().strip ()
248251
249252 if not logs :
250253 embed = Embed (
@@ -299,7 +302,7 @@ async def hastebin(self, ctx):
299302 """Upload logs to hastebin."""
300303 with open (os .path .join (os .path .dirname (os .path .abspath (__file__ )),
301304 '../temp/logs.log' ), 'r+' ) as f :
302- logs = f .read ().strip (' \n \r ' )
305+ logs = f .read ().strip ()
303306
304307 try :
305308 async with self .bot .session .post ('https://hastebin.com/documents' ,
@@ -431,10 +434,10 @@ async def activity(self, ctx, activity_type: str, *, message: str = ''):
431434 it must be followed by a "to": "listening to..."
432435 """
433436 if activity_type == 'clear' :
434- await self .bot .change_presence (activity = None )
435437 self .bot .config ['activity_type' ] = None
436438 self .bot .config ['activity_message' ] = None
437439 await self .bot .config .update ()
440+ await self .set_presence (log = False )
438441 embed = Embed (
439442 title = 'Activity Removed' ,
440443 color = self .bot .main_color
@@ -445,42 +448,167 @@ async def activity(self, ctx, activity_type: str, *, message: str = ''):
445448 raise commands .UserInputError
446449
447450 try :
448- activity_type = ActivityType [activity_type .lower ()]
449- except KeyError :
451+ activity , msg = (await self .set_presence (
452+ activity_identifier = activity_type ,
453+ activity_by_key = True ,
454+ activity_message = message ,
455+ log = False
456+ ))['activity' ]
457+ except ValueError :
450458 raise commands .UserInputError
451459
452- if activity_type == ActivityType .listening :
453- if not message .lower ().startswith ('to ' ):
454- # Must be listening to...
455- raise commands .UserInputError
456- normalized_message = message [3 :].strip ()
457- else :
458- # Discord does not allow leading/trailing spaces anyways
459- normalized_message = message .strip ()
460+ self .bot .config ['activity_type' ] = activity .type .value
461+ self .bot .config ['activity_message' ] = message
462+ await self .bot .config .update ()
460463
461- if activity_type == ActivityType .streaming :
462- url = self .bot .config .get ('twitch_url' ,
463- 'https://www.twitch.tv/discord-Modmail/' )
464- else :
465- url = None
464+ embed = Embed (
465+ title = 'Activity Changed' ,
466+ description = msg ,
467+ color = self .bot .main_color
468+ )
469+ return await ctx .send (embed = embed )
466470
467- activity = Activity (type = activity_type ,
468- name = normalized_message ,
469- url = url )
470- await self .bot .change_presence (activity = activity )
471+ @commands .command ()
472+ @checks .has_permissions (administrator = True )
473+ async def status (self , ctx , * , status_type : str ):
474+ """
475+ Set a custom status for the bot.
476+
477+ Possible status types:
478+ - `online`
479+ - `idle`
480+ - `dnd`
481+ - `do_not_disturb` or `do not disturb`
482+ - `invisible` or `offline`
483+ - `clear`
471484
472- self .bot .config ['activity_type' ] = activity_type
473- self .bot .config ['activity_message' ] = message
485+ When status type is set to `clear`, the current status is removed.
486+ """
487+ if status_type == 'clear' :
488+ self .bot .config ['status' ] = None
489+ await self .bot .config .update ()
490+ await self .set_presence (log = False )
491+ embed = Embed (
492+ title = 'Status Removed' ,
493+ color = self .bot .main_color
494+ )
495+ return await ctx .send (embed = embed )
496+ status_type = status_type .replace (' ' , '_' )
497+
498+ try :
499+ status , msg = (await self .set_presence (
500+ status_identifier = status_type ,
501+ status_by_key = True ,
502+ log = False
503+ ))['status' ]
504+ except ValueError :
505+ raise commands .UserInputError
506+
507+ self .bot .config ['status' ] = status .value
474508 await self .bot .config .update ()
475509
476- desc = f'Current activity is: { activity_type .name } { message } .'
477510 embed = Embed (
478- title = 'Activity Changed' ,
479- description = desc ,
511+ title = 'Status Changed' ,
512+ description = msg ,
480513 color = self .bot .main_color
481514 )
482515 return await ctx .send (embed = embed )
483516
517+ async def set_presence (self , * ,
518+ status_identifier = None ,
519+ status_by_key = True ,
520+ activity_identifier = None ,
521+ activity_by_key = True ,
522+ activity_message = None ,
523+ log = True ):
524+
525+ activity = status = None
526+ if status_identifier is None :
527+ status_identifier = self .bot .config .get ('status' , None )
528+ status_by_key = False
529+
530+ try :
531+ if status_by_key :
532+ status = Status [status_identifier ]
533+ else :
534+ status = Status (status_identifier )
535+ except (KeyError , ValueError ):
536+ if status_identifier is not None :
537+ msg = f'Invalid status type: { status_identifier } '
538+ if log :
539+ logger .warning (error (msg ))
540+ else :
541+ raise ValueError (msg )
542+
543+ if activity_identifier is None :
544+ if activity_message is not None :
545+ raise ValueError ('activity_message must be None '
546+ 'if activity_identifier is None.' )
547+ activity_identifier = self .bot .config .get ('activity_type' , None )
548+ activity_by_key = False
549+
550+ try :
551+ if activity_by_key :
552+ activity_type = ActivityType [activity_identifier ]
553+ else :
554+ activity_type = ActivityType (activity_identifier )
555+ except (KeyError , ValueError ):
556+ if activity_identifier is not None :
557+ msg = f'Invalid activity type: { activity_identifier } '
558+ if log :
559+ logger .warning (error (msg ))
560+ else :
561+ raise ValueError (msg )
562+ else :
563+ url = None
564+ activity_message = (
565+ activity_message or
566+ self .bot .config .get ('activity_message' , '' )
567+ ).strip ()
568+
569+ if activity_type == ActivityType .listening :
570+ if activity_message .lower ().startswith ('to ' ):
571+ # The actual message is after listening to [...]
572+ # discord automatically add the "to"
573+ activity_message = activity_message [3 :].strip ()
574+ elif activity_type == ActivityType .streaming :
575+ url = self .bot .config .get (
576+ 'twitch_url' , 'https://www.twitch.tv/discord-modmail/'
577+ )
578+
579+ if activity_message :
580+ activity = Activity (type = activity_type ,
581+ name = activity_message ,
582+ url = url )
583+ else :
584+ msg = 'You must supply an activity message to use custom activity.'
585+ if log :
586+ logger .warning (error (msg ))
587+ else :
588+ raise ValueError (msg )
589+
590+ await self .bot .change_presence (activity = activity , status = status )
591+
592+ presence = {'activity' : (None , 'No activity has been set.' ),
593+ 'status' : (None , 'No status has been set.' )}
594+ if activity is not None :
595+ # TODO: Trim message
596+ to = 'to ' if activity .type == ActivityType .listening else ''
597+ msg = f'Activity set to: { activity .type .name .capitalize ()} '
598+ msg += f'{ to } { activity .name } .'
599+ presence ['activity' ] = (activity , msg )
600+ if status is not None :
601+ msg = f'Status set to: { status .value } .'
602+ presence ['status' ] = (status , msg )
603+ return presence
604+
605+ async def on_ready (self ):
606+ # Wait until config cache is populated with stuff from db
607+ await self .bot .config .wait_until_ready ()
608+ presence = await self .set_presence ()
609+ logger .info (info (presence ['activity' ][1 ]))
610+ logger .info (info (presence ['status' ][1 ]))
611+
484612 @commands .command ()
485613 @trigger_typing
486614 @checks .has_permissions (administrator = True )
0 commit comments