myanimebot.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. #!/usr/bin/env python3
  2. # Copyright Penta (c) 2018/2020 - Under BSD License - Based on feed2discord.py by Eric Eisenhart
  3. # Compatible for Python 3.7.X
  4. #
  5. # Dependencies (for CentOS 7):
  6. # curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash
  7. # yum install gcc MariaDB-client MariaDB-common MariaDB-shared MariaDB-devel
  8. # python3.7 -m pip install --upgrade pip
  9. # pip3.7 install discord.py mariadb pytz feedparser python-dateutil asyncio html2text bs4 PyNaCL aiodns cchardet configparser
  10. # Library import
  11. import logging
  12. import os
  13. import sys
  14. import discord
  15. import feedparser
  16. import pytz
  17. import aiohttp
  18. import asyncio
  19. import urllib.request
  20. import mariadb
  21. import string
  22. import time
  23. import socket
  24. import requests
  25. # Custom libraries
  26. import globals
  27. import anilist
  28. import utils
  29. from configparser import ConfigParser
  30. from datetime import datetime
  31. from dateutil.parser import parse as parse_datetime
  32. from html2text import HTML2Text
  33. from aiohttp.web_exceptions import HTTPError, HTTPNotModified
  34. if not sys.version_info[:2] >= (3, 7):
  35. print("ERROR: Requires python 3.7 or newer.")
  36. exit(1)
  37. # TODO Create a Feed class instead of sending a lot of parameters
  38. def build_embed(user, item_title, item_link, item_description, pub_date, image, service: utils.Service):
  39. ''' Build the embed message related to the anime's status '''
  40. # Get service
  41. if service == utils.Service.MAL:
  42. service_name = 'MyAnimeList'
  43. profile_url = "{}{}".format(globals.MAL_PROFILE_URL, user)
  44. icon_url = globals.MAL_ICON_URL
  45. elif service == utils.Service.ANILIST:
  46. service_name = 'AniList'
  47. profile_url = "{}{}".format(globals.ANILIST_PROFILE_URL, user)
  48. icon_url = globals.ANILIST_ICON_URL
  49. else:
  50. raise NotImplementedError('Unknown service {}'.format(service))
  51. description = "[{}]({})\n```{}```".format(utils.filter_name(item_title), item_link, item_description)
  52. profile_url_label = "{}'s {}".format(user, service_name)
  53. try:
  54. embed = discord.Embed(colour=0xEED000, url=item_link, description=description, timestamp=pub_date.astimezone(pytz.timezone("utc")))
  55. embed.set_thumbnail(url=image)
  56. embed.set_author(name=profile_url_label, url=profile_url, icon_url=icon_url)
  57. embed.set_footer(text="MyAnimeBot", icon_url=globals.iconBot)
  58. return embed
  59. except Exception as e:
  60. globals.logger.error("Error when generating the message: " + str(e))
  61. return
  62. # Function used to send the embed
  63. async def send_embed_wrapper(asyncioloop, channelid, client, embed):
  64. channel = client.get_channel(int(channelid))
  65. try:
  66. await channel.send(embed=embed)
  67. globals.logger.info("Message sent in channel: " + channelid)
  68. except Exception as e:
  69. globals.logger.debug("Impossible to send a message on '" + channelid + "': " + str(e))
  70. return
  71. # Main function that check the RSS feeds from MyAnimeList
  72. async def background_check_feed(asyncioloop):
  73. globals.logger.info("Starting up background_check_feed")
  74. # We configure the http header
  75. http_headers = { "User-Agent": "MyAnimeBot Discord Bot v" + globals.VERSION, }
  76. await globals.client.wait_until_ready()
  77. globals.logger.debug("Discord client connected, unlocking background_check_feed...")
  78. while not globals.client.is_closed():
  79. try:
  80. db_user = globals.conn.cursor(buffered=True)
  81. db_user.execute("SELECT mal_user, servers FROM t_users")
  82. data_user = db_user.fetchone()
  83. except Exception as e:
  84. globals.logger.critical("Database unavailable! (" + str(e) + ")")
  85. quit()
  86. while data_user is not None:
  87. user=data_user[0]
  88. stop_boucle = 0
  89. feed_type = 1
  90. globals.logger.debug("checking user: " + user)
  91. try:
  92. while stop_boucle == 0 :
  93. try:
  94. async with aiohttp.ClientSession() as httpclient:
  95. if feed_type == 1 :
  96. http_response = await httpclient.request("GET", "https://myanimelist.net/rss.php?type=rm&u=" + user, headers=http_headers)
  97. media = "manga"
  98. else :
  99. http_response = await httpclient.request("GET", "https://myanimelist.net/rss.php?type=rw&u=" + user, headers=http_headers)
  100. media = "anime"
  101. except Exception as e:
  102. globals.logger.error("Error while loading RSS (" + str(feed_type) + ") of '" + user + "': " + str(e))
  103. break
  104. http_data = await http_response.read()
  105. feed_data = feedparser.parse(http_data)
  106. for item in feed_data.entries:
  107. pubDateRaw = datetime.strptime(item.published, '%a, %d %b %Y %H:%M:%S %z').astimezone(globals.timezone)
  108. DateTimezone = pubDateRaw.strftime("%z")[:3] + ':' + pubDateRaw.strftime("%z")[3:]
  109. pubDate = pubDateRaw.strftime("%Y-%m-%d %H:%M:%S")
  110. cursor = globals.conn.cursor(buffered=True)
  111. cursor.execute("SELECT published, title, url FROM t_feeds WHERE published=%s AND title=%s AND user=%s", [pubDate, item.title, user])
  112. data = cursor.fetchone()
  113. if data is None:
  114. var = datetime.now(globals.timezone) - pubDateRaw
  115. globals.logger.debug(" - " + item.title + ": " + str(var.total_seconds()))
  116. if var.total_seconds() < globals.secondMax:
  117. globals.logger.info(user + ": Item '" + item.title + "' not seen, processing...")
  118. if item.description.startswith('-') :
  119. if feed_type == 1 : item.description = "Re-Reading " + item.description
  120. else : item.description = "Re-Watching " + item.description
  121. cursor.execute("SELECT thumbnail FROM t_animes WHERE guid=%s LIMIT 1", [item.guid])
  122. data_img = cursor.fetchone()
  123. if data_img is None:
  124. try:
  125. image = utils.getThumbnail(item.link)
  126. globals.logger.info("First time seeing this " + media + ", adding thumbnail into database: " + image)
  127. except Exception as e:
  128. globals.logger.warning("Error while getting the thumbnail: " + str(e))
  129. image = ""
  130. 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])
  131. globals.conn.commit()
  132. else: image = data_img[0]
  133. type = item.description.partition(" - ")[0]
  134. cursor.execute("INSERT INTO t_feeds (published, title, url, user, found, type) VALUES (%s, %s, %s, %s, NOW(), %s)", (pubDate, item.title, item.guid, user, type))
  135. globals.conn.commit()
  136. for server in data_user[1].split(","):
  137. db_srv = globals.conn.cursor(buffered=True)
  138. db_srv.execute("SELECT channel FROM t_servers WHERE server = %s", [server])
  139. data_channel = db_srv.fetchone()
  140. while data_channel is not None:
  141. 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))
  142. data_channel = db_srv.fetchone()
  143. if feed_type == 1:
  144. feed_type = 0
  145. await asyncio.sleep(1)
  146. else:
  147. stop_boucle = 1
  148. except Exception as e:
  149. globals.logger.error("Error when parsing RSS for '" + user + "': " + str(e))
  150. await asyncio.sleep(1)
  151. data_user = db_user.fetchone()
  152. async def fetch_activities_anilist():
  153. print("Fetching activities")
  154. feed = {'__typename': 'ListActivity', 'id': 150515141, 'type': 'ANIME_LIST', 'status': 'rewatched episode', 'progress': '10 - 12', 'isLocked': False, 'createdAt': 1608738377, 'user': {'id': 102213, 'name': 'lululekiddo'}, 'media': {'id': 5081, 'siteUrl': 'https://anilist.co/anime/5081', 'title': {'romaji': 'Bakemonogatari', 'english': 'Bakemonogatari'}}}
  155. await anilist.check_new_activities()
  156. @globals.client.event
  157. async def on_ready():
  158. globals.logger.info("Logged in as " + globals.client.user.name + " (" + str(globals.client.user.id) + ")")
  159. globals.logger.info("Starting all tasks...")
  160. task_feed = globals.client.loop.create_task(background_check_feed(globals.client.loop))
  161. task_thumbnail = globals.client.loop.create_task(update_thumbnail_catalog(globals.client.loop))
  162. task_gameplayed = globals.client.loop.create_task(change_gameplayed(globals.client.loop))
  163. @globals.client.event
  164. async def on_error(event, *args, **kwargs):
  165. globals.logger.exception("Crap! An unknown Discord error occured...")
  166. def build_info_cmd_message(users, server, channels):
  167. ''' Build the corresponding message for the info command '''
  168. users_str = ''
  169. for user in users:
  170. # If user is part of the server, add it to the message
  171. if (str(server.id) in user['servers'].split(',')):
  172. if (users_str == ''): # First element
  173. users_str = '{}({})'.format(user[globals.DB_USER_NAME], user['service'])
  174. else:
  175. users_str += ', {}({})'.format(user[globals.DB_USER_NAME], user['service'])
  176. registered_channel = globals.client.get_channel(int(channels[0]["channel"]))
  177. if (users_str == ''):
  178. return "No users registered on this server. Try to add one."
  179. else:
  180. return "Registered user(s) on **{}**:\n```{}```\nAssigned channel: **{}**".format(server,
  181. users_str,
  182. registered_channel)
  183. async def info_cmd(message):
  184. ''' Processes the command "info" and sends a message '''
  185. server = message.guild
  186. if utils.is_server_in_db(server.id) == False:
  187. await message.channel.send("The server **{}** is not in our database.".format(server))
  188. else:
  189. users = utils.get_users()
  190. channels = utils.get_channels(server.id)
  191. if channels is None:
  192. await message.channel.send("No channel assigned for this bot on this server.")
  193. else:
  194. await message.channel.send(build_info_cmd_message(users, server, channels))
  195. async def delete_user_cmd(words, message):
  196. ''' Processes the command "delete" and remove a registered user '''
  197. if len(words) >= 4:
  198. if (len(words) == 4):
  199. try:
  200. service = utils.Service.from_str(words[2])
  201. except NotImplementedError:
  202. await message.channel.send('Incorrect service. Use **"{}"** or **"{}"** for example'.format(globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  203. return
  204. user = words[3]
  205. cursor = globals.conn.cursor(buffered=True, dictionary=True)
  206. cursor.execute("SELECT servers FROM t_users WHERE LOWER({})=%s AND service=%s".format(globals.DB_USER_NAME), [user.lower(), service.value])
  207. data = cursor.fetchone()
  208. print('data = {}'.format(data))
  209. print("SELECT servers FROM t_users WHERE LOWER({})={} AND service={}".format(globals.DB_USER_NAME, user.lower(), service.value))
  210. # If user is present in the database
  211. if data is not None:
  212. srv_string = ""
  213. present = False
  214. for server in data['servers'].split(','):
  215. if server != str(message.guild.id):
  216. if srv_string == "": srv_string = server
  217. else: srv_string += "," + server
  218. else: present = True
  219. if present == True:
  220. if srv_string == "": cursor.execute("DELETE FROM t_users WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME), [user.lower(), service.value])
  221. else: cursor.execute("UPDATE t_users SET servers = %s WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME), [srv_string, user.lower(), service.value])
  222. globals.conn.commit()
  223. await message.channel.send("**" + user + "** deleted from the database for this server.")
  224. else: await message.channel.send("The user **" + user + "** is not in our database for this server!")
  225. else: await message.channel.send("The user **" + user + "** is not in our database for this server!")
  226. cursor.close()
  227. else: await message.channel.send("Too many arguments! You have to specify only one username.")
  228. else: await message.channel.send("Usage: {} delete **{}**/**{}** **username**".format(globals.prefix, globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  229. @globals.client.event
  230. async def on_message(message):
  231. if message.author == globals.client.user: return
  232. words = message.content.split(" ")
  233. author = str('{0.author.mention}'.format(message))
  234. # A user is trying to get help
  235. if words[0] == globals.prefix:
  236. if len(words) > 1:
  237. if words[1] == "ping": await message.channel.send("pong")
  238. elif words[1] == "here":
  239. if message.author.guild_permissions.administrator:
  240. cursor = globals.conn.cursor(buffered=True)
  241. cursor.execute("SELECT server, channel FROM t_servers WHERE server=%s", [str(message.guild.id)])
  242. data = cursor.fetchone()
  243. if data is None:
  244. cursor.execute("INSERT INTO t_servers (server, channel) VALUES (%s,%s)", [str(message.guild.id), str(message.channel.id)])
  245. globals.conn.commit()
  246. await message.channel.send("Channel **" + str(message.channel) + "** configured for **" + str(message.guild) + "**.")
  247. else:
  248. if(data[1] == str(message.channel.id)): await message.channel.send("Channel **" + str(message.channel) + "** already in use for this server.")
  249. else:
  250. cursor.execute("UPDATE t_servers SET channel = %s WHERE server = %s", [str(message.channel.id), str(message.guild.id)])
  251. globals.conn.commit()
  252. await message.channel.send("Channel updated to: **" + str(message.channel) + "**.")
  253. cursor.close()
  254. else: await message.channel.send("Only server's admins can use this command!")
  255. elif words[1] == "add":
  256. if len(words) >= 4:
  257. if (len(words) == 4):
  258. try:
  259. service = utils.Service.from_str(words[2])
  260. except NotImplementedError:
  261. await message.channel.send('Incorrect service. Use **"{}"** or **"{}"** for example'.format(globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  262. return
  263. user = words[3]
  264. if(len(user) < 15):
  265. if service == utils.Service.MAL:
  266. try:
  267. urllib.request.urlopen('{}{}'.format(globals.MAL_PROFILE_URL, user))
  268. cursor = globals.conn.cursor(buffered=True)
  269. cursor.execute("SELECT servers FROM t_users WHERE LOWER(mal_user)=%s", [user.lower()])
  270. data = cursor.fetchone()
  271. if data is None:
  272. cursor.execute("INSERT INTO t_users ({}, service, servers) VALUES (%s, %s, %s)".format(globals.DB_USER_NAME), [user, globals.SERVICE_MAL, str(message.guild.id)])
  273. globals.conn.commit()
  274. await message.channel.send("**" + user + "** added to the database for the server **" + str(message.guild) + "**.")
  275. else:
  276. var = 0
  277. for server in data[0].split(","):
  278. if (server == str(message.guild.id)): var = 1
  279. if (var == 1):
  280. await message.channel.send("User **" + user + "** already in our database for this server!")
  281. else:
  282. cursor.execute("UPDATE t_users SET servers = %s WHERE LOWER(mal_user) = %s", [data[0] + "," + str(message.guild.id), user.lower()])
  283. globals.conn.commit()
  284. await message.channel.send("**" + user + "** added to the database for the server **" + str(message.guild) + "**.")
  285. cursor.close()
  286. except urllib.error.HTTPError as e:
  287. if (e.code == 404): await message.channel.send("User **" + user + "** doesn't exist on MyAnimeList!")
  288. else:
  289. await message.channel.send("An error occured when we checked this username on MyAnimeList, maybe the website is down?")
  290. globals.logger.warning("HTTP Code " + str(e.code) + " while checking to add for the new user '" + user + "'")
  291. except Exception as e:
  292. await message.channel.send("An unknown error occured while addind this user, the error has been logged.")
  293. globals.logger.warning("Error while adding user '{}' on server '{}': {}".format(user, message.guild, str(e)))
  294. elif service == utils.Service.ANILIST:
  295. try:
  296. is_user_valid = anilist.check_username_validity(user)
  297. if is_user_valid:
  298. cursor = globals.conn.cursor(buffered=True, dictionary=True)
  299. cursor.execute("SELECT servers FROM t_users WHERE LOWER({})=%s AND service=%s".format(globals.DB_USER_NAME), [user.lower(), globals.SERVICE_ANILIST])
  300. data = cursor.fetchone()
  301. if data is None:
  302. cursor.execute("INSERT INTO t_users ({}, service, servers) VALUES (%s, %s, %s)".format(globals.DB_USER_NAME), [user, globals.SERVICE_ANILIST, str(message.guild.id)])
  303. globals.conn.commit()
  304. await message.channel.send("**" + user + "** added to the database for the server **" + str(message.guild) + "**.")
  305. else:
  306. found_server = False
  307. servers = data["servers"].split(",")
  308. for server in servers:
  309. if (server == str(message.guild.id)):
  310. found_server = True
  311. if found_server == True:
  312. await message.channel.send("User **" + user + "** already in our database for this server!")
  313. else:
  314. cursor.execute("UPDATE t_users SET servers = %s WHERE LOWER({}}) = %s".format(globals.DB_USER_NAME), [data[0] + "," + str(message.guild.id), user.lower()])
  315. globals.conn.commit()
  316. await message.channel.send("**" + user + "** added to the database for the server **" + str(message.guild) + "**.")
  317. cursor.close()
  318. else:
  319. await message.channel.send("User **" + user + "** doesn't exist on AniList!")
  320. globals.logger.warning("No results returned while checking to add the new user '{}'".format(user))
  321. except Exception as e:
  322. await message.channel.send("An unknown error occured while addind this user, the error has been logged.")
  323. globals.logger.warning("Error while adding user '{}' on server '{}': {}".format(user, message.guild, str(e)))
  324. else: await message.channel.send("Username too long!")
  325. else: await message.channel.send("Too many arguments! You have to specify only one username.")
  326. else: await message.channel.send("Usage: {} add **{}**/**{}** **username**".format(globals.prefix, globals.SERVICE_MAL, globals.SERVICE_ANILIST))
  327. elif words[1] == "delete":
  328. await delete_user_cmd(words, message)
  329. elif words[1] == "stop":
  330. if message.author.guild_permissions.administrator:
  331. if (len(words) == 2):
  332. cursor = globals.conn.cursor(buffered=True)
  333. cursor.execute("SELECT server FROM t_servers WHERE server=%s", [str(message.guild.id)])
  334. data = cursor.fetchone()
  335. if data is None: await globals.client.send_message(message.channel, "The server **" + str(message.guild) + "** is not in our database.")
  336. else:
  337. cursor.execute("DELETE FROM t_servers WHERE server = %s", [message.guild.id])
  338. globals.conn.commit()
  339. await message.channel.send("Server **" + str(message.guild) + "** deleted from our database.")
  340. cursor.close()
  341. else: await message.channel.send("Too many arguments! Only type *stop* if you want to stop this bot on **" + message.guild + "**")
  342. else: await message.channel.send("Only server's admins can use this command!")
  343. elif words[1] == "info":
  344. await info_cmd(message)
  345. elif words[1] == "about": await message.channel.send(embed=discord.Embed(colour=0x777777, title="MyAnimeBot version " + globals.VERSION + " by Penta", description="This bot check the MyAnimeList's RSS for each user specified, and send a message if there is something new.\nMore help with the **!malbot help** command.\n\nAdd me on steam: http://steamcommunity.com/id/Penta_Pingouin").set_thumbnail(url="https://cdn.discordapp.com/avatars/415474467033317376/2d847944aab2104923c18863a41647da.jpg?size=64"))
  346. elif words[1] == "help": await message.channel.send(globals.HELP)
  347. elif words[1] == "top":
  348. if len(words) == 2:
  349. try:
  350. cursor = globals.conn.cursor(buffered=True)
  351. cursor.execute("SELECT * FROM v_Top")
  352. data = cursor.fetchone()
  353. if data is None: await message.channel.send("It seems that there is no statistics... (what happened?!)")
  354. else:
  355. topText = "**__Here is the global statistics of this bot:__**\n\n"
  356. while data is not None:
  357. topText += " - " + str(data[0]) + ": " + str(data[1]) + "\n"
  358. data = cursor.fetchone()
  359. cursor = globals.conn.cursor(buffered=True)
  360. cursor.execute("SELECT * FROM v_TotalFeeds")
  361. data = cursor.fetchone()
  362. topText += "\n***Total user entry***: " + str(data[0])
  363. cursor = globals.conn.cursor(buffered=True)
  364. cursor.execute("SELECT * FROM v_TotalAnimes")
  365. data = cursor.fetchone()
  366. topText += "\n***Total unique manga/anime***: " + str(data[0])
  367. await message.channel.send(topText)
  368. cursor.close()
  369. except Exception as e:
  370. globals.logger.warning("An error occured while displaying the global top: " + str(e))
  371. await message.channel.send("Unable to reply to your request at the moment...")
  372. elif len(words) > 2:
  373. keyword = str(' '.join(words[2:]))
  374. globals.logger.info("Displaying the global top for the keyword: " + keyword)
  375. try:
  376. cursor = globals.conn.cursor(buffered=True)
  377. cursor.callproc('sp_UsersPerKeyword', [str(keyword), '20'])
  378. for result in cursor.stored_results():
  379. data = result.fetchone()
  380. if data is None: await message.channel.send("It seems that there is no statistics for the keyword **" + keyword + "**.")
  381. else:
  382. topKeyText = "**__Here is the statistics for the keyword " + keyword + ":__**\n\n"
  383. while data is not None:
  384. topKeyText += " - " + str(data[0]) + ": " + str(data[1]) + "\n"
  385. data = result.fetchone()
  386. await message.channel.send(topKeyText)
  387. cursor.close()
  388. except Exception as e:
  389. globals.logger.warning("An error occured while displaying the global top for keyword '" + keyword + "': " + str(e))
  390. await message.channel.send("Unable to reply to your request at the moment...")
  391. elif words[1] == "group":
  392. if len(words) > 2:
  393. if message.author.guild_permissions.administrator:
  394. group = words[2]
  395. await message.channel.send("admin OK")
  396. else: await message.channel.send("Only server's admins can use this command!")
  397. else:
  398. await message.channel.send("You have to specify a group!")
  399. elif words[1] == "fetch-debug":
  400. await fetch_activities_anilist()
  401. # If mentioned
  402. elif globals.client.user in message.mentions:
  403. await message.channel.send(":heart:")
  404. # Get a random anime name and change the bot's activity
  405. async def change_gameplayed(asyncioloop):
  406. globals.logger.info("Starting up change_gameplayed")
  407. await globals.client.wait_until_ready()
  408. await asyncio.sleep(1)
  409. while not globals.client.is_closed():
  410. # Get a random anime name from the users' list
  411. cursor = globals.conn.cursor(buffered=True)
  412. cursor.execute("SELECT title FROM t_animes ORDER BY RAND() LIMIT 1")
  413. data = cursor.fetchone()
  414. anime = utils.truncate_end_show(data[0])
  415. # Try to change the bot's activity
  416. try:
  417. if data is not None: await globals.client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=anime))
  418. except Exception as e:
  419. globals.logger.warning("An error occured while changing the displayed anime title: " + str(e))
  420. cursor.close()
  421. # Do it every minute
  422. await asyncio.sleep(60)
  423. async def update_thumbnail_catalog(asyncioloop):
  424. globals.logger.info("Starting up update_thumbnail_catalog")
  425. while not globals.client.is_closed():
  426. await asyncio.sleep(43200)
  427. globals.logger.info("Automatic check of the thumbnail database on going...")
  428. reload = 0
  429. cursor = globals.conn.cursor(buffered=True)
  430. cursor.execute("SELECT guid, title, thumbnail FROM t_animes")
  431. data = cursor.fetchone()
  432. while data is not None:
  433. try:
  434. if (data[2] != "") : urllib.request.urlopen(data[2])
  435. else: reload = 1
  436. except urllib.error.HTTPError as e:
  437. globals.logger.warning("HTTP Error while getting the current thumbnail of '" + str(data[1]) + "': " + str(e))
  438. reload = 1
  439. except Exception as e:
  440. globals.logger.debug("Error while getting the current thumbnail of '" + str(data[1]) + "': " + str(e))
  441. if (reload == 1) :
  442. try:
  443. image = utils.getThumbnail(data[0])
  444. cursor.execute("UPDATE t_animes SET thumbnail = %s WHERE guid = %s", [image, data[0]])
  445. globals.conn.commit()
  446. globals.logger.info("Updated thumbnail found for \"" + str(data[1]) + "\": %s", image)
  447. except Exception as e:
  448. globals.logger.warning("Error while downloading updated thumbnail for '" + str(data[1]) + "': " + str(e))
  449. await asyncio.sleep(3)
  450. data = cursor.fetchone()
  451. cursor.close()
  452. globals.logger.info("Thumbnail database checked.")
  453. # Starting main function
  454. if __name__ == "__main__":
  455. try:
  456. globals.client.run(globals.token)
  457. except:
  458. logging.info("Closing all tasks...")
  459. globals.task_feed.cancel()
  460. globals.task_thumbnail.cancel()
  461. globals.task_gameplayed.cancel()
  462. globals.logger.critical("Script halted.")
  463. # We close all the ressources
  464. globals.conn.close()
  465. globals.log_cursor.close()
  466. globals.log_conn.close()