コード例 #1
0
ファイル: zendesk.py プロジェクト: thxireland/SF
class ZenWorker(object):
    def __init__(self, url, email, api, search_string):
        self.zendesk = Zendesk(url, zdesk_email=email, zdesk_api=api)
        self.barcode_re = re.compile(r"barcode\s(\d+)")
        self.username_re = re.compile(r"assigned_to\s(\w+)")
        self.search = search_string
        self.me = self.zendesk.users_me()['user']

    def run(self):
        """
        Iterates through the tickets found by SEARCH_STRING and
        """
        try:
            result = self.zendesk.search(query=self.search, get_all_pages=True)
        except Exception as exc:
            logging.error("encountered an exception during a zendesk search: %s", str(exc))
            return
        logging.debug("found %s tickets in the queue", str(result['count']))
        tickets = {}
        # iterating over the tickets which haven't been touched
        for ticket in result['results']:
            try:
                comments = self.zendesk.ticket_comments(ticket_id=ticket['id'])
            except Exception as exc:
                logging.error("encountered an error %s while trying to fetch comments "
                              "for ticket: %s", str(exc), str(ticket['id']))
            # check if it's open and there's a cmment from a human
            if ticket['status'] == 'open' and comments.get("count", 0) > 1:  #and
                    #comments['comments'][-1].get('author_id') != self.me['id']):
                logging.info("ticket %s is open and has comments", str(ticket['id']))
                continue
            # get the user, barcode and ticket id
            user_match = self.username_re.search(ticket['description'])
            barcode_match = self.barcode_re.search(ticket['description'])
            if not (user_match and barcode_match):
                continue
            # TODO: uncomment
            user = user_match.group(1)
            barcode = barcode_match.group(1)
            tickets[ticket['id']] = TicketData(user, barcode, ticket['status'])
        return tickets

    def update_ticket(self, ticket_id, message):
        """
        Updates a ticket with given ticket_id with a message.
        """
        data = {
            "ticket": {
                "id": ticket_id,
                "comment": {
                    "public": True,
                    "body": message
                }
            }
        }
        response = self.zendesk.ticket_update(ticket_id, data)
        logging.debug(response)
コード例 #2
0
ファイル: tasks.py プロジェクト: alessandrohc/zendeskspent
def sync_remote(*args, **kwargs):
    zendesk = Zendesk(
        settings.ZENDESK_BASE_URL,
        settings.ZENDESK_EMAIL,
        settings.ZENDESK_PASSWORD,
        api_version=settings.ZENDESK_API_VERSION,
    )

    field_names = Ticket.get_all_field_names("pk", "_fields")
    queryfmt = "type:ticket organization_id:{0.organization_external}"

    for company in Company.objects.all():
        next_page = True
        page = 1
        registers = []
        while next_page:
            try:
                results = zendesk.search(query=queryfmt.format(company), page=page)
            except ZendeskError as err:
                # view does not exist
                if "error" in err.msg and err.error_code == 404:
                    break
            if "results" not in results:
                break
            for ticket in results["results"]:
                params = {}
                for name in field_names:
                    params[name] = ticket[name]
                obj = None
                try:
                    obj = Ticket.objects.get(pk=params["id"])
                    for name, value in params.iteritems():  # update
                        setattr(obj, name, value)
                except Ticket.DoesNotExist:
                    obj = Ticket.objects.create(**params)
                finally:
                    obj.fields = ticket["fields"]
                    obj.save()
                registers.append(obj.pk)

            next_page = results.get("next_page", False)
            page += 1

        # database clean
        Ticket.objects.filter(organization_id=company.organization_external).exclude(pk__in=registers).delete()

    return Ticket.objects.count()
コード例 #3
0
ファイル: sm.py プロジェクト: octoberry/smart-support-python
	def zdeskNotify(self):
		room = "55a12b9bb82d04e3b232a99c"
		zendesk = Zendesk(**testconfig)
		results = zendesk.search(query='type:ticket sort:desc', page=1)
		for ticket in results['results']:
			if ticket['status'] == 'solved':
				#send message to ws
				#print "Hey man problem resolved"
				self.sayAnswer(room, "Андрей, мы нашли проблему и все исправили, пишите если опять будет регулярно повторяться проблема")

				ticket_id = ticket['id']
				data = {
					"ticket": {
						"id": ticket_id,
						"status": "closed"
						}
					}
				response = zendesk.ticket_update(ticket_id, data)
				#self.sayAnswer(room, "ticket %s closed" % ticket_id)
				print "tiket %s closed" % ticket_id
