A RedBot cog for sending notifications when there are new founderless regions in NationStates.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

179 rindas
7.1 KiB

  1. from redbot.core import commands, checks, Config
  2. from redbot.core.utils.chat_formatting import pagify
  3. import discord
  4. import asyncio
  5. import requests
  6. import xml.etree.cElementTree as et
  7. import time
  8. import datetime
  9. import gzip
  10. R_HEADERS = {'User-Agent': '<Nation: Haku>'}
  11. RATE_LIMIT = 0.7
  12. def canonicalize(in_str):
  13. return in_str.lower().replace(' ', '_')
  14. def get_founderless_regions():
  15. """Return the list of founderless regions."""
  16. r = requests.get('https://www.nationstates.net/cgi-bin/api.cgi?q=regionsbytag;tags=founderless',
  17. headers=R_HEADERS)
  18. time.sleep(RATE_LIMIT)
  19. tree = et.fromstring(r.text)
  20. return tree[0].text.split(',')
  21. def download_region_dump():
  22. """Download the latest region dump from the NS API."""
  23. r = requests.get('https://www.nationstates.net/pages/regions.xml.gz',
  24. headers=R_HEADERS)
  25. time.sleep(RATE_LIMIT)
  26. with open('regions.xml.gz', 'wb') as f:
  27. f.write(r.content)
  28. with gzip.open('regions.xml.gz', 'rb') as f_in:
  29. with open('regions.xml', 'wb') as f_out:
  30. f_out.write(f_in.read())
  31. def get_region_endos(regions):
  32. """Using the region dump, return a Dict with the number of endos for each region."""
  33. tree = et.parse('regions.xml')
  34. endo_dict = dict()
  35. root = tree.getroot()
  36. for region in root:
  37. if region[0].text in regions:
  38. endo_dict[region[0].text] = int(region[5].text) - 1
  39. return endo_dict
  40. class FounderlessNotify(commands.Cog):
  41. """Notifies when a region changes in founderless status."""
  42. def __init__(self, bot, *args, **kwargs):
  43. super().__init__(*args, **kwargs)
  44. self.bot = bot
  45. self.config = Config.get_conf(self, identifier=82732344, force_registration=True)
  46. default_global_settings = {'update_time': 0, 'previous_founderless': [], 'notify_channel': 0}
  47. self.config.register_global(**default_global_settings)
  48. self.bg_loop_task = asyncio.create_task(self.bg_loop())
  49. def cog_unload(self):
  50. if self.bg_loop_task:
  51. self.bg_loop_task.cancel()
  52. @commands.command()
  53. @checks.is_owner()
  54. async def is_task_running(self, ctx):
  55. """Check if the main loop is running."""
  56. if self.bg_loop_task:
  57. await ctx.send('True')
  58. else:
  59. await ctx.send('False')
  60. @commands.command()
  61. @checks.is_owner()
  62. async def start_task(self, ctx):
  63. """Start the main loop if it is not running."""
  64. if self.bg_loop_task:
  65. await ctx.send('Task is already running')
  66. else:
  67. self.bg_loop_task = asyncio.create_task(self.bg_loop())
  68. @commands.command()
  69. @checks.is_owner()
  70. async def force_check(self, ctx, update_type):
  71. """Force perform a check in the differences of founderless regions."""
  72. if update_type.lower() == 'major' or update_type.lower() == 'minor':
  73. await self.update_founderless(update_type)
  74. else:
  75. return
  76. async def update_founderless(self, update_type):
  77. """Check for the differences in the founderless regions and then output that to a channel."""
  78. channel_id = await self.config.notify_channel()
  79. channel = self.bot.get_channel(channel_id)
  80. await channel.send(f'Beginning {update_type} check...')
  81. new_founderless_regions = get_founderless_regions()
  82. previous_founderless_regions = await self.config.previous_founderless()
  83. # A region is now foundered if it was in the previous list, but isn't in the current list
  84. # now_foundered = [region for region in previous_founderless_regions if (region not in new_founderless_regions)]
  85. # A region is now founderless if it is in the current list, but wasn't in the previous list
  86. now_founderless = [region for region in new_founderless_regions if (region not in previous_founderless_regions)]
  87. # Get the region endos for both lists
  88. # now_foundered_endos = get_region_endos(now_foundered)
  89. now_founderless_endos = get_region_endos(now_founderless)
  90. message_content = ''
  91. await channel.send('The following regions are now **Founderless**:')
  92. for region in now_founderless:
  93. message_content += f"https://www.nationstates.net/region={canonicalize(region)} " \
  94. f"({now_founderless_endos[region]})"
  95. for page in pagify(message_content):
  96. await channel.send(page)
  97. await self.config.previous_founderless.set(new_founderless_regions)
  98. async def bg_loop(self):
  99. """Main background loop."""
  100. while True:
  101. # Only check once every 10 minutes
  102. await asyncio.sleep(600)
  103. current_time = datetime.datetime.utcnow()
  104. major_time = await self.config.update_time()
  105. minor_time = major_time + 12
  106. # Major Update
  107. if current_time.hour == (major_time + 2):
  108. download_region_dump()
  109. await self.update_founderless('major')
  110. await asyncio.sleep(3600)
  111. # Minor Update
  112. elif current_time.hour == (minor_time + 1):
  113. await self.update_founderless('minor')
  114. await asyncio.sleep(3600)
  115. @commands.command()
  116. @checks.is_owner()
  117. async def update_channel(self, ctx):
  118. """Set the notification channel."""
  119. await self.config.notify_channel.set(ctx.channel.id)
  120. await ctx.send(f'Founderless notify channel set to {ctx.channel.mention}')
  121. @commands.command()
  122. @checks.is_owner()
  123. async def update_time(self, ctx, update):
  124. """Set the update time."""
  125. update = int(update)
  126. await self.config.update_time.set(update)
  127. await ctx.send(f'Major update set to: {await self.config.update_time()}:00 UTC'
  128. f'\nMinor update set to: {await self.config.update_time() + 12}:00 UTC')
  129. @commands.command()
  130. @checks.is_owner()
  131. async def get_settings(self, ctx):
  132. """Get the current settings from config."""
  133. stored_channel = await self.config.notify_channel()
  134. major_time = await self.config.update_time()
  135. minor_time = major_time + 12
  136. founderless = await self.config.previous_founderless()
  137. channel = ctx.guild.get_channel(stored_channel)
  138. await ctx.send(f'Current Channel: {channel.mention}\nCurrent Major Update Time: {major_time}:00 UTC\n'
  139. f'Current Minor Update Time: {minor_time}:00 UTC\nCurrent # Founderless: {len(founderless)}')
  140. @commands.command()
  141. @checks.is_owner()
  142. async def force_update_founderless(self, ctx):
  143. """Manually update the founderless region list."""
  144. founderless_regions = get_founderless_regions()
  145. await self.config.previous_founderless.set(founderless_regions)
  146. await ctx.send(f'Manually updated founderless regions list. There are now '
  147. f'{len(founderless_regions)} founderless regions.')
  148. @commands.command()
  149. @checks.is_owner()
  150. async def force_download_dump(self, ctx):
  151. """Manually download the region dump."""
  152. await ctx.send('Downloading region dump...')
  153. download_region_dump()
  154. await ctx.send('Region dump successfully updated.')