Переглянути джерело

Fix status and progress for AniList feeds

Lucas Villeneuve 5 роки тому
батько
коміт
c02280268b
4 змінених файлів з 157 додано та 58 видалено
  1. 108 53
      src/anilist.py
  2. 1 0
      src/globals.py
  3. 18 5
      src/myanimebot.py
  4. 30 0
      src/utils.py

+ 108 - 53
src/anilist.py

@@ -33,6 +33,34 @@ class MediaType(Enum):
             raise NotImplementedError('Error: Cannot convert "{}" to a MediaType'.format(label))
 
 
+class MediaListStatus(Enum):
+    CURRENT=0
+    PLANNING=1
+    COMPLETED=2
+    DROPPED=3
+    PAUSED=4
+    REPEATING=5
+
+    @staticmethod
+    def from_str(label: str):
+        if label.upper().startswith('READ') or \
+            label.upper().startswith('WATCHED') :
+            return MediaListStatus.CURRENT
+        elif label.upper().startswith('PLANNING'):
+            return MediaListStatus.PLANNING
+        elif label.upper().startswith('COMPLETED'):
+            return MediaListStatus.COMPLETED
+        elif label.upper().startswith('DROPPED'):
+            return MediaListStatus.DROPPED
+        elif label.upper().startswith('PAUSED'):
+            return MediaListStatus.PAUSED
+        elif label.upper().startswith('REREAD') or \
+              label.upper().startswith('REWATCHED'):
+            return MediaListStatus.REPEATING
+        else:
+            raise NotImplementedError('Error: Cannot convert "{}" to a MediaListStatus'.format(label))
+
+
 def get_mal_id_from_anilist_id(anilist_media_id, media_type: MediaType):
     """ Converts an AniList media ID to a MyAnimeList ID and returns it """
 
@@ -80,7 +108,7 @@ def get_thumbnail_from_anilist_id(anilist_media_id, media_type: MediaType):
 
     print("Getting thumbnail from URL '{}'".format(mal_url))
     return utils.getThumbnail(mal_url)
-
+    
 
 def get_anilist_userId_from_name(user_name : str):
     """ Searches an AniList user by its name and returns its ID """
@@ -132,9 +160,15 @@ def get_latest_users_activities(users_id, page, perPage = 5):
                     media {
                         id
                         siteUrl
+                        episodes
+                        chapters
                         title {
                             romaji
                             english
+                            native
+                        }
+                        coverImage {
+                            large
                         }
                     }
                 } 
@@ -195,69 +229,90 @@ def get_latest_activity(users_id):
     return None
 
 
-async def send_embed_to_channels(activity):
+def get_media_name(activity):
+    ''' Returns the media name in english if possible '''
 
-    # Fetch user's data
-    try:
-        db_user = globals.conn.cursor(buffered=True)
-        db_user.execute("SELECT mal_user, servers FROM t_users")
-        data_user = db_user.fetchone()
-    except Exception as e:
-        # TODO Catch exception
-        globals.logger.critical("Database unavailable! (" + str(e) + ")")
-        quit()
-
-    # TODO Fetch and insert AniList thumbnail
-    # Fetch image's data
-    # cursor.execute("SELECT thumbnail FROM t_animes WHERE guid=%s LIMIT 1", [item.guid])
-    # data_img = cursor.fetchone()
-    
-    # if data_img is None:
-    try:
-        # TODO Directly send malId instead
-        image = get_thumbnail_from_anilist_id(activity["media"]["id"], MediaType.from_str(activity["type"]))
-        
-        globals.logger.info("First time seeing this " + activity["media"]["title"]["english"] + ", adding thumbnail into database: " + image)
-    except Exception as e:
-        globals.logger.warning("Error while getting the thumbnail: " + str(e))
-        image = ""
-            
-        # cursor.execute("INSERT INTO t_animes (guid, title, thumbnail, found, discoverer, media) VALUES (%s, %s, %s, NOW(), %s, %s)", [item.guid, item.title, image, user, media])
-        # globals.conn.commit()
-    # else: image = data_img[0]
+    english_name = activity["media"]["title"]["english"]
+    if english_name is not None:
+        return english_name
 