コード例 #4
0
ファイル: example.py プロジェクト: fprimex/zdesk
    }
}
result = zendesk.group_create(data=new_group)
group_id = get_id_from_url(result)

# Show
zendesk.group_show(id=group_id)

# Delete
zendesk.group_delete(id=group_id)


################################################################
# TAGS
################################################################

# List
zendesk.tags_list()


################################################################
# TICKET TYPES
################################################################
zendesk.ticket_fields_list()


################################################################
# SEARCH
################################################################
results = zendesk.search(query='type:ticket sort:desc', page=1)
コード例 #5
0
ファイル: __init__.py プロジェクト: Allard-Timothy/zdesk
                'agent': 123
            },
        ]
    }
}
result = zendesk.group_create(data=new_group)
group_id = get_id_from_url(result)

# Show
zendesk.group_show(id=group_id)

# Delete
zendesk.group_delete(id=group_id)

################################################################
## TAGS
################################################################

# List
zendesk.tags_list()

################################################################
## TICKET TYPES
################################################################
zendesk.ticket_fields_list()

################################################################
## SEARCH
################################################################
results = zendesk.search(query='type:ticket sort:desc', page=1)
コード例 #6
0
ファイル: zdgrab.py プロジェクト: fprimex/zdgrab
def zdgrab(verbose=False,
           tickets=None,
           work_dir=os.path.join(os.path.expanduser('~'), 'zdgrab'),
           agent='me',
           ss_host=None,
           ss_id=None,
           ss_secret=None,
           ss_command=None):
    "Download attachments from Zendesk tickets."

    # SendsafelyGrab will only be invoked if the comment body contains a link.
    # See the corresponding REGEX used by them, which has been ported to Python:
    # https://github.com/SendSafely/Windows-Client-API/blob/master/SendsafelyAPI/Utilities/ParseLinksUtility.cs
    ss_link_re = r'https://[-a-zA-Z\.]+/receive/\?[-A-Za-z0-9&=]+packageCode=[-A-Za-z0-9_]+#keyCode=[-A-Za-z0-9_]+'
    ss_link_pat = re.compile(ss_link_re)

    vp = verbose_printer(verbose)

    cfg = zdgrab.getconfig()

    if cfg['zdesk_url'] and (
            cfg['zdesk_oauth'] or
            (cfg['zdesk_email'] and cfg['zdesk_password']) or
            (cfg['zdesk_email'] and cfg['zdesk_api'])
            ):
        vp.print('Configuring Zendesk with:\n'
                '  url: {}\n'
                '  email: {}\n'
                '  token: {}\n'
                '  password/oauth/api: (hidden)\n'.format(cfg['zdesk_url'],
                                                cfg['zdesk_email'],
                                                repr(cfg['zdesk_token'])))

        zd = Zendesk(**cfg)
    else:
        msg = textwrap.dedent("""\
            Error: Need Zendesk config to continue.

            Config file (~/.zdeskcfg) should be something like:
            [zdesk]
            url = https://example.zendesk.com
            email = [email protected]
            api = dneib393fwEF3ifbsEXAMPLEdhb93dw343
            # or
            # oauth = ndei393bEwF3ifbEsssX

            [zdgrab]
            agent = [email protected]
            """)
        print(msg)
        return 1

    # Log the cfg
    vp.print('Running with zdgrab config:\n'
            ' verbose: {}\n'
            ' tickets: {}\n'
            ' work_dir: {}\n'
            ' agent: {}\n'.format(verbose, tickets, work_dir, agent))

    # tickets=None means default to getting all of the attachments for this
    # user's open tickets. If tickets is given, try to split it into ints
    if tickets:
        # User gave a list of tickets
        try:
            tickets = [int(i) for i in tickets.split(',')]
        except ValueError:
            print('Error: Could not convert to integers: {}'.format(tickets))
            return 1

    # dict of paths to attachments retrieved to return. format is:
    # { 'path/to/ticket/1': [ 'path/to/attachment1', 'path/to/attachment2' ],
    #   'path/to/ticket/2': [ 'path/to/attachment1', 'path/to/attachment2' ] }
    grabs = {}

    # Save the current directory so we can go back once done
    start_dir = os.getcwd()

    # Normalize all of the given paths to absolute paths
    work_dir = os.path.abspath(work_dir)

    # Check for and create working directory
    if not os.path.isdir(work_dir):
        os.makedirs(work_dir)

    # Change to working directory to begin file output
    os.chdir(work_dir)

    vp.print('Retrieving tickets')

    if tickets:
        # tickets given, query for those
        response = zd.tickets_show_many(ids=','.join([s for s in map(str, tickets)]),
                                        get_all_pages=True)
        result_field = 'tickets'
    else:
        # List of tickets not given. Get all of the attachments for all of this
        # user's open tickets.
        q = 'status<solved assignee:{}'.format(agent)
        response = zd.search(query=q, get_all_pages=True)
        result_field = 'results'

    if response['count'] == 0:
        # No tickets from which to get attachments
        print("No tickets provided for attachment retrieval.")
        return {}
    else:
        vp.print("Located {} tickets".format(response['count']))

    results = response[result_field]

    # Fix up some headers to use for downloading the attachments.
    # We're going to borrow the zdesk object's httplib client.
    headers = {}
    if zd.zdesk_email is not None and zd.zdesk_password is not None:
        headers["Authorization"] = "Basic {}".format(
            base64.b64encode(zd.zdesk_email.encode('ascii') + b':' +
                             zd.zdesk_password.encode('ascii')))

    # Get the attachments from the given zendesk tickets
    for ticket in results:
        if result_field == 'results' and ticket['result_type'] != 'ticket':
            # This is not actually a ticket. Weird. Skip it.
            continue

        vp.print('Ticket {}'.format(ticket['id']))

        ticket_dir = os.path.join(work_dir, str(ticket['id']))
        ticket_com_dir = os.path.join(ticket_dir, 'comments')
        comment_num = 0

        response = zd.ticket_audits(ticket_id=ticket['id'],
                                    get_all_pages=True)
        audits = response['audits']

        for audit in audits:
            for event in audit['events']:
                if event['type'] != 'Comment':
                    # This event isn't a comment. Skip it.
                    continue

                comment_num += 1
                comment_dir = os.path.join(ticket_com_dir, str(comment_num))

                for attachment in event['attachments']:
                    name = attachment['file_name']
                    if os.path.isfile(os.path.join(comment_dir, name)):
                        vp.print(' Attachment {} already present'.format(name))
                        continue

                    # Get this attachment
                    vp.print(' Downloading attachment {}'.format(name))

                    # Check for and create the download directory
                    if not os.path.isdir(comment_dir):
                        os.makedirs(comment_dir)

                    os.chdir(comment_dir)
                    response = zd.client.request('GET',
                                                 attachment['content_url'],
                                                 headers=headers)

                    if response.status_code != 200:
                        print('Error downloading {}'.format(
                            attachment['content_url']))
                        continue

                    with open(name, 'wb') as f:
                        f.write(response.content)

                    # Check for and create the grabs entry to return
                    if ticket_dir not in grabs:
                        grabs[ticket_dir] = []

                    grabs[ticket_dir].append(
                        os.path.join('comments', str(comment_num), name))

                    # Let's try to extract this if it's compressed
                    zdsplode(name, verbose=verbose)

                if ss_command:
                    if not ss_link_pat.search(event['body']):
                        # Don't bother invoking SendSafelyGrab if the body has
                        # no SendSafely links.
                        continue

                    try:
                        ss_output = subprocess.check_output(ss_command.split() + ["-v",
                            "-h", ss_host, "-k", ss_id, "-s", ss_secret,
                            "-d", comment_dir, event['body']],
                            stderr=sys.stderr)

                        if ss_output:
                            # Check for and create the grabs entry to return
                            if ss_output and (ticket_dir not in grabs):
                                grabs[ticket_dir] = []

                        for name in ss_output.splitlines():
                            namestr = name.decode()
                            grabs[ticket_dir].append(
                                os.path.join('comments', str(comment_num), namestr))

                            # Let's try to extract this if it's compressed
                            os.chdir(comment_dir)
                            zdsplode(namestr, verbose=verbose)
                    except subprocess.CalledProcessError:
                        # SendSafelyGrab.exe will print its own errors
                        pass

    os.chdir(start_dir)
    return grabs
