Skip to content

Helpdesk

helpdesk

Functionality around the HelpDesk / LiveChat API

__all__ = ['HelpDeskClient', 'Mail', 'MailClient', 'Recipient'] module-attribute

HelpDeskClient(config: Optional[Config] = None)

Source code in pytanis/helpdesk/client.py
def __init__(self, config: Optional[Config] = None):
    if config is None:
        config = get_cfg()
    self._config = config
    # Important: Always use a custom User-Agent, never a generic one.
    # Generic User-Agents are filtered by helpdesk to reduce spam.
    self._headers = {"User-Agent": "Pytanis"}

    self._get_throttled = self._get
    self._post_throttled = self._post
    self.set_throttling(2, 1)  # we are nice by default

create_ticket(ticket: NewTicket)

Source code in pytanis/helpdesk/client.py
def create_ticket(self, ticket: NewTicket):
    return self.post("tickets", data=ticket.dict())

get(endpoint: str, params: Optional[Dict[str, str]] = None) -> JSON

Retrieve data via throttled GET request and return the JSON

Source code in pytanis/helpdesk/client.py
def get(self, endpoint: str, params: Optional[Dict[str, str]] = None) -> JSON:
    """Retrieve data via throttled GET request and return the JSON"""
    resp = self._get_throttled(endpoint, params)
    resp.raise_for_status()
    return resp.json()

list_agents() -> List[Agent]

Source code in pytanis/helpdesk/client.py
def list_agents(self) -> List[Agent]:
    agents = self.get("agents")
    assert isinstance(agents, List)
    return [Agent.parse_obj(dct) for dct in agents]

list_teams() -> List[Team]

Source code in pytanis/helpdesk/client.py
def list_teams(self) -> List[Team]:
    teams = self.get("teams")
    assert isinstance(teams, List)
    return [Team.parse_obj(dct) for dct in teams]

post(endpoint: str, data: Dict[str, Any], params: Optional[Dict[str, str]] = None) -> JSON

Source code in pytanis/helpdesk/client.py
def post(self, endpoint: str, data: Dict[str, Any], params: Optional[Dict[str, str]] = None) -> JSON:
    resp = self._post_throttled(endpoint, data, params)
    resp.raise_for_status()
    return resp.json()

set_throttling(calls: int, seconds: int)

Throttle the number of calls per seconds to the Pretalx API

Source code in pytanis/helpdesk/client.py
def set_throttling(self, calls: int, seconds: int):
    """Throttle the number of calls per seconds to the Pretalx API"""
    _logger.debug("throttling", calls=calls, seconds=seconds)
    self._get_throttled = throttle(calls, seconds)(self._get)
    self._post_throttled = throttle(calls, seconds)(self._post)

Mail

Mail template

Use the data field to store additional information

You can use the typical Format String Syntax and the objects recipient and mail to access metadata to complement the template, e.g.:

Hello {recipient.address_as},

We hope it's ok to address you your first name rather than using your full name being {recipient.name}.
Have you read the email's subject '{mail.subject}'? How is your work right now at {recipient.data.company}?

Cheers!

agent_id: str class-attribute

data: Optional[MetaData] class-attribute

recipients: List[Recipient] class-attribute

status: str = 'solved' class-attribute

subject: str class-attribute

team_id: str class-attribute

text: str class-attribute

MailClient(helpdesk_client: Optional[HelpDeskClient] = None)

Mail client for mass mails over HelpDesk

Source code in pytanis/helpdesk/mail.py
def __init__(self, helpdesk_client: Optional[HelpDeskClient] = None):
    if helpdesk_client is None:
        helpdesk_client = HelpDeskClient()
    self._helpdesk_client = helpdesk_client
    self.dry_run: Callable[[NewTicket], None] = self.print_new_ticket

dry_run: Callable[[NewTicket], None] = self.print_new_ticket instance-attribute

print_new_ticket(ticket: NewTicket) staticmethod

Default action in a dry-run. Mainly for making sure you sent what you mean!

Overwrite it by assigning to self.dry_run another function

ToDo: Make this function nice, maybe use the rich library even

Source code in pytanis/helpdesk/mail.py
@staticmethod
def print_new_ticket(ticket: NewTicket):
    """Default action in a dry-run. Mainly for making sure you sent what you mean!

    Overwrite it by assigning to self.dry_run another function

    ToDo: Make this function nice, maybe use the `rich` library even
    """
    print("#" * 40)
    print(f"Recipient: {ticket.requester.name} <{ticket.requester.email}>")
    print(f"Subject: {ticket.subject}")
    print(f"{ticket.message.text}")

send(mail: Mail, dry_run: bool = True) -> Tuple[List[Tuple[Recipient, Optional[Ticket]]], List[Tuple[Recipient, Exception]]]

Send a mail to all recipients using HelpDesk

Source code in pytanis/helpdesk/mail.py
def send(
    self, mail: Mail, dry_run: bool = True
) -> Tuple[List[Tuple[Recipient, Optional[Ticket]]], List[Tuple[Recipient, Exception]]]:
    """Send a mail to all recipients using HelpDesk"""
    errors = []
    tickets = []
    for recipient in tqdm(mail.recipients):
        recip_mail = mail.copy()
        try:
            recip_mail.subject = mail.subject.format(recipient=recipient, mail=mail)
            # be aware here that the body might reference to subject line, so it must be filled already
            recip_mail.text = recip_mail.text.format(recipient=recipient, mail=recip_mail)
            ticket = self._create_ticket(recip_mail, recipient)
            if dry_run:
                self.print_new_ticket(ticket)
                resp_ticket = None
            else:
                resp = self._helpdesk_client.create_ticket(ticket)
                resp_ticket = Ticket.parse_obj(resp)
        except Exception as e:
            errors.append((recipient, e))
        else:
            tickets.append((recipient, resp_ticket))
    return tickets, errors

Recipient

Details about the recipient

Use the data field to store additional information

address_as: Optional[str] class-attribute

data: Optional[MetaData] class-attribute

email: str class-attribute

name: str class-attribute

fill_with_name(v, values) classmethod

Source code in pytanis/helpdesk/mail.py
@validator("address_as")
@classmethod
def fill_with_name(cls, v, values):
    if v is None:
        v = values["name"]
    return v