+    romaji_name = activity["media"]["title"]["romaji"]
+    if romaji_name is not None:
+        return romaji_name
 
-    for server in data_user[1].split(","):
-        db_srv = globals.conn.cursor(buffered=True)
-        db_srv.execute("SELECT channel FROM t_servers WHERE server = %s", [server])
-        data_channel = db_srv.fetchone()
+    native_name = activity["media"]["title"]["native"]
+    if native_name is not None:
+        return native_name
+
+    return ''
+
+
+def get_progress(activity):
+    progress = activity["progress"]
+    if progress is None:
+        return '?'
+    return progress
+
+
+def build_status_string(activity):
+    status_str = activity["status"].capitalize()
+    status = MediaListStatus.from_str(status_str)
+    progress = get_progress(activity)
+    episodes = ''
+    media_label = ''
+    media_type = MediaType.from_str(activity["type"])
+
+    # TODO Manage Completed/Dropped/Planned episodes/chapters count
+    if media_type.ANIME:
+        episodes = activity["media"]["episodes"]
+        if episodes is None:
+            episodes = '?'
+        media_label = 'episodes'
+    elif media_type.MANGA:
+        episodes = activity["media"]["chapters"]
+        if episodes is None:
+            episodes = '?'
+        media_label = 'chapters'
+
+    return '{} - {} of {} {}'.format(status_str, progress, episodes, media_label)
+
+
+async def send_embed_to_channels(activity):
+
+    image = activity["media"]["coverImage"]["large"]
+    user_name = activity["user"]["name"]
+    media_name = get_media_name(activity)
+    media_url = activity["media"]["siteUrl"]
+    published_date = datetime.datetime.fromtimestamp(activity["createdAt"])
+    status_str = build_status_string(activity)
+
+    user_data = utils.get_user_data()
+    servers = user_data["servers"].split(",")
+    for server in servers:
+        data_channels = utils.get_channels(server)
     
-        # FIXME 'Completed None'
-        while data_channel is not None:
-            for channel in data_channel:
+        if data_channels is not None:
+            for channel in data_channels:
                 await myanimebot.send_embed_wrapper(None,
-                                                    channel,
+                                                    channel["channel"],
                                                     globals.client,
-                                                    myanimebot.build_embed(activity["user"]["name"],
-                                                                            activity["media"]["title"]["english"],
-                                                                            activity["media"]["siteUrl"],
-                                                                            "{} {}".format(activity["status"], activity["progress"]),
-                                                                            datetime.datetime.fromtimestamp(activity["createdAt"]),
-                                                                            image))
-            
-            data_channel = db_srv.fetchone()
-
+                                                    myanimebot.build_embed(user_name, media_name, media_url, status_str, published_date, image, utils.Service.ANILIST))
 
 
 def insert_feed_db(activity):
-    cursor = globals.conn.cursor(buffered=True)
+    user_name = activity["user"]["name"]
+    media_name = get_media_name(activity)
+    media_url = activity["media"]["siteUrl"]
+    published_date = datetime.datetime.fromtimestamp(activity["createdAt"]).isoformat()
+    status = activity["status"]
 
