106 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			106 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
import argparse
 | 
						|
import logging
 | 
						|
import pathlib
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
 | 
						|
logger = logging.getLogger('rt-bulk-send')
 | 
						|
 | 
						|
def parse_arguments(arglist):
 | 
						|
    parser = argparse.ArgumentParser()
 | 
						|
 | 
						|
    action = parser.add_mutually_exclusive_group()
 | 
						|
    action.add_argument(
 | 
						|
        '--correspond',
 | 
						|
        dest='action', action='store_const', const='correspond',
 | 
						|
        default='correspond',
 | 
						|
        help="Send correspondence on found tickets (default)",
 | 
						|
    )
 | 
						|
    action.add_argument(
 | 
						|
        '--comment',
 | 
						|
        dest='action', action='store_const', const='comment',
 | 
						|
        help="Comment on found tickets",
 | 
						|
    )
 | 
						|
 | 
						|
    parser.add_argument(
 | 
						|
        '--loglevel',
 | 
						|
        choices=['debug', 'info', 'warning', 'error', 'critical'],
 | 
						|
        default='info',
 | 
						|
        help="Show log messages at this level (default %(default)s)",
 | 
						|
    )
 | 
						|
 | 
						|
    parser.add_argument(
 | 
						|
        'search',
 | 
						|
        help="TicketSQL search, like you would pass to `rt search`",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        'body_file',
 | 
						|
        type=pathlib.Path,
 | 
						|
        help="Path to file that has the content of your correspondence/comment",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        'rt_args', metavar='rt arguments',
 | 
						|
        nargs=argparse.REMAINDER,
 | 
						|
        help="Additional arguments to pass to `rt correspond/comment`",
 | 
						|
    )
 | 
						|
    return parser.parse_args(arglist)
 | 
						|
 | 
						|
def setup_logger(logger, loglevel, stream):
 | 
						|
    formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
 | 
						|
    handler = logging.StreamHandler(stream)
 | 
						|
    handler.setFormatter(formatter)
 | 
						|
    logger.addHandler(handler)
 | 
						|
    logger.setLevel(loglevel)
 | 
						|
 | 
						|
def act_on_ticket(ticket_id, args, body_file):
 | 
						|
    body_file.seek(0)
 | 
						|
    return subprocess.run(
 | 
						|
        ['rt', args.action, *args.rt_args, '-m', '-', ticket_id],
 | 
						|
        stdin=body_file,
 | 
						|
        check=True,
 | 
						|
    )
 | 
						|
 | 
						|
def main(arglist=None, stdout=sys.stdout, stderr=sys.stderr):
 | 
						|
    args = parse_arguments(arglist)
 | 
						|
    setup_logger(logger, getattr(logging, args.loglevel.upper()), stderr)
 | 
						|
    try:
 | 
						|
        body_file = args.body_file.open()
 | 
						|
    except OSError as error:
 | 
						|
        logger.critical("error opening {}: {}".format(args.body_file, error))
 | 
						|
        return 3
 | 
						|
    if not body_file.seekable():
 | 
						|
        logger.critical("file {} must be seekable".format(args.body_file))
 | 
						|
        with body_file:
 | 
						|
            return 3
 | 
						|
 | 
						|
    action_verb = args.action.title()
 | 
						|
    failures = 0
 | 
						|
    with body_file, subprocess.Popen(
 | 
						|
            ['rt', 'search', '-i', args.search],
 | 
						|
            stdout=subprocess.PIPE,
 | 
						|
            universal_newlines=True,
 | 
						|
    ) as search_pipe:
 | 
						|
        for line in search_pipe.stdout:
 | 
						|
            ticket_id = line.rstrip('\n')
 | 
						|
            if not ticket_id:
 | 
						|
                continue
 | 
						|
            logger.info("%sing on %s", action_verb, ticket_id)
 | 
						|
            try:
 | 
						|
                act_on_ticket(ticket_id, args, body_file)
 | 
						|
            except subprocess.CalledProcessError as error:
 | 
						|
                logger.error("Failed to %s on %s: rt returned exit code %s",
 | 
						|
                             args.action, ticket_id, error.returncode)
 | 
						|
                failures += 1
 | 
						|
 | 
						|
    if search_pipe.returncode != 0:
 | 
						|
        logger.critical("`rt search` returned exit code %s", search_pipe.returncode)
 | 
						|
        return 4
 | 
						|
    elif failures == 0:
 | 
						|
        return 0
 | 
						|
    else:
 | 
						|
        return min(10 + failures, 99)
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    exit(main())
 |