コード例 #7
0
class Command(BaseCommand):

    option_list = BaseCommand.option_list + (
        make_option(
            '--to-csv',
            action='store_true',
            dest='to-csv',
            default=False,
            help='Export all relevant zendesk items to csv file'),
        )

    def handle(self, *args, **options):
        self.load_settings()
        self.init_zen()

        if options["to-csv"]:
            self.to_csv()

    def load_settings(self):
        with open(os.path.expanduser("~/.hmcts-zendesk"), "r") as config_file:
            self.settings = yaml.load(config_file.read())

    def init_csv(self, f, dialect=csv.excel):
        self.writer = csv.writer(f, dialect=dialect)

    def init_zen(self):
        self.zendesk = Zendesk(
            self.settings["url"],
            self.settings["email"],
            self.settings["password"])

    def get_all_fieldnames(self):
        return \
            SIMPLE_FIELDS + \
            NEWLINE_FIELDS + \
            STRUCTURED_FIELDS

    def to_csv(self):
        self.outfile = self.settings.get('outfile', 'zendump.csv')
        self.batch_size = 100
        with open(self.outfile, "wb") as f:

            self.init_csv(f)
            self.writerow(self.get_all_fieldnames())  # CSV Header

            for ticket in self.yield_tickets():
                self.writerow(self.ticket_to_row(ticket))

    def writerow(self, row):
        self.writer.writerow([
            cell.encode("utf-8")
            for cell in row])

    def ticket_to_row(self, ticket):
        return \
            self.get_simple_fields(ticket) + \
            self.get_newline_fields(ticket) + \
            self.get_structured_fields(ticket)

    def parse_simple_field(self, field):
        if field is None:
            return ""
        else:
            try:
                return field.encode("utf-8")
            except AttributeError:  # ints
                return str(field).encode("utf-8")

    def get_simple_fields(self, ticket):
        return [
            self.parse_simple_field(ticket.get(field))
            for field in SIMPLE_FIELDS]

    def parse_newline_field(self, field):
        return field or ""

    def get_newline_fields(self, ticket):
        return [
            self.parse_newline_field(ticket.get(field))
            for field in NEWLINE_FIELDS]

    def parse_structured_field(self, field):
        if field is None:
            return ""
        else:
            try:
                return field.encode("utf-8")
            except AttributeError:  # ints
                return str(field).encode("utf-8")

    def get_structured_fields(self, ticket):
        return [
            self.parse_structured_field(ticket.get(field))
            for field in STRUCTURED_FIELDS]

    def yield_tickets(self):
        """Generates batches of tickets"""

        for raw_query in self.settings["raw_queries"]:

            first_page = self.zendesk.search(raw_query=raw_query)
            for item in first_page["results"]:
                yield item
            count = first_page["count"]
            print "{} tickets from ZenDesk match filter.".format(count)

            page_count, remainder = divmod(count, self.batch_size)
            page_count = page_count + 1 if remainder else page_count

            for page_id in range(2, page_count):
                qpage = " page:{}".format(page_id)
                page = self.zendesk.search(raw_query=raw_query + qpage)
                for item in page["results"]:
                    yield item
