commands.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import discord
  2. import urllib
  3. import datetime
  4. from typing import List, Tuple
  5. import myanimebot.utils as utils
  6. import myanimebot.globals as globals
  7. def build_info_cmd_message(users, server, channels, filters : List[utils.Service]) -> str:
  8. ''' Build the corresponding message for the info command '''
  9. registered_channel = globals.client.get_channel(int(channels[0]["channel"]))
  10. # Store users
  11. mal_users = []
  12. anilist_users = []
  13. for user in users:
  14. # If user is part of the server, add it to the message
  15. if str(server.id) in user['servers'].split(','):
  16. try:
  17. user_service = utils.Service.from_str(user["service"])
  18. if user_service == utils.Service.MAL:
  19. mal_users.append(user[globals.DB_USER_NAME])
  20. elif user_service == utils.Service.ANILIST:
  21. anilist_users.append(user[globals.DB_USER_NAME])
  22. except NotImplementedError:
  23. pass # Nothing to do here
  24. if not mal_users and not anilist_users:
  25. return "No users registered on this server. Try to add one."
  26. else:
  27. message = 'Registered user(s) on **{}**\n\n'.format(server)
  28. if mal_users: # If not empty
  29. # Don't print if there is filters and MAL is not in them
  30. if not filters or (filters and utils.Service.MAL in filters):
  31. message += '**MyAnimeList** users:\n'
  32. message += '```{}```\n'.format(', '.join(mal_users))
  33. if anilist_users: # If not empty
  34. # Don't print if there is filters and MAL is not in them
  35. if not filters or (filters and utils.Service.ANILIST in filters):
  36. message += '**AniList** users:\n'
  37. message += '```{}```\n'.format(', '.join(anilist_users))
  38. message += 'Assigned channel : **{}**'.format(registered_channel)
  39. return message
  40. def get_service_filters_list(filters : str) -> List[utils.Service]:
  41. ''' Creates and returns a service filter list from a comma-separated string '''
  42. filters_list = []
  43. for filter in filters.split(','):
  44. try:
  45. filters_list.append(utils.Service.from_str(filter))
  46. except NotImplementedError:
  47. pass # Ignore incorrect filter
  48. return filters_list
  49. def check_user_name_validity(user_name: str, service : utils.Service) -> Tuple[bool, str]:
  50. """ Check if user_name exists on a specific service.
  51. Returns:
  52. - bool: True if user_name exists
  53. - str: Error string if the user does not exist
  54. """
  55. if service == utils.Service.MAL:
  56. try:
  57. # Ping user profile to check validity
  58. urllib.request.urlopen('{}{}'.format(globals.MAL_PROFILE_URL, user_name))
  59. except urllib.error.HTTPError as e:
  60. if (e.code == 404): # URL profile not found
  61. return False, "User **{}** doesn't exist on MyAnimeList!".format(user_name)
  62. else:
  63. globals.logger.warning("HTTP Code {} while trying to add user '{}' and checking its validity.".format(e.code, user_name))
  64. return False, "An error occured when we checked this username on MyAnimeList, maybe the website is down?"
  65. elif service == utils.Service.ANILIST:
  66. is_user_valid = anilist.check_username_validity(user_name)
  67. if is_user_valid == False:
  68. globals.logger.warning("No results returned while trying to add user '{}' and checking its validity.".format(user_name))
  69. return False, "User **{}** doesn't exist on AniList!".format(user_name)
  70. return True, None
  71. async def add_user_cmd(words, message):
  72. ''' Processes the command "add" and add a user to fetch the data for '''
  73. # Check if command is valid
  74. if len(words) != 4:
  75. if (len(words) < 4):
  76. return await message.channel.send("Usage: {} add **{}**/**{}** **username**".format(globals.prefix, globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  77. return await message.channel.send("Too many arguments! You have to specify only one username.")
  78. try:
  79. service = utils.Service.from_str(words[2])
  80. except NotImplementedError:
  81. return await message.channel.send('Incorrect service. Use **"{}"** or **"{}"** for example'.format(globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  82. user = words[3]
  83. server_id = str(message.guild.id)
  84. if(len(user) > 14):
  85. return await message.channel.send("Username too long!")
  86. try:
  87. # Check user validity
  88. is_valid, error_string = check_user_name_validity(user, service)
  89. if is_valid == False:
  90. return await message.channel.send(error_string)
  91. # Get user's servers
  92. user_servers = utils.get_user_servers(user, service)
  93. # User not present in database
  94. if user_servers is None:
  95. utils.insert_user_into_db(user, service, server_id)
  96. return await message.channel.send("**{}** added to the database for the server **{}**.".format(user, str(message.guild)))
  97. else: # User present in database
  98. is_server_present = server_id in user_servers.split(',')
  99. if is_server_present == True: # The user already has registered this server
  100. return await message.channel.send("User **{}** is already registered in our database for this server!".format(user))
  101. else:
  102. new_servers = '{},{}'.format(user_servers, server_id)
  103. utils.update_user_servers_db(user, service, new_servers)
  104. return await message.channel.send("**{}** added to the database for the server **{}**.".format(user, str(message.guild)))
  105. except Exception as e:
  106. globals.logger.warning("Error while adding user '{}' on server '{}': {}".format(user, message.guild, str(e)))
  107. return await message.channel.send("An unknown error occured while addind this user, the error has been logged.")
  108. async def delete_user_cmd(words, message):
  109. ''' Processes the command "delete" and remove a registered user '''
  110. # Check if command is valid
  111. if len(words) != 4:
  112. if (len(words) < 4):
  113. return await message.channel.send("Usage: {} delete **{}**/**{}** **username**".format(globals.prefix, globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  114. return await message.channel.send("Too many arguments! You have to specify only one username.")
  115. try:
  116. service = utils.Service.from_str(words[2])
  117. except NotImplementedError:
  118. return await message.channel.send('Incorrect service. Use **"{}"** or **"{}"** for example'.format(globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  119. user = words[3]
  120. server_id = str(message.guild.id)
  121. user_servers = utils.get_user_servers(user, service)
  122. # If user is not present in the database
  123. if user_servers is None:
  124. return await message.channel.send("The user **" + user + "** is not in our database for this server!")
  125. # Else if present, update the servers for this user
  126. srv_string = utils.remove_server_from_servers(server_id, user_servers)
  127. if srv_string is None: # Server not present in the user's servers
  128. return await message.channel.send("The user **" + user + "** is not in our database for this server!")
  129. if srv_string == "":
  130. utils.delete_user_from_db(user, service)
  131. else:
  132. utils.update_user_servers_db(user, service, srv_string)
  133. return await message.channel.send("**" + user + "** deleted from the database for this server.")
  134. async def info_cmd(message, words):
  135. ''' Processes the command "info" and sends a message '''
  136. # Get filters if available
  137. filters = []
  138. if (len(words) >= 3): # If filters are specified
  139. filters = get_service_filters_list(words[2])
  140. server = message.guild
  141. if utils.is_server_in_db(server.id) == False:
  142. await message.channel.send("The server **{}** is not in our database.".format(server))
  143. else:
  144. users = utils.get_users()
  145. channels = utils.get_channels(server.id)
  146. if channels is None:
  147. await message.channel.send("No channel assigned for this bot on this server.")
  148. else:
  149. await message.channel.send(build_info_cmd_message(users, server, channels, filters))
  150. async def ping_cmd(message, channel):
  151. ''' Responds to ping command '''
  152. messageTimestamp = message.created_at
  153. currentTimestamp = datetime.datetime.utcnow()
  154. delta = round((currentTimestamp - messageTimestamp).total_seconds() * 1000)
  155. await channel.send("pong (" + str(delta) + "ms)")
  156. async def about_cmd(channel):
  157. ''' Responds to about command with a brief description of this bot '''
  158. title = "MyAnimeBot version {} by Penta & lulu".format(globals.VERSION)
  159. description = """MyAnimeBot checks MyAnimeList and Anilist profiles for specified users, and send a message for every new activities found.
  160. More help with the **{} help** command.
  161. Check our GitHub page for more informations: https://github.com/Penta/MyAnimeBot
  162. """.format(globals.prefix)
  163. await channel.send(embed=discord.Embed(colour=0x777777, title=title, description=description).set_thumbnail(url=globals.iconBot))
  164. async def help_cmd(channel):
  165. ''' Responds to help command '''
  166. embed = discord.Embed(title="***MyAnimeBot Commands***", colour=0xEED000)
  167. embed.add_field(name="`here`", value="Register this channel. The bot will send new activities on registered channels.")
  168. embed.add_field(name="`stop`", value="Un-register this channel. The bot will now stop sending new activities for this channel.")
  169. embed.add_field(name="`info [mal|ani]`", value="Get the registered users for this server. Users can be filtered by specifying a service.")
  170. embed.add_field(name="`add {mal|ani} <user>`", value="Register a user for a specific service.\nEx: `add mal MyUser`")
  171. embed.add_field(name="`delete {mal|ani} <user>`", value="Remove a user for a specific service.\nEx: `delete ani MyUser`")
  172. embed.add_field(name="`role <@discord_role>`", value="Specify a role that is able to manage the bot.\nEx: `role @Moderator`, `role @everyone`")
  173. embed.add_field(name="`top`", value="Show statistics for this server.")
  174. embed.add_field(name="`ping`", value="Ping the bot.")
  175. embed.add_field(name="`about`", value="Get some information about this bot")
  176. await channel.send(embed=embed)