+    cursor = globals.conn.cursor(buffered=True)
     cursor.execute("INSERT INTO t_feeds (published, title, url, user, found, type, service) VALUES (%s, %s, %s, %s, NOW(), %s, %s)",
-                    (datetime.datetime.fromtimestamp(activity["createdAt"]).isoformat(),
-                     activity["media"]["title"]["english"], # TODO When getting title if no english take romaji
-                     activity["media"]["siteUrl"], # TODO Get siteurl from MAL I guess
-                     activity["user"]["name"], # TODO Same user than mal one
-                     activity["status"], # TODO Create enum to make it generic
+                    (published_date,
+                     media_name,
+                     media_url,
+                     user_name,
+                     status, # TODO Create enum to make it generic
                      globals.SERVICE_ANILIST))
     globals.conn.commit()
 

+ 1 - 0
src/globals.py

@@ -57,6 +57,7 @@ iconBot=CONFIG.get("iconBot", "http://myanimebot.pentou.eu/rsc/bot_avatar.jpg")
 SERVICE_ANILIST="AniList"
 SERVICE_MAL="mal"
 MAL_URL="https://myanimelist.net/"
+MAL_PROFILE_URL="https://myanimelist.net/profile/"
 
 # class that send logs to DB
 class LogDBHandler(logging.Handler):

+ 18 - 5
src/myanimebot.py

@@ -40,15 +40,28 @@ if not sys.version_info[:2] >= (3, 7):
 	print("ERROR: Requires python 3.7 or newer.")
 	exit(1)
 
-# Function used to make the embed message related to the animes status
-def build_embed(user, item_title, item_link, item_description, pub_date, image):
+# TODO Create a Feed class instead of sending a lot of parameters
+def build_embed(user, item_title, item_link, item_description, pub_date, image, service: utils.Service):
+	''' Build the embed message related to the anime's status '''
+
+	# Get service
+	if service == utils.Service.MAL:
+		service_name = 'MyAnimeList'
+	elif service == utils.Service.ANILIST:
+		service_name = 'AniList'
+	else:
+		raise NotImplementedError('Unknown service {}'.format(service))
+	description = "[{}]({})\n```{}```".format(utils.filter_name(item_title), item_link, item_description)
+	profile_url_label = "{}'s {}".format(user, service_name)
+	profile_url = "{}{}".format(globals.MAL_PROFILE_URL, user)
+
 	try:	
 		embed = discord.Embed(colour=0xEED000,
 								url=item_link,
-								description="[" + utils.filter_name(item_title) + "](" + item_link + ")\n```" + item_description + "```",
+								description=description,
 								timestamp=pub_date.astimezone(pytz.timezone("utc")))
 		embed.set_thumbnail(url=image)
-		embed.set_author(name=user + "'s MyAnimeList", url="https://myanimelist.net/profile/" + user, icon_url=globals.iconMAL)
+		embed.set_author(name=profile_url_label, url=profile_url, icon_url=globals.iconMAL)
 		embed.set_footer(text="MyAnimeBot", icon_url=globals.iconBot)
 		
 		return embed
@@ -159,7 +172,7 @@ async def background_check_feed(asyncioloop):
 									data_channel = db_srv.fetchone()
 									
 									while data_channel is not None:
-										for channel in data_channel: await send_embed_wrapper(asyncioloop, channel, globals.client, build_embed(user, item.title, item.link, item.description, pubDateRaw, image))
+										for channel in data_channel: await send_embed_wrapper(asyncioloop, channel, globals.client, build_embed(user, item.title, item.link, item.description, pubDateRaw, image, utils.Service.MAL))
 										
 										data_channel = db_srv.fetchone()
 					if feed_type == 1:

+ 30 - 0
src/utils.py

@@ -1,8 +1,17 @@
 import urllib.request
 import re
+from enum import Enum
 
 from bs4 import BeautifulSoup
 
+import globals
+
+
+class Service(Enum):
+	MAL="MyAnimeList"
+	ANILIST="AniList"
+
+
 # Get thumbnail from an URL
 def getThumbnail(urlParam):
 	url = "/".join((urlParam).split("/")[:5])
@@ -56,3 +65,24 @@ def truncate_end_show(show):
 		return show[:show.rindex('-') - 1]
 	return show
 
+
+def get_user_data():
+    ''' Returns the user's data store in the database table t_users '''
+
+    try:
+        db_user = globals.conn.cursor(buffered=True, dictionary=True)
+        db_user.execute("SELECT mal_user, servers FROM t_users")
+        return db_user.fetchone()
+    except Exception as e:
+        # TODO Catch exception
+        globals.logger.critical("Database unavailable! ({})".format(e))
+        quit()
+
+
+def get_channels(server):
+	''' Returns the registered channels for a server '''
+
+	# TODO Make generic execute
+	db_srv = globals.conn.cursor(buffered=True, dictionary=True)
+	db_srv.execute("SELECT channel FROM t_servers WHERE server = %s", [server])
+	return db_srv.fetchall()