コード例 #8
0
ファイル: zdgrab.py プロジェクト: brianshumate/zdgrab
def zdgrab(verbose=False,
           tickets=None,
           work_dir=os.path.join(os.path.expanduser('~'), 'zdgrab'),
           agent='me'):
    "Download attachments from Zendesk tickets."

    cfg = zdgrab.getconfig()

    if cfg['zdesk_url'] and cfg['zdesk_email'] and cfg['zdesk_password']:
        if verbose:
            print('Configuring Zendesk with:\n'
                  '  url: {}\n'
                  '  email: {}\n'
                  '  token: {}\n'
                  '  password: (hidden)\n'.format( cfg['zdesk_url'],
                                                   cfg['zdesk_email'],
                                                   repr(cfg['zdesk_token']) ))
        zd = Zendesk(**cfg)
    else:
        msg = textwrap.dedent("""\
            Error: Need Zendesk config to continue.

            Config file (~/.zdeskcfg) should be something like:
            [zdesk]
            url = https://example.zendesk.com
            email = [email protected]
            password = dneib393fwEF3ifbsEXAMPLEdhb93dw343
            token = 1

            [zdgrab]
            agent = [email protected]
            """)
        print(msg)
        return 1

    # Log the cfg
    if verbose:
        print('Running with zdgrab config:\n'
              ' verbose: {}\n'
              ' tickets: {}\n'
              ' work_dir: {}\n'
              ' agent: {}\n'.format(verbose, tickets, work_dir, agent))

    # tickets=None means default to getting all of the attachments for this
    # user's open tickets. If tickets is given, try to split it into ints
    if tickets:
        # User gave a list of tickets
        try:
            tickets = [int(i) for i in tickets.split(',')]
        except ValueError:
            print('Error: Could not convert to integers: {}'.format(tickets))
            return 1

    # dict of paths to attachments retrieved to return. format is:
    # { 'path/to/ticket/1': [ 'path/to/attachment1', 'path/to/attachment2' ],
    #   'path/to/ticket/2': [ 'path/to/attachment1', 'path/to/attachment2' ] }
    grabs = {}

    # Save the current directory so we can go back once done
    start_dir = os.getcwd()

    # Normalize all of the given paths to absolute paths
    work_dir = os.path.abspath(work_dir)

    # Check for and create working directory
    if not os.path.isdir(work_dir):
        os.makedirs(work_dir)

    # Change to working directory to begin file output
    os.chdir(work_dir)

    if verbose:
        print('Retrieving tickets')

    if tickets:
        # tickets given, query for those
        response = zd.tickets_show_many(ids=','.join([s for s in map(str,tickets)]),
                get_all_pages=True)
        result_field = 'tickets'
    else:
        # List of tickets not given. Get all of the attachments for all of this
        # user's open tickets.
        q = 'status<solved assignee:{}'.format(agent)
        response = zd.search(query=q, get_all_pages=True)
        result_field = 'results'

    if response['count'] == 0:
        # No tickets from which to get attachments
        print("No tickets provided for attachment retrieval.")
        return {}

    results = response[result_field]

    # Fix up some headers to use for downloading the attachments.
    # We're going to borrow the zdesk object's httplib client.
    headers = {}
    if zd.zdesk_email is not None and zd.zdesk_password is not None:
        headers["Authorization"] = "Basic {}".format(
            base64.b64encode(zd.zdesk_email.encode('ascii') + b':' +
                             zd.zdesk_password.encode('ascii')))

    # Get the attachments from the given zendesk tickets
    for ticket in results:
        if result_field == 'results' and ticket['result_type'] != 'ticket':
            # This is not actually a ticket. Weird. Skip it.
            continue

        if verbose:
            print('Ticket {}'.format(ticket['id']))

        ticket_dir = os.path.join(work_dir, str(ticket['id']))
        ticket_com_dir = os.path.join(ticket_dir, 'comments')
        comment_num = 0

        if verbose:
            print('Retrieving audits')

        response = zd.ticket_audits(ticket_id=ticket['id'],
                get_all_pages=True)
        audits = response['audits']

        for audit in audits:
            for event in audit['events']:
                if event['type'] != 'Comment':
                    # This event isn't a comment. Skip it.
                    continue

                comment_num += 1
                comment_dir = os.path.join(ticket_com_dir, str(comment_num))

                if verbose and event['attachments']:
                    print('Comment {}'.format(comment_num))

                for attachment in event['attachments']:
                    name = attachment['file_name']
                    if os.path.isfile(os.path.join(comment_dir, name)):
                        if verbose:
                            print('Attachment {} already present'.format(name))
                        continue

                    # Get this attachment
                    if verbose:
                        print('Attachment {}'.format(name))

                    # Check for and create the download directory
                    if not os.path.isdir(comment_dir):
                        os.makedirs(comment_dir)

                    os.chdir(comment_dir)
                    response = zd.client.request('GET',
                                                 attachment['content_url'],
                                                 headers=headers)
                    
                    if response.status_code != 200:
                        print('Error downloading {}'.format(attachment['content_url']))
                        continue

                    with open(name, 'wb') as f:
                        f.write(response.content)

                    # Check for and create the grabs entry to return
                    if ticket_dir not in grabs:
                        grabs[ticket_dir] = []

                    grabs[ticket_dir].append(
                        os.path.join('comments', str(comment_num), name) )

                    # Let's try to extract this if it's compressed
                    zdsplode(name, verbose=verbose)

    os.chdir(start_dir)
    return grabs