Loading cog/actions.py +81 −13 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ import cogdb.side import cog.inara import cog.tbl import cog.util from cogdb.schema import FortUser, UMUser from cogdb.schema import FortUser, UMUser, EUMSheet async def bot_shutdown(bot): # pragma: no cover Loading Loading @@ -1027,14 +1027,17 @@ class Hold(Action): if not self.args.system: raise cog.exc.InvalidCommandArgs("You forgot to specify a system to update.") system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system)) system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system), sheet_src=self.args.sheet_src) self.log.info('HOLD %s - Matched system name %s: \n%s.', self.duser.display_name, self.args.system, system) hold = cogdb.query.um_add_hold(self.session, system=system, user=self.undermine, held=self.args.amount) user=self.undermine, held=self.args.amount, sheet_src=self.args.sheet_src) if self.args.set: system.set_status(self.args.set) # TODO: Same payload now, but will have to switch if diverge. self.payloads += cogdb.scanners.UMScanner.update_systemum_dict( system.sheet_col, system.progress_us, system.progress_them, system.map_offset Loading @@ -1058,12 +1061,14 @@ class Hold(Action): self.duser.display_name, self.duser.id, self.undermine) if self.args.died: holds = cogdb.query.um_reset_held(self.session, self.undermine) holds = cogdb.query.um_reset_held(self.session, self.undermine, sheet_src=self.args.sheet_src) self.log.info('HOLD %s - User reset merits.', self.duser.display_name) response = 'Sorry you died :(. Held merits reset.' elif self.args.redeem: holds, redeemed = cogdb.query.um_redeem_merits(self.session, self.undermine) holds, redeemed = cogdb.query.um_redeem_merits(self.session, self.undermine, sheet_src=self.args.sheet_src) self.log.info('HOLD %s - Redeemed %d merits.', self.duser.display_name, redeemed) response = '**Redeemed Now** {}\n\n__Cycle Summary__\n'.format(redeemed) Loading @@ -1074,7 +1079,8 @@ class Hold(Action): elif self.args.redeem_systems: system_strs = " ".join(self.args.redeem_systems).split(",") holds, redeemed = cogdb.query.um_redeem_systems(self.session, self.undermine, system_strs) holds, redeemed = cogdb.query.um_redeem_systems(self.session, self.undermine, system_strs, sheet_src=self.args.sheet_src) response = '**Redeemed Now** {}\n\n__Cycle Summary__\n'.format(redeemed) lines = [['System', 'Hold', 'Redeemed']] Loading @@ -1091,7 +1097,7 @@ class Hold(Action): self.payloads += cogdb.scanners.UMScanner.update_hold_dict( hold.system.sheet_col, hold.user.row, hold.held, hold.redeemed) scanner = get_scanner("hudson_undermine") scanner = get_scanner("hudson_undermine" if self.args.sheet_src == EUMSheet.main else "hudson_snipe") await scanner.send_batch(self.payloads) await self.bot.send_message(self.msg.channel, response) Loading Loading @@ -1621,10 +1627,12 @@ class UM(Action): weekly_tick += datetime.timedelta(days=1) prefix = "**Held Merits**\n\n{}\n".format('DEADLINE **{}**'.format(weekly_tick - now)) response = cog.tbl.format_table(cogdb.query.um_all_held_merits(self.session), header=True, prefix=prefix)[0] held_merits = cogdb.query.um_all_held_merits(self.session, sheet_src=self.args.sheet_src) response = cog.tbl.format_table(held_merits, header=True, prefix=prefix)[0] elif self.args.system: system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system)) system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system), sheet_src=self.args.sheet_src) if self.args.offset: system.map_offset = self.args.offset Loading @@ -1633,11 +1641,12 @@ class UM(Action): if self.args.set or self.args.offset: self.session.commit() # TODO: Same payload now, but will have to switch if diverge. self.payloads += cogdb.scanners.UMScanner.update_systemum_dict( system.sheet_col, system.progress_us, system.progress_them, system.map_offset ) scanner = get_scanner("hudson_undermine") scanner = get_scanner("hudson_undermine" if self.args.sheet_src == EUMSheet.main else "hudson_snipe") await scanner.send_batch(self.payloads) response = system.display() Loading Loading @@ -1674,7 +1683,7 @@ class UM(Action): return else: systems = cogdb.query.um_get_systems(self.session) systems = cogdb.query.um_get_systems(self.session, sheet_src=self.args.sheet_src) response = '__Current UM Targets__\n\n' + '\n'.join( [system.display() for system in systems]) Loading Loading @@ -2008,7 +2017,7 @@ def get_scanner(name): raise cog.exc.InvalidCommandArgs("The scanners are not ready. Please try again in 15 seconds.") from exc async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, delay=60): async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, delay=60): # pragma: no cover """ Simple async task that just checks for new events every delay. Loading Loading @@ -2054,7 +2063,7 @@ async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, d ) async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): # pragma: no cover """ Simple async task that just checks for changes to the OCR sheet. This task will schedule itself infinitely on a delay. Loading Loading @@ -2094,6 +2103,64 @@ async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): ) async def monitor_snipe_merits(client, *, repeat=True): # pragma: no cover """ Schedule self to check snipe merits at the following times. This task will sleep until required. - 12 hours before tick, remind everyone with @here ping to redeem. - 30 minutes before tick, ping unredeemed users directly. Kwargs: repeat: If true, will schedule itself infinitely. """ guild = [x for x in client.guilds if x.name == FUC_GUILD] if guild: guild = guild[0] chan_id = cog.util.get_config('snipe_channel', default=None) if not guild or not chan_id: # Don't bother if not set return snipe_chan = client.get_channel(chan_id) next_cycle = cog.util.next_weekly_tick(datetime.datetime.utcnow()) # 12 hours to tick, ping here next_date = next_cycle - datetime.timedelta(hours=12) now = datetime.datetime.utcnow() if now < next_date: diff_dates = next_date - now delay_seconds = diff_dates.seconds + diff_dates.days * 24 * 60 * 60 await asyncio.sleep(delay_seconds) # Notify here with cogdb.session_scope(cogdb.Session) as session: if cogdb.query.get_all_snipe_holds(session): client.send_message( snipe_chan, "@here Snipe members it is tick day and there are 12 hours remaining." ) # 30 mins to tick ping remaining in message next_date = next_cycle - datetime.timedelta(minutes=30) now = datetime.datetime.utcnow() if now < next_date: diff_dates = next_date - now delay_seconds = diff_dates.seconds + diff_dates.days * 24 * 60 * 60 await asyncio.sleep(delay_seconds) # Notify members holding with cogdb.session_scope(cogdb.Session) as session: if cogdb.query.get_all_snipe_holds(session): to_contact = cogdb.query.get_snipe_members_holding(session, client) msg = """__Final Snipe Reminder__ There are less than **30 minutes left**. The following members are still holding. """ client.send_message(snipe_chan, msg + to_contact) if repeat: asyncio.ensure_future( monitor_snipe_merits(client, repeat=repeat) ) async def report_to_leadership(client, msgs): # pragma: no cover """ Send messages to the channel configured to receive reports. Loading Loading @@ -2194,3 +2261,4 @@ UM_NPC_TABLE = [ ['Archon Delaine', 'Kumo Crew Watch', 'Kumo Crew Transport', 'Kumo Crew Strike Ships'] ] TRACK_LIMIT = 20 FUC_GUILD = "Federal United Command" cog/bot.py +3 −0 Original line number Diff line number Diff line Loading @@ -182,6 +182,8 @@ class CogBot(discord.Client): ('Drop', 'Fort', 'User')) self.sched.register('hudson_undermine', scanners['hudson_undermine'], ('Hold', 'UM', 'User')) self.sched.register('hudson_snipe', scanners['hudson_snipe'], ('Hold', 'UM', 'User')) self.sched.register('hudson_kos', scanners['hudson_kos'], ('KOS')) self.sched.register('hudson_ocr', scanners['hudson_ocr'], ('OCR')) self.sched.schedule_all(delay=1) Loading @@ -201,6 +203,7 @@ class CogBot(discord.Client): simple_heartbeat(), cog.actions.monitor_carrier_events(self, next_summary=next_summary, delay=60), cog.actions.monitor_ocr_sheet(self), cog.actions.monitor_snipe_merits(self), cogdb.eddb.monitor_eddb_caches(), cogdb.monitor_pools(), )) Loading cog/parse.py +74 −2 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from argparse import RawDescriptionHelpFormatter as RawHelp import cog.exc import cog.util from cogdb.schema import EUMSheet PARSERS = [] Loading Loading @@ -327,7 +328,43 @@ def subs_hold(subs, prefix): and 130% opposition. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'hold', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='Hold') sub.set_defaults(cmd='Hold', sheet_src=EUMSheet.main) sub.add_argument('amount', nargs='?', type=int, help='The amount of merits held.') sub.add_argument('system', nargs='*', help='The system merits are held in.') sub.add_argument('-r', '--redeem', action='store_true', help='Redeem all held merits.') sub.add_argument('--redeem-systems', nargs='+', help='Redeem all held merits.') sub.add_argument('-d', '--died', action='store_true', help='Zero out held merits.') sub.add_argument('-s', '--set', help='Update the galmap progress us:them. Example: --set 3500:200') @register_parser def subs_shold(subs, prefix): """ Subcommand parsing for shold, snipe variant of hold """ desc = """Update a user's held or redeemed merits. IMPORTANT: This is the __SNIPE__ sheet. Examples: **{prefix}shold 1200 burr** Set your held merits at Burr to 1200. **{prefix}shold 900 af leopris @Memau** Set held merits at System AF Leopris to 900 held for Memau. **{prefix}shold --died** **{prefix}shold -d** Reset your held merits to 0 due to dying. **{prefix}shold --redeem** **{prefix}shold -r** Move all held merits to redeemed column. **{prefix}shold --redeem-systems Burr, Rana** Redeem only merits for systems listed. **{prefix}shold 720 burr --set 60000:130** **{prefix}shold 720 burr -s 60000:130** Update held merits to 720 at Burr expansion and set progress to 60000 merits for us and 130% opposition. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'shold', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='Hold', sheet_src=EUMSheet.snipe) sub.add_argument('amount', nargs='?', type=int, help='The amount of merits held.') sub.add_argument('system', nargs='*', help='The system merits are held in.') sub.add_argument('-r', '--redeem', action='store_true', help='Redeem all held merits.') Loading Loading @@ -608,7 +645,42 @@ def subs_um(subs, prefix): List powerplay NPC ships by alligence. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'um', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='UM') sub.set_defaults(cmd='UM', sheet_src=EUMSheet.main) sub.add_argument('system', nargs='*', help='The system to update or show.') sub.add_argument('-s', '--set', help='Set the status of the system, us:them. Example-> --set 3500:200') sub.add_argument('-o', '--offset', type=int, help='Set the system galmap offset.') sub.add_argument('-l', '--list', action='store_true', help='Show all outstanding merits on sheet.') sub.add_argument('--npcs', action='store_true', help='List powerplay NPC ships by alligence.') @register_parser def subs_snipe(subs, prefix): """ Subcommand parsing for snipe, the snipe variant of um """ desc = """Get undermining targets and update their galmap status. IMPORTANT: This is the __SNIPE__ sheet. Examples: **{prefix}snipe** Show current active undermining targets. **{prefix}snipe burr** Show the current status and information on Burr. **{prefix}snipe afl** Show the current status and information on AF Leopris, matched search. **{prefix}snipe burr --set 60000:130** **{prefix}snipe burr -s 60000:130** Set the galmap status of Burr to 60000 and opposition to 130%. **{prefix}snipe burr --offset 4000** **{prefix}snipe burr -o 4000** Set the offset difference of cmdr merits and galmap. **{prefix}snipe --list** Show all outstanding merits by users and system. **{prefix}snipe --npcs** List powerplay NPC ships by alligence. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'snipe', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='UM', sheet_src=EUMSheet.snipe) sub.add_argument('system', nargs='*', help='The system to update or show.') sub.add_argument('-s', '--set', help='Set the status of the system, us:them. Example-> --set 3500:200') Loading cogdb/query.py +110 −31 Original line number Diff line number Diff line Loading @@ -15,7 +15,8 @@ import cog.sheets import cogdb.eddb from cog.util import substr_match, get_config from cogdb.schema import (DiscordUser, FortSystem, FortPrep, FortDrop, FortUser, FortOrder, UMSystem, UMUser, UMHold, KOS, AdminPerm, ChannelPerm, RolePerm, UMSystem, UMUser, UMHold, EUMSheet, KOS, AdminPerm, ChannelPerm, RolePerm, TrackSystem, TrackSystemCached, TrackByID, OCRTracker, OCRTrigger, OCRPrep, Global, Vote) from cogdb.scanners import FortScanner Loading Loading @@ -70,7 +71,7 @@ def get_duser(session, discord_id): NoMatch - No possible match found. """ try: return session.query(DiscordUser).filter_by(id=discord_id).one() return session.query(DiscordUser).filter(DiscordUser.id == discord_id).one() except sqla_oexc.NoResultFound as exc: raise cog.exc.NoMatch(discord_id, 'DiscordUser') from exc Loading Loading @@ -147,6 +148,7 @@ def users_with_um_merits(session): """ return session.query(DiscordUser, UMUser.combo).\ join(UMUser, UMUser.name == DiscordUser.pref_name).\ filter(UMUser.sheet_src == EUMSheet.main).\ order_by(UMUser.combo.desc()).\ all() Loading Loading @@ -263,7 +265,7 @@ def fort_find_system(session, system_name, search_all=True): MoreThanOneMatch - Too many matches possible, ask user to resubmit. """ try: return session.query(FortSystem).filter_by(name=system_name).one() return session.query(FortSystem).filter(FortSystem.name == system_name).one() except (sqla_oexc.NoResultFound, sqla_oexc.MultipleResultsFound): index = 0 if search_all else fort_find_current_index(session) systems = fort_get_systems(session)[index:] + fort_get_preps(session) Loading Loading @@ -379,7 +381,10 @@ def fort_add_drop(session, *, user, system, amount): raise cog.exc.InvalidCommandArgs('Drop amount must be in range [-{num}, {num}]'.format(num=MAX_DROP)) try: drop = session.query(FortDrop).filter_by(user_id=user.id, system_id=system.id).one() drop = session.query(FortDrop).\ filter(FortDrop.user_id == user.id, FortDrop.system_id == system.id).\ one() except sqla_oexc.NoResultFound: drop = FortDrop(user_id=user.id, system_id=system.id, amount=0) session.add(drop) Loading Loading @@ -446,15 +451,19 @@ def fort_order_drop(session): session.commit() def um_find_system(session, system_name): def um_find_system(session, system_name, *, sheet_src=EUMSheet.main): """ Find the UMSystem with system_name """ try: return session.query(UMSystem).filter_by(name=system_name).one() return session.query(UMSystem).\ filter(UMSystem.name == system_name, UMSystem.sheet_src == sheet_src).\ one() except (sqla_oexc.NoResultFound, sqla_oexc.MultipleResultsFound) as exc: systems = session.query(UMSystem).\ filter(UMSystem.name.ilike('%{}%'.format(system_name))).\ filter(UMSystem.name.ilike('%{}%'.format(system_name)), UMSystem.sheet_src == sheet_src).\ all() if len(systems) > 1: Loading @@ -466,25 +475,29 @@ def um_find_system(session, system_name): return systems[0] def um_get_systems(session, exclude_finished=True): def um_get_systems(session, exclude_finished=True, *, sheet_src=EUMSheet.main): """ Return a list of all current undermining targets. kwargs: finished: Return just the finished targets. """ systems = session.query(UMSystem).all() systems = session.query(UMSystem).\ filter(UMSystem.sheet_src == sheet_src).\ all() if exclude_finished: systems = [system for system in systems if not system.is_undermined] return systems def um_reset_held(session, user): def um_reset_held(session, user, *, sheet_src=EUMSheet.main): """ Reset all held merits to 0. """ holds = session.query(UMHold).filter_by(user_id=user.id).all() holds = session.query(UMHold).\ filter(UMHold.user_id == user.id, UMHold.sheet_src == sheet_src).all() for hold in holds: hold.held = 0 Loading @@ -492,12 +505,14 @@ def um_reset_held(session, user): return holds def um_redeem_merits(session, user): def um_redeem_merits(session, user, *, sheet_src=EUMSheet.main): """ Redeem all held merits for user. """ total = 0 holds = session.query(UMHold).filter_by(user_id=user.id).all() holds = session.query(UMHold).\ filter(UMHold.user_id == user.id).\ all() for hold in holds: total += hold.held hold.redeemed = hold.redeemed + hold.held Loading @@ -507,13 +522,14 @@ def um_redeem_merits(session, user): return (holds, total) def um_redeem_systems(session, user, systems): def um_redeem_systems(session, user, systems, *, sheet_src=EUMSheet.main): """ Redeem merits only for the specified user and the systems that matched exactly. """ total = 0 subq = session.query(UMSystem.id).\ filter(UMSystem.name.in_(systems)).\ filter(UMSystem.name.in_(systems), UMSystem.sheet_src == sheet_src).\ scalar_subquery() holds = session.query(UMHold).filter(UMHold.user_id == user.id, UMHold.system_id.in_(subq)).all() Loading @@ -526,7 +542,7 @@ def um_redeem_systems(session, user, systems): return (holds, total) def um_add_hold(session, **kwargs): def um_add_hold(session, *, sheet_src=EUMSheet.main, **kwargs): """ Add or update the user's Hold, that is their UM merits held or redeemed. System.name and SUser.name Loading @@ -545,10 +561,13 @@ def um_add_hold(session, **kwargs): raise cog.exc.InvalidCommandArgs('Hold amount must be in range [0, \u221E]') try: hold = session.query(UMHold).filter_by(user_id=user.id, system_id=system.id).one() hold = session.query(UMHold).\ filter(UMHold.user_id == user.id, UMHold.system_id == system.id, UMHold.sheet_src == sheet_src).\ one() except sqla_oexc.NoResultFound: hold = UMHold(user_id=user.id, system_id=system.id, held=0, redeemed=0) hold = UMHold(user_id=user.id, system_id=system.id, held=0, redeemed=0, sheet_src=sheet_src) session.add(hold) hold.held = held Loading @@ -557,7 +576,7 @@ def um_add_hold(session, **kwargs): return hold def um_all_held_merits(session): def um_all_held_merits(session, *, sheet_src=EUMSheet.main): """ Return a list of lists that show all users with merits still held. Loading @@ -569,13 +588,21 @@ def um_all_held_merits(session): ] """ c_dict = {} for merit in session.query(UMHold).filter(UMHold.held > 0).order_by(UMHold.system_id).all(): held_merits = session.query(UMHold).\ filter(UMHold.held > 0, UMHold.sheet_src == sheet_src).\ order_by(UMHold.system_id).\ all() for merit in held_merits: try: c_dict[merit.user.name][merit.system.name] = merit except KeyError: c_dict[merit.user.name] = {merit.system.name: merit} systems = session.query(UMSystem).order_by(UMSystem.id).all() systems = session.query(UMSystem).\ filter(UMSystem.sheet_src == EUMSheet.main).\ order_by(UMSystem.id).\ all() system_names = [sys.name for sys in systems] rows = [] for cmdr in c_dict: Loading @@ -597,7 +624,7 @@ def get_admin(session, member): Otherwise, raise NoMatch. """ try: return session.query(AdminPerm).filter_by(id=member.id).one() return session.query(AdminPerm).filter(AdminPerm.id == member.id).one() except sqla_oexc.NoResultFound as exc: raise cog.exc.NoMatch(member.display_name, 'Admin') from exc Loading Loading @@ -631,8 +658,12 @@ def add_role_perm(session, cmd, server, role): def remove_channel_perm(session, cmd, server, channel): try: session.delete(session.query(ChannelPerm). filter_by(cmd=cmd, server_id=server.id, channel_id=channel.id).one()) perm = session.query(ChannelPerm).\ filter(ChannelPerm.cmd == cmd, ChannelPerm.server_id == server.id, ChannelPerm.channel_id == channel.id).\ one() session.delete(perm) session.commit() except sqla_oexc.NoResultFound as exc: raise cog.exc.InvalidCommandArgs("Channel permission does not exist.") from exc Loading @@ -640,8 +671,12 @@ def remove_channel_perm(session, cmd, server, channel): def remove_role_perm(session, cmd, server, role): try: session.delete(session.query(RolePerm). filter_by(cmd=cmd, server_id=server.id, role_id=role.id).one()) perm = session.query(RolePerm).\ filter(RolePerm.cmd == cmd, RolePerm.server_id == server.id, RolePerm.role_id == role.id).\ one() session.delete(perm) session.commit() except sqla_oexc.NoResultFound as exc: raise cog.exc.InvalidCommandArgs("Role permission does not exist.") from exc Loading @@ -666,8 +701,11 @@ def check_channel_perms(session, cmd, server, channel): Raises InvalidPerms if fails permission check. """ channels = [perm.channel_id for perm in session.query(ChannelPerm). filter_by(cmd=cmd, server_id=server.id)] perms = session.query(ChannelPerm).\ filter(ChannelPerm.cmd == cmd, ChannelPerm.server_id == server.id).\ all() channels = [perm.channel_id for perm in perms] if channels and channel.id not in channels: raise cog.exc.InvalidPerms("The '{}' command is not permitted on this channel.".format( cmd.lower())) Loading @@ -681,8 +719,11 @@ def check_role_perms(session, cmd, server, member_roles): Raises InvalidPerms if fails permission check. """ perm_roles = {perm.role_id for perm in session.query(RolePerm). filter_by(cmd=cmd, server_id=server.id)} perms = session.query(RolePerm).\ filter(RolePerm.cmd == cmd, RolePerm.server_id == server.id).\ all() perm_roles = {perm.role_id for perm in perms} member_roles = {role.id for role in member_roles} if perm_roles and len(member_roles - perm_roles) == len(member_roles): raise cog.exc.InvalidPerms("You do not have the roles for the command.") Loading Loading @@ -1259,3 +1300,41 @@ def get_vote(session, discord_id, vote_type): session.add(the_vote) return the_vote def get_all_snipe_holds(session): """ Args: session: A session onto the db. Returns: A list of UMHolds for the snipe sheet. Empty by default. """ return session.query(UMHold).\ filter(UMHold.sheet_src == EUMSheet.snipe, UMHold.held > 0).\ all() def get_snipe_members_holding(session, guild): """ Find the members who are holding merits on the snipe sheet. For each found member, attempt to resolve them with the guild to mention them. If we cannot resolve their name with guild, just write the name. Format one large message reminding the users and return it. Args: session: A session onto the db. guild: The guild of the server in question. Returns: A string formatted mentioning or naming all users with snipe merits. """ template_msg = "{} is holding {} merits in {}\n" reply = "" for hold in get_all_snipe_holds(session): duser = hold.user.discord_user found = guild.get_member_named(duser.pref_name) mention = found.mention if found else duser.pref_name reply += template_msg.format(mention, hold.held, hold.system.name) return reply cogdb/scanners.py +114 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
cog/actions.py +81 −13 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ import cogdb.side import cog.inara import cog.tbl import cog.util from cogdb.schema import FortUser, UMUser from cogdb.schema import FortUser, UMUser, EUMSheet async def bot_shutdown(bot): # pragma: no cover Loading Loading @@ -1027,14 +1027,17 @@ class Hold(Action): if not self.args.system: raise cog.exc.InvalidCommandArgs("You forgot to specify a system to update.") system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system)) system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system), sheet_src=self.args.sheet_src) self.log.info('HOLD %s - Matched system name %s: \n%s.', self.duser.display_name, self.args.system, system) hold = cogdb.query.um_add_hold(self.session, system=system, user=self.undermine, held=self.args.amount) user=self.undermine, held=self.args.amount, sheet_src=self.args.sheet_src) if self.args.set: system.set_status(self.args.set) # TODO: Same payload now, but will have to switch if diverge. self.payloads += cogdb.scanners.UMScanner.update_systemum_dict( system.sheet_col, system.progress_us, system.progress_them, system.map_offset Loading @@ -1058,12 +1061,14 @@ class Hold(Action): self.duser.display_name, self.duser.id, self.undermine) if self.args.died: holds = cogdb.query.um_reset_held(self.session, self.undermine) holds = cogdb.query.um_reset_held(self.session, self.undermine, sheet_src=self.args.sheet_src) self.log.info('HOLD %s - User reset merits.', self.duser.display_name) response = 'Sorry you died :(. Held merits reset.' elif self.args.redeem: holds, redeemed = cogdb.query.um_redeem_merits(self.session, self.undermine) holds, redeemed = cogdb.query.um_redeem_merits(self.session, self.undermine, sheet_src=self.args.sheet_src) self.log.info('HOLD %s - Redeemed %d merits.', self.duser.display_name, redeemed) response = '**Redeemed Now** {}\n\n__Cycle Summary__\n'.format(redeemed) Loading @@ -1074,7 +1079,8 @@ class Hold(Action): elif self.args.redeem_systems: system_strs = " ".join(self.args.redeem_systems).split(",") holds, redeemed = cogdb.query.um_redeem_systems(self.session, self.undermine, system_strs) holds, redeemed = cogdb.query.um_redeem_systems(self.session, self.undermine, system_strs, sheet_src=self.args.sheet_src) response = '**Redeemed Now** {}\n\n__Cycle Summary__\n'.format(redeemed) lines = [['System', 'Hold', 'Redeemed']] Loading @@ -1091,7 +1097,7 @@ class Hold(Action): self.payloads += cogdb.scanners.UMScanner.update_hold_dict( hold.system.sheet_col, hold.user.row, hold.held, hold.redeemed) scanner = get_scanner("hudson_undermine") scanner = get_scanner("hudson_undermine" if self.args.sheet_src == EUMSheet.main else "hudson_snipe") await scanner.send_batch(self.payloads) await self.bot.send_message(self.msg.channel, response) Loading Loading @@ -1621,10 +1627,12 @@ class UM(Action): weekly_tick += datetime.timedelta(days=1) prefix = "**Held Merits**\n\n{}\n".format('DEADLINE **{}**'.format(weekly_tick - now)) response = cog.tbl.format_table(cogdb.query.um_all_held_merits(self.session), header=True, prefix=prefix)[0] held_merits = cogdb.query.um_all_held_merits(self.session, sheet_src=self.args.sheet_src) response = cog.tbl.format_table(held_merits, header=True, prefix=prefix)[0] elif self.args.system: system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system)) system = cogdb.query.um_find_system(self.session, ' '.join(self.args.system), sheet_src=self.args.sheet_src) if self.args.offset: system.map_offset = self.args.offset Loading @@ -1633,11 +1641,12 @@ class UM(Action): if self.args.set or self.args.offset: self.session.commit() # TODO: Same payload now, but will have to switch if diverge. self.payloads += cogdb.scanners.UMScanner.update_systemum_dict( system.sheet_col, system.progress_us, system.progress_them, system.map_offset ) scanner = get_scanner("hudson_undermine") scanner = get_scanner("hudson_undermine" if self.args.sheet_src == EUMSheet.main else "hudson_snipe") await scanner.send_batch(self.payloads) response = system.display() Loading Loading @@ -1674,7 +1683,7 @@ class UM(Action): return else: systems = cogdb.query.um_get_systems(self.session) systems = cogdb.query.um_get_systems(self.session, sheet_src=self.args.sheet_src) response = '__Current UM Targets__\n\n' + '\n'.join( [system.display() for system in systems]) Loading Loading @@ -2008,7 +2017,7 @@ def get_scanner(name): raise cog.exc.InvalidCommandArgs("The scanners are not ready. Please try again in 15 seconds.") from exc async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, delay=60): async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, delay=60): # pragma: no cover """ Simple async task that just checks for new events every delay. Loading Loading @@ -2054,7 +2063,7 @@ async def monitor_carrier_events(client, *, next_summary, last_timestamp=None, d ) async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): # pragma: no cover """ Simple async task that just checks for changes to the OCR sheet. This task will schedule itself infinitely on a delay. Loading Loading @@ -2094,6 +2103,64 @@ async def monitor_ocr_sheet(client, *, delay=1800, repeat=True): ) async def monitor_snipe_merits(client, *, repeat=True): # pragma: no cover """ Schedule self to check snipe merits at the following times. This task will sleep until required. - 12 hours before tick, remind everyone with @here ping to redeem. - 30 minutes before tick, ping unredeemed users directly. Kwargs: repeat: If true, will schedule itself infinitely. """ guild = [x for x in client.guilds if x.name == FUC_GUILD] if guild: guild = guild[0] chan_id = cog.util.get_config('snipe_channel', default=None) if not guild or not chan_id: # Don't bother if not set return snipe_chan = client.get_channel(chan_id) next_cycle = cog.util.next_weekly_tick(datetime.datetime.utcnow()) # 12 hours to tick, ping here next_date = next_cycle - datetime.timedelta(hours=12) now = datetime.datetime.utcnow() if now < next_date: diff_dates = next_date - now delay_seconds = diff_dates.seconds + diff_dates.days * 24 * 60 * 60 await asyncio.sleep(delay_seconds) # Notify here with cogdb.session_scope(cogdb.Session) as session: if cogdb.query.get_all_snipe_holds(session): client.send_message( snipe_chan, "@here Snipe members it is tick day and there are 12 hours remaining." ) # 30 mins to tick ping remaining in message next_date = next_cycle - datetime.timedelta(minutes=30) now = datetime.datetime.utcnow() if now < next_date: diff_dates = next_date - now delay_seconds = diff_dates.seconds + diff_dates.days * 24 * 60 * 60 await asyncio.sleep(delay_seconds) # Notify members holding with cogdb.session_scope(cogdb.Session) as session: if cogdb.query.get_all_snipe_holds(session): to_contact = cogdb.query.get_snipe_members_holding(session, client) msg = """__Final Snipe Reminder__ There are less than **30 minutes left**. The following members are still holding. """ client.send_message(snipe_chan, msg + to_contact) if repeat: asyncio.ensure_future( monitor_snipe_merits(client, repeat=repeat) ) async def report_to_leadership(client, msgs): # pragma: no cover """ Send messages to the channel configured to receive reports. Loading Loading @@ -2194,3 +2261,4 @@ UM_NPC_TABLE = [ ['Archon Delaine', 'Kumo Crew Watch', 'Kumo Crew Transport', 'Kumo Crew Strike Ships'] ] TRACK_LIMIT = 20 FUC_GUILD = "Federal United Command"
cog/bot.py +3 −0 Original line number Diff line number Diff line Loading @@ -182,6 +182,8 @@ class CogBot(discord.Client): ('Drop', 'Fort', 'User')) self.sched.register('hudson_undermine', scanners['hudson_undermine'], ('Hold', 'UM', 'User')) self.sched.register('hudson_snipe', scanners['hudson_snipe'], ('Hold', 'UM', 'User')) self.sched.register('hudson_kos', scanners['hudson_kos'], ('KOS')) self.sched.register('hudson_ocr', scanners['hudson_ocr'], ('OCR')) self.sched.schedule_all(delay=1) Loading @@ -201,6 +203,7 @@ class CogBot(discord.Client): simple_heartbeat(), cog.actions.monitor_carrier_events(self, next_summary=next_summary, delay=60), cog.actions.monitor_ocr_sheet(self), cog.actions.monitor_snipe_merits(self), cogdb.eddb.monitor_eddb_caches(), cogdb.monitor_pools(), )) Loading
cog/parse.py +74 −2 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from argparse import RawDescriptionHelpFormatter as RawHelp import cog.exc import cog.util from cogdb.schema import EUMSheet PARSERS = [] Loading Loading @@ -327,7 +328,43 @@ def subs_hold(subs, prefix): and 130% opposition. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'hold', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='Hold') sub.set_defaults(cmd='Hold', sheet_src=EUMSheet.main) sub.add_argument('amount', nargs='?', type=int, help='The amount of merits held.') sub.add_argument('system', nargs='*', help='The system merits are held in.') sub.add_argument('-r', '--redeem', action='store_true', help='Redeem all held merits.') sub.add_argument('--redeem-systems', nargs='+', help='Redeem all held merits.') sub.add_argument('-d', '--died', action='store_true', help='Zero out held merits.') sub.add_argument('-s', '--set', help='Update the galmap progress us:them. Example: --set 3500:200') @register_parser def subs_shold(subs, prefix): """ Subcommand parsing for shold, snipe variant of hold """ desc = """Update a user's held or redeemed merits. IMPORTANT: This is the __SNIPE__ sheet. Examples: **{prefix}shold 1200 burr** Set your held merits at Burr to 1200. **{prefix}shold 900 af leopris @Memau** Set held merits at System AF Leopris to 900 held for Memau. **{prefix}shold --died** **{prefix}shold -d** Reset your held merits to 0 due to dying. **{prefix}shold --redeem** **{prefix}shold -r** Move all held merits to redeemed column. **{prefix}shold --redeem-systems Burr, Rana** Redeem only merits for systems listed. **{prefix}shold 720 burr --set 60000:130** **{prefix}shold 720 burr -s 60000:130** Update held merits to 720 at Burr expansion and set progress to 60000 merits for us and 130% opposition. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'shold', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='Hold', sheet_src=EUMSheet.snipe) sub.add_argument('amount', nargs='?', type=int, help='The amount of merits held.') sub.add_argument('system', nargs='*', help='The system merits are held in.') sub.add_argument('-r', '--redeem', action='store_true', help='Redeem all held merits.') Loading Loading @@ -608,7 +645,42 @@ def subs_um(subs, prefix): List powerplay NPC ships by alligence. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'um', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='UM') sub.set_defaults(cmd='UM', sheet_src=EUMSheet.main) sub.add_argument('system', nargs='*', help='The system to update or show.') sub.add_argument('-s', '--set', help='Set the status of the system, us:them. Example-> --set 3500:200') sub.add_argument('-o', '--offset', type=int, help='Set the system galmap offset.') sub.add_argument('-l', '--list', action='store_true', help='Show all outstanding merits on sheet.') sub.add_argument('--npcs', action='store_true', help='List powerplay NPC ships by alligence.') @register_parser def subs_snipe(subs, prefix): """ Subcommand parsing for snipe, the snipe variant of um """ desc = """Get undermining targets and update their galmap status. IMPORTANT: This is the __SNIPE__ sheet. Examples: **{prefix}snipe** Show current active undermining targets. **{prefix}snipe burr** Show the current status and information on Burr. **{prefix}snipe afl** Show the current status and information on AF Leopris, matched search. **{prefix}snipe burr --set 60000:130** **{prefix}snipe burr -s 60000:130** Set the galmap status of Burr to 60000 and opposition to 130%. **{prefix}snipe burr --offset 4000** **{prefix}snipe burr -o 4000** Set the offset difference of cmdr merits and galmap. **{prefix}snipe --list** Show all outstanding merits by users and system. **{prefix}snipe --npcs** List powerplay NPC ships by alligence. """.format(prefix=prefix) sub = subs.add_parser(prefix + 'snipe', description=desc, formatter_class=RawHelp) sub.set_defaults(cmd='UM', sheet_src=EUMSheet.snipe) sub.add_argument('system', nargs='*', help='The system to update or show.') sub.add_argument('-s', '--set', help='Set the status of the system, us:them. Example-> --set 3500:200') Loading
cogdb/query.py +110 −31 Original line number Diff line number Diff line Loading @@ -15,7 +15,8 @@ import cog.sheets import cogdb.eddb from cog.util import substr_match, get_config from cogdb.schema import (DiscordUser, FortSystem, FortPrep, FortDrop, FortUser, FortOrder, UMSystem, UMUser, UMHold, KOS, AdminPerm, ChannelPerm, RolePerm, UMSystem, UMUser, UMHold, EUMSheet, KOS, AdminPerm, ChannelPerm, RolePerm, TrackSystem, TrackSystemCached, TrackByID, OCRTracker, OCRTrigger, OCRPrep, Global, Vote) from cogdb.scanners import FortScanner Loading Loading @@ -70,7 +71,7 @@ def get_duser(session, discord_id): NoMatch - No possible match found. """ try: return session.query(DiscordUser).filter_by(id=discord_id).one() return session.query(DiscordUser).filter(DiscordUser.id == discord_id).one() except sqla_oexc.NoResultFound as exc: raise cog.exc.NoMatch(discord_id, 'DiscordUser') from exc Loading Loading @@ -147,6 +148,7 @@ def users_with_um_merits(session): """ return session.query(DiscordUser, UMUser.combo).\ join(UMUser, UMUser.name == DiscordUser.pref_name).\ filter(UMUser.sheet_src == EUMSheet.main).\ order_by(UMUser.combo.desc()).\ all() Loading Loading @@ -263,7 +265,7 @@ def fort_find_system(session, system_name, search_all=True): MoreThanOneMatch - Too many matches possible, ask user to resubmit. """ try: return session.query(FortSystem).filter_by(name=system_name).one() return session.query(FortSystem).filter(FortSystem.name == system_name).one() except (sqla_oexc.NoResultFound, sqla_oexc.MultipleResultsFound): index = 0 if search_all else fort_find_current_index(session) systems = fort_get_systems(session)[index:] + fort_get_preps(session) Loading Loading @@ -379,7 +381,10 @@ def fort_add_drop(session, *, user, system, amount): raise cog.exc.InvalidCommandArgs('Drop amount must be in range [-{num}, {num}]'.format(num=MAX_DROP)) try: drop = session.query(FortDrop).filter_by(user_id=user.id, system_id=system.id).one() drop = session.query(FortDrop).\ filter(FortDrop.user_id == user.id, FortDrop.system_id == system.id).\ one() except sqla_oexc.NoResultFound: drop = FortDrop(user_id=user.id, system_id=system.id, amount=0) session.add(drop) Loading Loading @@ -446,15 +451,19 @@ def fort_order_drop(session): session.commit() def um_find_system(session, system_name): def um_find_system(session, system_name, *, sheet_src=EUMSheet.main): """ Find the UMSystem with system_name """ try: return session.query(UMSystem).filter_by(name=system_name).one() return session.query(UMSystem).\ filter(UMSystem.name == system_name, UMSystem.sheet_src == sheet_src).\ one() except (sqla_oexc.NoResultFound, sqla_oexc.MultipleResultsFound) as exc: systems = session.query(UMSystem).\ filter(UMSystem.name.ilike('%{}%'.format(system_name))).\ filter(UMSystem.name.ilike('%{}%'.format(system_name)), UMSystem.sheet_src == sheet_src).\ all() if len(systems) > 1: Loading @@ -466,25 +475,29 @@ def um_find_system(session, system_name): return systems[0] def um_get_systems(session, exclude_finished=True): def um_get_systems(session, exclude_finished=True, *, sheet_src=EUMSheet.main): """ Return a list of all current undermining targets. kwargs: finished: Return just the finished targets. """ systems = session.query(UMSystem).all() systems = session.query(UMSystem).\ filter(UMSystem.sheet_src == sheet_src).\ all() if exclude_finished: systems = [system for system in systems if not system.is_undermined] return systems def um_reset_held(session, user): def um_reset_held(session, user, *, sheet_src=EUMSheet.main): """ Reset all held merits to 0. """ holds = session.query(UMHold).filter_by(user_id=user.id).all() holds = session.query(UMHold).\ filter(UMHold.user_id == user.id, UMHold.sheet_src == sheet_src).all() for hold in holds: hold.held = 0 Loading @@ -492,12 +505,14 @@ def um_reset_held(session, user): return holds def um_redeem_merits(session, user): def um_redeem_merits(session, user, *, sheet_src=EUMSheet.main): """ Redeem all held merits for user. """ total = 0 holds = session.query(UMHold).filter_by(user_id=user.id).all() holds = session.query(UMHold).\ filter(UMHold.user_id == user.id).\ all() for hold in holds: total += hold.held hold.redeemed = hold.redeemed + hold.held Loading @@ -507,13 +522,14 @@ def um_redeem_merits(session, user): return (holds, total) def um_redeem_systems(session, user, systems): def um_redeem_systems(session, user, systems, *, sheet_src=EUMSheet.main): """ Redeem merits only for the specified user and the systems that matched exactly. """ total = 0 subq = session.query(UMSystem.id).\ filter(UMSystem.name.in_(systems)).\ filter(UMSystem.name.in_(systems), UMSystem.sheet_src == sheet_src).\ scalar_subquery() holds = session.query(UMHold).filter(UMHold.user_id == user.id, UMHold.system_id.in_(subq)).all() Loading @@ -526,7 +542,7 @@ def um_redeem_systems(session, user, systems): return (holds, total) def um_add_hold(session, **kwargs): def um_add_hold(session, *, sheet_src=EUMSheet.main, **kwargs): """ Add or update the user's Hold, that is their UM merits held or redeemed. System.name and SUser.name Loading @@ -545,10 +561,13 @@ def um_add_hold(session, **kwargs): raise cog.exc.InvalidCommandArgs('Hold amount must be in range [0, \u221E]') try: hold = session.query(UMHold).filter_by(user_id=user.id, system_id=system.id).one() hold = session.query(UMHold).\ filter(UMHold.user_id == user.id, UMHold.system_id == system.id, UMHold.sheet_src == sheet_src).\ one() except sqla_oexc.NoResultFound: hold = UMHold(user_id=user.id, system_id=system.id, held=0, redeemed=0) hold = UMHold(user_id=user.id, system_id=system.id, held=0, redeemed=0, sheet_src=sheet_src) session.add(hold) hold.held = held Loading @@ -557,7 +576,7 @@ def um_add_hold(session, **kwargs): return hold def um_all_held_merits(session): def um_all_held_merits(session, *, sheet_src=EUMSheet.main): """ Return a list of lists that show all users with merits still held. Loading @@ -569,13 +588,21 @@ def um_all_held_merits(session): ] """ c_dict = {} for merit in session.query(UMHold).filter(UMHold.held > 0).order_by(UMHold.system_id).all(): held_merits = session.query(UMHold).\ filter(UMHold.held > 0, UMHold.sheet_src == sheet_src).\ order_by(UMHold.system_id).\ all() for merit in held_merits: try: c_dict[merit.user.name][merit.system.name] = merit except KeyError: c_dict[merit.user.name] = {merit.system.name: merit} systems = session.query(UMSystem).order_by(UMSystem.id).all() systems = session.query(UMSystem).\ filter(UMSystem.sheet_src == EUMSheet.main).\ order_by(UMSystem.id).\ all() system_names = [sys.name for sys in systems] rows = [] for cmdr in c_dict: Loading @@ -597,7 +624,7 @@ def get_admin(session, member): Otherwise, raise NoMatch. """ try: return session.query(AdminPerm).filter_by(id=member.id).one() return session.query(AdminPerm).filter(AdminPerm.id == member.id).one() except sqla_oexc.NoResultFound as exc: raise cog.exc.NoMatch(member.display_name, 'Admin') from exc Loading Loading @@ -631,8 +658,12 @@ def add_role_perm(session, cmd, server, role): def remove_channel_perm(session, cmd, server, channel): try: session.delete(session.query(ChannelPerm). filter_by(cmd=cmd, server_id=server.id, channel_id=channel.id).one()) perm = session.query(ChannelPerm).\ filter(ChannelPerm.cmd == cmd, ChannelPerm.server_id == server.id, ChannelPerm.channel_id == channel.id).\ one() session.delete(perm) session.commit() except sqla_oexc.NoResultFound as exc: raise cog.exc.InvalidCommandArgs("Channel permission does not exist.") from exc Loading @@ -640,8 +671,12 @@ def remove_channel_perm(session, cmd, server, channel): def remove_role_perm(session, cmd, server, role): try: session.delete(session.query(RolePerm). filter_by(cmd=cmd, server_id=server.id, role_id=role.id).one()) perm = session.query(RolePerm).\ filter(RolePerm.cmd == cmd, RolePerm.server_id == server.id, RolePerm.role_id == role.id).\ one() session.delete(perm) session.commit() except sqla_oexc.NoResultFound as exc: raise cog.exc.InvalidCommandArgs("Role permission does not exist.") from exc Loading @@ -666,8 +701,11 @@ def check_channel_perms(session, cmd, server, channel): Raises InvalidPerms if fails permission check. """ channels = [perm.channel_id for perm in session.query(ChannelPerm). filter_by(cmd=cmd, server_id=server.id)] perms = session.query(ChannelPerm).\ filter(ChannelPerm.cmd == cmd, ChannelPerm.server_id == server.id).\ all() channels = [perm.channel_id for perm in perms] if channels and channel.id not in channels: raise cog.exc.InvalidPerms("The '{}' command is not permitted on this channel.".format( cmd.lower())) Loading @@ -681,8 +719,11 @@ def check_role_perms(session, cmd, server, member_roles): Raises InvalidPerms if fails permission check. """ perm_roles = {perm.role_id for perm in session.query(RolePerm). filter_by(cmd=cmd, server_id=server.id)} perms = session.query(RolePerm).\ filter(RolePerm.cmd == cmd, RolePerm.server_id == server.id).\ all() perm_roles = {perm.role_id for perm in perms} member_roles = {role.id for role in member_roles} if perm_roles and len(member_roles - perm_roles) == len(member_roles): raise cog.exc.InvalidPerms("You do not have the roles for the command.") Loading Loading @@ -1259,3 +1300,41 @@ def get_vote(session, discord_id, vote_type): session.add(the_vote) return the_vote def get_all_snipe_holds(session): """ Args: session: A session onto the db. Returns: A list of UMHolds for the snipe sheet. Empty by default. """ return session.query(UMHold).\ filter(UMHold.sheet_src == EUMSheet.snipe, UMHold.held > 0).\ all() def get_snipe_members_holding(session, guild): """ Find the members who are holding merits on the snipe sheet. For each found member, attempt to resolve them with the guild to mention them. If we cannot resolve their name with guild, just write the name. Format one large message reminding the users and return it. Args: session: A session onto the db. guild: The guild of the server in question. Returns: A string formatted mentioning or naming all users with snipe merits. """ template_msg = "{} is holding {} merits in {}\n" reply = "" for hold in get_all_snipe_holds(session): duser = hold.user.discord_user found = guild.get_member_named(duser.pref_name) mention = found.mention if found else duser.pref_name reply += template_msg.format(mention, hold.held, hold.system.name) return reply