async def unsubscribe(self, channel: int, world: str, subscription: str): """ Unsubscribe a channel from hunt events """ world = world.strip().lower().title() if world not in self.WORLDS: await self.bot.get_channel(channel).send( "No world by that name found on the Crystal DC - please check your spelling and try again" ) return try: sub = getattr(self, f"""SUB_{subscription.upper()}""") except AttributeError: await self.bot.get_channel(channel).send( "Invalid subscription provided, valid subscriptions are: sb_a, sb_s, hw_a, hw_s, arr_a, arr_s" ) return Subscriptions.delete().where( (Subscriptions.channel_id == channel) & (Subscriptions.world == world) & (Subscriptions.category == sub) ).execute() await self.bot.get_channel(channel).send(f"""Unsubscribed channel from {str(sub).replace('_', ' ').title()}-Rank hunts on {world}""") self._reload()
async def clear_subscriptions(self, channel: int) -> None: """ Clear all subscriptions for the specified channel """ Subscriptions.delete().where( Subscriptions.channel_id == channel).execute() self._reload()
async def subscribe(self, channel: int, world: str, subscription: str, conditions: typing.Optional[str] = 'all'): """ Subscribe a channel to hunt events """ # Validate world world = world.strip().lower().title() if world not in Worlds.get_worlds(): await self.bot.get_channel(channel).send( "No world by that name found - please check your spelling and try again" ) return # Validate subscription channel try: sub = getattr(self, f"""SUB_{subscription.upper()}""") except AttributeError: await self.bot.get_channel(channel).send( "Invalid subscription provided, valid subscriptions are: shb_a, shb_s, sb_a, sb_s, hw_a, hw_s, arr_a, arr_s" ) return # Validate conditions if conditions == 'all': conditions = list(self.CONDITIONS) else: conditions = conditions.replace(' ', '').lower().split(',') _invalid_conditions = set(conditions) - set(self.CONDITIONS) if _invalid_conditions: await self.bot.get_channel(channel).send( "Invalid conditions supplied: " + str(_invalid_conditions)) return # Already subscribed? if Subscriptions.select().where( (Subscriptions.channel_id == channel) & (Subscriptions.world == world) & (Subscriptions.category == sub)).count(): await self.bot.get_channel(channel).send( "This channel is already subscribed to this feed. If you want unsubscribe, use the unsub command" ) return for condition in conditions: Subscriptions.insert({ 'channel_id': channel, 'world': world, 'category': sub, 'event': condition }).execute() await self.bot.get_channel(channel).send( f"""Subscribed channel to {str(sub).replace('_', ' ').title()}-Rank hunts on {world}""" ) self._reload()
async def _send_sub_message(self, message, embed: discord.Embed, sub: Subscriptions) -> typing.Optional[discord.Message]: """ Attempt to send a subscription message """ try: return await self.bot.get_channel(sub.channel_id).send(message, embed=embed) except AttributeError: self._log.warning(f"Subscription channel is no longer active; removing channel {sub.channel_id}") Subscriptions.delete().where(Subscriptions.channel_id == sub.channel_id).execute() except discord.errors.Forbidden: self._log.warning(f"No permission to send to channel {sub.channel_id}")
async def subscribe_all(self, datacenter: str, channel: int, subscription: str, conditions: typing.Optional[str] = 'all'): """ Subscribe a channel to hunt events on all worlds """ # Validate subscription channel try: sub = getattr(self, f"""SUB_{subscription.upper()}""") except AttributeError: await self.bot.get_channel(channel).send( "Invalid subscription provided, valid subscriptions are: sb_a, sb_s, hw_a, hw_s, arr_a, arr_s" ) return # Validate conditions if conditions == 'all': conditions = list(self.CONDITIONS) else: conditions = conditions.replace(' ', '').lower().split(',') _invalid_conditions = set(conditions) - set(self.CONDITIONS) if _invalid_conditions: await self.bot.get_channel(channel).send( "Invalid conditions supplied: " + str(_invalid_conditions)) return # Validate datacenter datacenter = datacenter.strip().lower().title() if datacenter not in Worlds.get_datacenters(): await self.bot.get_channel(channel).send( f"Invalid datacenter provided, valid datacenters are: {', '.join(Worlds.get_datacenters())}" ) return for world in Worlds.get_datacenter_worlds(datacenter): # Already subscribed? Overwrite it Subscriptions.delete().where((Subscriptions.channel_id == channel) & (Subscriptions.world == world) & (Subscriptions.category == sub)) for condition in conditions: Subscriptions.insert({ 'channel_id': channel, 'world': world, 'category': sub, 'event': condition }).execute() await self.bot.get_channel(channel).send( f"""Subscribed channel to {str(sub).replace('_', ' ').title()}-Rank hunts on **all worlds**""" ) self._reload()
async def on_find(self, world: str, name: str, xivhunt: dict, instance=1): """ Hunt found event handler """ if world not in self._hunts: self._hunts[world] = {'horus': {}, 'xivhunt': []} _key = f"{name.strip().lower()}_{instance}" if _key in self._hunts[world]['xivhunt']: self._log.debug(f"Hunt {name} on instance {instance} already logged") return hunt = self._marks_info[name.lower()] if hunt['Rank'] not in ('A', 'S'): self._log.debug(f"""Ignoring notifications for {hunt['Rank']} rank hunts""") return print(f"A hunt has been found on world {world} (Instance {instance}) :: {name}, Rank {xivhunt['rank']}") subs = Subscriptions.select().where( (Subscriptions.world == world) & (Subscriptions.category == hunt['Channel']) ) embed = hunt_embed(name, xivhunt=xivhunt) for sub in subs: # type: Subscriptions if self.COND_FIND != sub.event: continue _meta = SubscriptionsMeta.select().where(SubscriptionsMeta.channel_id == sub.channel_id) meta = {m.name : m.value for m in _meta} role_mention = meta['notifier'] if 'notifier' in meta else None content = f"""A hunt has been found on **{world}** (**Instance {instance}**)!""" if role_mention: content = f"""{role_mention} {content}""" message = await self._send_sub_message(content, embed, sub) if not message: continue await self.log_notification(message, sub.channel_id, world, name, instance) # Relay counter _counter_key = f"""{hunt['Rank'].lower()}_count""" if _counter_key not in meta: SubscriptionsMeta.insert({ 'channel_id' : sub.channel_id, 'name' : _counter_key, 'value' : 1 }).execute() else: SubscriptionsMeta.update({ 'value' : int(meta[_counter_key]) + 1 }).where( (SubscriptionsMeta.channel_id == sub.channel_id) & (SubscriptionsMeta.name == _counter_key) ).execute() self._hunts[world]['xivhunt'].append(_key)
async def get_subscriptions(self, channel: int) -> typing.List[Subscriptions]: """ Get all subscriptions for the specified channel """ return list( Subscriptions.select().where(Subscriptions.channel_id == channel))
async def on_change(self, world: str, old: HorusHunt, new: HorusHunt): """ Hunt status change event handler """ hunt = self._marks_info[old.name.lower()] subs = Subscriptions.select().where( (Subscriptions.world == world) & (Subscriptions.category == hunt['Channel']) ) embed = hunt_embed(new.name, new) for sub in subs: # type: Subscriptions if new.status == new.STATUS_OPENED and self.COND_OPEN == sub.event: await self._send_sub_message(f"A hunt has opened on **{world}** (**Instance {new.instance}**)!", embed, sub) break if new.status == new.STATUS_MAXED and self.COND_OPEN == sub.event: await self._send_sub_message(f"A hunts maximum spawn window has been reached on **{world}** (**Instance {new.instance}**)!", embed, sub) break if new.status == new.STATUS_DIED and self.COND_DEAD == sub.event: # If we previously sent a notification that the hunt was found, edit that message instead of # sending a new one notification = await self.get_notification(sub.channel_id, world, new.name, new.instance) if notification: notification, log = notification found = int(notification.created_at.timestamp()) killed = arrow.get(int(new.last_mark / 1000)).timestamp seconds = killed - log.found kill_time = [] if seconds > 120: kill_time.append(f"""{int(seconds / 60)} minutes""") seconds -= int(seconds / 60) * 60 elif seconds > 60: kill_time.append(f"""1 minute""") seconds -= 60 kill_time.append(f"""{int(seconds)} seconds""") log.killed = killed log.kill_time = seconds log.save() try: await notification.edit(content=f"""A scouted hunt has died on **{world}** (**Instance {new.instance}**) after **{', '.join(kill_time)}**!""", embed=embed) continue except discord.NotFound: self._log.warning(f"Notification message for hunt {new.name} on world {world} has been deleted") await self._send_sub_message(f"A hunt has died on **{world}** (**Instance {new.instance}**)!", embed, sub) _key = f"{new.name.strip().lower()}_{new.instance}" if _key in self._hunts[world]['xivhunt']: self._hunts[world]['xivhunt'].remove(_key)
def __init__(self, bot: Bot): self._log = logging.getLogger(__name__) self.bot = bot self.xivhunt = XivHunt(bot) self.horus = Horus(bot) self._subscriptions = list(Subscriptions.select()) self._subscriptions_meta = list(SubscriptionsMeta.select()) self._marks_info = {} self._load_marks() self._hunts = {} self._changed = {} self._found = {} # Callbacks self._recheck_cbs = [] # Logged notifications for editing later self._notifications = {}
def _reload(self): """ Save configuration changes """ self._subscriptions = list(Subscriptions.select()) self._subscriptions_meta = list(SubscriptionsMeta.select())