RTIR

Request Tracker for Incident Response is a ticketing system which provides pre-configured queues and workflows designed for incident response teams.

Case Management · RTIR

Details

IDRTIR
ProviderOpen Source
CategoryCase Management
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Use the Request Tracker for Incident Response (RTIR) integration to manage tickets and incidents.

This integration was integrated and tested with RTIR v4.4.2, using the SDK python-rtir v1.0.11.

Use Cases

  • Create new tickets.
  • Resolve existing tickets.
  • Search for tickets using filters.
  • Edit tickets.
  • Get ticket data.

Known Limitations

  • This integration does not support the lifecycle countermeasures.
  • Custom fields cannot be created through this integration, but custom fields created on RTIR can be filled when creating a new ticket.

Configure RTIR on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for RTIR.
  3. Click Add instance to create and configure a new integration instance.
    • Name: A textual name for the integration instance.
    • Server URL (for example: https://192.168.0.1)
    • Username
    • Password
    • Token
    • Certificate
    • Private Key
    • Trust any certificate (not secure)
    • Use system proxy settings
    • Use system proxy settings
    • Fetch incidents
    • Fetch incidents with priority greater or equal to
    • Fetch incidents of the following status
    • Incident type
    • Referer request header
  4. Click Test to validate the URLs and token.

Fetched Incidents Data

The integration fetches newly created tickets. The tickets are fetched by ID in ascending order, starting from 0 at the first fetch. The fetch is filtered by priority and status, which can be set in the integration settings. The initial fetch interval is one minute.

The following data is fetched for each ticket:

  • General ticket information: ID, priority, created date, subject, queue, custom fields, and so on.
  • Ticket history.
  • Ticket attachments.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.

  1. Create a new ticket: rtir-create-ticket
  2. Search for tickets: rtir-search-ticket
  3. Close a resolved ticket: rtir-resolve-ticket
  4. Edit a ticket: rtir-edit-ticket
  5. Get the history of a ticket: rtir-ticket-history
  6. Get ticket details: rtir-get-ticket
  7. Get ticket attachments: rtir-ticket-attachments
  8. Add a comment to a ticket: rtir-add-comment

1. Create a new ticket

Creates a new ticket in RFIR.

Base Command

rtir-create-ticket

Input
Argument Name Description
queue Where to create the ticket.
subject Subject of the ticket.
requestor Email address of the requester.
cc Sends a carbon-copy (cc) of this update to a comma separated list of email addresses. These people will also receive future updates.
admin-cc Sends a carbon-copy (cc) of this update to a comma separated list of administrative email addresses. These people will also receive future updates.
owner Ticket owner
status Ticket status
priority Ticket priority
text The ticket content
initial-priority Initial priority of ticket
final-priority Final priority of ticket
member-of Ticket MembersOF links
members Ticket Members links
attachment

Comma separated list of entry IDs of attachment to add to the ticket (for example: entryID1,entryID2).

customfields

Ticket custom fields, in the following format: field1=value1,field2=value2.

For example: IP=8.8.8.8,HowReported=Email.

Note: This command does not create custom fields, these should be created on RTIR.

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.InitialPriority Ticket initial priority 0-100).
RTIR.Ticket.Priority Ticket priority (0-100).
RTIR.Ticket.FinalPriority Ticket final priority (0-100).
RTIR.Ticket.Owner Ticket owner.
RTIR.Ticket.Subject Ticket subject.
RTIR.Ticket.Creator Ticket creator.

 

Command Example
!rtir-create-ticket subject=NewTicket queue="Incident Reports" priority=70 requestor=root@localhost customfields="IP=8.8.8.8,How Reported=Email"
Raw Output
Ticket 121 was created successfully.
Context Example
{
    "RTIR": {
     "Ticket": {
        "CF_How Reported": "Email",
        "CF_IP": "8.8.8.8",
        "Priority": 70,
        "Requestor": "root@localhost",
        "Subject": "NewTicket"
      }
    }
}

2. Search for tickets

Searches for tickets in RTIR using specified filters.

Base Command

rtir-search-ticket

Input
Argument Name Description
ticket-id Ticket ID
subject Ticket subject
queue

CSV list of ticket queues.

For example: General,Incident reports,Incidents

status Ticket status
creator Ticket creator
priority-equal-to Ticket priority (range 0-100)
priority-greater-than Ticket priority (range 0-100)
created-after

Date after which the ticket was created, in the following format: YYYY-MM-DD.

For example: 2011-02-24

created-on

Date the ticket was created, in the following format: YYYY-MM-DD.

For example: 2011-02-24

created-before

Date before which the ticket was created, in the following format: YYYY-MM-DD.

For example: 2011-02-24

owner Ticket owner
due

Ticket due date, in the following format: YYYY-MM-DD.

For example: 2011-02-24

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.State Ticket state.
RTIR.Ticket.Creator Ticket creator.
RTIR.Ticket.Subject Ticket subject.
RTIR.Ticket.Created Ticket creation date.
RTIR.Ticket.Priority Ticket priority (0-100).
RTIR.Ticket.InitialPriority Ticket initial priority (0-100).
RTIR.Ticket.FinalPriority Ticket final priority (0-100).
RTIR.Ticket.Queue Ticket queue.
RTIR.Ticket.Owner Ticket owner.
RTIR.Ticket.IP Ticket custom field - IP address.
RTIR.Ticket.HowReported Ticket custom field - How ticket was reported.
RTIR.Ticket.Customer Ticket custom field - Customer.

 

Command Example
!rtir-search-ticket queue=General created-after=2018-07-10 priority-greater-than=10 status=new
[
    {
        "Created": "Thu Jul 12 03:56:06 2018",
        "Creator": "root",
        "Due": "Not set",
        "FinalPriority": "0",
        "InitialPriority": "0",
        "LastUpdated": "Thu Jul 12 03:56:06 2018",
        "Owner": "Nobody",
        "Priority": "12",
        "Queue": "General",
        "Requestors": [
            ""
        ],
        "Resolved": "Not set",
        "Started": "Not set",
        "Starts": "Not set",
        "Status": "new",
        "Subject": "unbelievable",
        "TimeEstimated": "0",
        "TimeLeft": "0",
        "TimeWorked": "0",
        "Told": "Not set",
        "id": "ticket/21"
    }
  }
]
Context Example
{
    "RTIR": {
       "Ticket": {
        {
            "Created": "Thu Jul 12 03:56:06 2018",
            "Creator": "root",
            "FinalPriority": 0,
            "ID": 21,
            "InitialPriority": 0,
            "Owner": "Nobody",
            "Priority": 12,
            "Queue": "General",
            "State": "new",
            "Subject": "unbelievable"
       }
   }
}

3. Close a resolved ticket

Closes a ticket that has been resolved.

Base Command

rtir-resolve-ticket

Input
Argument Name Description
ticket-id Ticket ID of the ticket to close.

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.State Ticket state.

 

Command Example
!rtir-resolve-ticket ticket-id=121
Raw Output

Ticket 121 was closed successfully.

Context Example
{  
   "RTIR":{  
      "Ticket":{  
         "ID":"121",
         "State":"resolved"
      }
   }
}

4. Edit a ticket

Edit a specific ticket. Ticket ID specifies which ticket to edit.

Base Command

rtir-edit-ticket

Input
Input Parameter Description
ticket-id Ticket ID of the ticket you want to edit.
subject Modified ticket subject.
priority Modified ticket priority (0-100).
final-priority Modified ticket final priority (0-100).
owner Modified ticket owner.
status Modified ticket status.
member-of Modified ticket MembersOF ID
members Modified ticket Members ID
depends-on Modified ticket DependedOn ID
depended-on-by Modified ticket DependedOnBy ID
refers-to Modified ticket RefersTo ID
referred-to-by Modified ticket ReferredToBy ID
due

Modified ticket due date, in the following format: YYYY-MM-DD.

For example: 2011-02-24

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.FinalPriority Ticket final priority (0-100).
RTIR.Ticket.Priority Ticket priority (0-100).
RTIR.Ticket.Owner Ticket owner.
RTIR.Ticket.State Ticket state.
RTIR.Ticket.Subject Ticket subject.

 

Command Example
!rtir-edit-ticket ticket-id=115 final-priority=100 status=open subject=NewTicketSubject
Raw Output
Ticket 115 was edited successfully.
Context Example
{
    "RTIR": {
      "Ticket": {
        "FinalPriority": 100,
        "ID": 115,
        "Owner": "root",
        "Priority": 0,
        "State": "open",
        "Subject": "NewTicketSubject"
       }
    }
}

5. Get the history of a ticket

Get the history of a specified ticket.

Base Command

rtir-ticket-history

Input
Input Parameter Description
ticket-id Ticket ID for which to retrieve the history.

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.History.Content Ticket history content.
RTIR.Ticket.History.Created Ticket history creation date.
RTIR.Ticket.History.Creator Ticket history creator.
RTIR.Ticket.History.Description Ticket history description.
RTIR.Ticket.History.NewValue Value updated in history transaction.

 

Command Example
!rtir-ticket-history ticket-id=1
Raw Output
[
    {
        "Attachments": [
            [
                1,
                "untitled (4b)"
            ]
        ],
        "Content": "test\n",
        "Created": "2018-07-09 07:25:47",
        "Creator": "root",
        "Data": "",
        "Description": "Ticket created by root",
        "Field": "",
        "NewValue": "",
        "OldValue": "",
        "Ticket": "1",
        "TimeTaken": "0",
        "Type": "Create",
        "id": "54"
    }
]
Context Example
{
    "RTIR": {
     "Ticket": {
        "History": [
            {
                "Content": "test\n",
                "Created": "2018-07-09 07:25:47",
                "Creator": "root",
                "Description": "Ticket created by root"
            }
        ],
        "ID": 1
     }
    }
}

6. Get ticket details

Get the details of a specific ticket.

Base Command

rtir-get-ticket

Input
Input Parameter Description
ticket-id Ticket ID for which to retrieve details.

 

Context Output
Path Description
RTIR.Ticket.LinkedTo.ID Linked ticket ID.
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.State Ticket state.
RTIR.Ticket.Creator Ticket creator.
RTIR.Ticket.Subject Ticket subject.
RTIR.Ticket.Created Ticket creation date.
RTIR.Ticket.Priority Ticket priority (0-100).
RTIR.Ticket.InitialPriority Ticket initial priority (0-100).
RTIR.Ticket.FinalPriority Ticket final priority (0-100).
RTIR.Ticket.Queue Ticket queue.
RTIR.Ticket.Owner Ticket owner.
RTIR.Ticket.IP Ticket custom field - IP address.
RTIR.Ticket.HowReported Ticket custom field - How the ticket was reported.
RTIR.Ticket.Customer Ticket custom field - Customer.

 

Command Example
!rtir-get-ticket ticket-id=1
Raw Output
{
    "CF.{Customer}": "",
    "CF.{How Reported}": "Email",
    "CF.{IP}": "8.8.8.8",
    "CF.{Reporter Type}": "",
    "Created": "Mon Jul 09 03:25:47 2018",
    "Creator": "root",
    "Due": "Thu Jul 19 07:47:05 2018",
    "FinalPriority": "0",
    "InitialPriority": "0",
    "LastUpdated": "Thu Jul 12 10:20:02 2018",
    "Owner": "root",
    "Priority": "0",
    "Queue": "Incident Reports",
    "Requestors": [
        ""
    ],
    "Resolved": "Not set",
    "Started": "Thu Jul 19 00:00:00 2018",
    "Starts": "Not set",
    "Status": "open",
    "Subject": "test",
    "TimeEstimated": "0",
    "TimeLeft": "0",
    "TimeWorked": "0",
    "Told": "Not set",
    "id": "ticket/1"
}
Context Example
{
    "RTIR": {
      "Ticket":
        "Created": "Mon Jul 09 03:25:47 2018",
        "Creator": "root",
        "Due": "Thu Jul 19 07:47:05 2018",
        "FinalPriority": 0,
        "HowReported": "Email",
        "ID": 1,
        "IP": "8.8.8.8",
        "InitialPriority": 0,
        "LinkedTo": [
            {
                "ID": 15
            }
        ],
        "Owner": "root",
        "Priority": 0,
        "Queue": "Incident Reports",
        "State": "open",
        "Subject": "test"
      }
    }
}

7. Get ticket attachments

Returns the attachment details of the specified ticket, and the attachment files to download from the War Room.

Base Command

rtir-ticket-attachments

Input
Input Parameter Description
ticket-id Ticket ID for which to retrieve attachments.

 

Context Output
Path Description
RTIR.Ticket.ID Ticket ID.
RTIR.Ticket.Attachment.ID Attachment ID.
RTIR.Ticket.Attachment.Name Attachment file name.
RTIR.Ticket.Attachment.Size Attachment file size.
RTIR.Ticket.Attachment.Type Attachment file type.
File.EntryID Cortex XSOAR entry ID of the attachment.

 

Command Example
!rtir-ticket-attachments ticket-id=41
Raw Output
[
    {
        "ID": 77,
        "Name": "pdf.pdf",
        "Size": "423.8k",
        "Type": "application/octet-stream"
    },
    {
        "ID": 78,
        "Name": "SampleTextFile_10kb.txt",
        "Size": "9.2k",
        "Type": "text/plain"
    }
]
Context Example
{
    "RTIR": {
      "Ticket": {
        "Attachment": [
            {
                "ID": 77,
                "Name": "pdf.pdf",
                "Size": "423.8k",
                "Type": "application/octet-stream"
            },
            {
                "ID": 78,
                "Name": "SampleTextFile_10kb.txt",
                "Size": "9.2k",
                "Type": "text/plain"
            }
        ],
        "ID": 41
      }
    }
}

8. Add a comment to a ticket

Add a textual comment to a specified ticket.

Base Command

rtir-add-comment

Input
Input Parameter Description
ticket-id Ticket ID to add a comment to.
text Text of the comment.
attachment

CSV list of attachment entry IDs to add to the ticket.

For example: entryID1,entryID2

 

Context Output

There is no context output for this command. 

Command Example
!rtir-add-comment text=CommentText ticket-id=113 attachment=1336@cc6f4232-d87e-496e-82b5-6bbeab422243
Raw Output
Added comment to ticket 113 successfully.
Context Example

There is no context example for this command.


9. Add a reply to a ticket

Add a textual reply to a specified ticket.

Base Command

rtir-add-reply

Input
Input Parameter Description
ticket-id Ticket ID to add a comment to.
text Text of the comment.
cc

Email of the user to send the reply.

 

Context Output

There is no context output for this command. 

Command Example
!rtir-add-reply text=replyText ticket-id=113
Raw Output
Added reply to ticket 113 successfully.
Context Example

There is no context example for this command.

Configuration parameters

  • server — Server URL (e.g. http://192.168.0.1) (required)
  • credentials — Username
  • token
  • certificate — Certificate
  • unsecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • fetch_status — Fetch incidents of the following status:
  • fetch_priority — Fetch incidents with priority greater or equal to:
  • fetch_queue — Queue name to fetch incidents from
  • referer — Referer request header
  • fetch_limit — Fetch incidents limit (the max amount of incidents per fetch iteration)

Commands (9)

  • rtir-add-comment

    Add a comment to an existing ticket.

  • rtir-add-reply

    Send a reply from an existing ticket to the user.

  • rtir-create-ticket

    Create new ticket on RTIR.

  • rtir-edit-ticket

    Modify a ticket on RTIR.

  • rtir-get-ticket

    Gets the data for a single ticket.

  • rtir-resolve-ticket

    Sets a ticket as resolved on RTIR.

  • rtir-search-ticket

    Search for tickets on RTIR.

  • rtir-ticket-attachments

    Gets a list of all attachments details and data content related to the ticket.

  • rtir-ticket-history

    Gets a list of all the history items for a given ticket.

from CommonServerPython import *

""" IMPORTS """
import json
import re
import tempfile
import urllib

import requests

""" GLOBAL VARS """
SERVER = None
BASE_URL = ""
USERNAME = None
PASSWORD = None
TOKEN = ""
CERTIFICATE = None
PRIVATE_KEY = None
USE_SSL = None
FETCH_PRIORITY = 0
FETCH_STATUS = ""
FETCH_QUEUE = None
CURLY_BRACKETS_REGEX = r"\{(.*?)\}"  # Extracts string in curly brackets, e.g. '{string}' -> 'string'
apostrophe = "'"
SESSION = requests.session()
SESSION.verify = USE_SSL
REFERER = None
HEADERS = {"Referer": REFERER} if REFERER else {}  # type: dict

""" HELPER FUNCTIONS """


def ticket_to_incident(ticket):
    incident = {
        "name": "RTIR Ticket " + str(ticket["ID"]),
        "rawJSON": json.dumps(ticket),
    }
    attachments, attachments_content = get_ticket_attachments(ticket["ID"])
    if attachments:
        incident_attachments = []
        for i in range(len(attachments)):
            incident_attachments.append({"path": attachments_content[i]["FileID"], "name": attachments[i]["Name"]})

        incident["attachment"] = incident_attachments  # type: ignore
    return incident


def ticket_string_to_id(ticket_string):
    """
    Translates 'ticket/1' to the integer 1
    """
    slash_index = ticket_string.index("/")
    ticket_id = int(ticket_string[slash_index + 1 :])
    return ticket_id


class TempFile:
    def __init__(self, data):
        _, self.path = tempfile.mkstemp()
        with open(self.path, "w") as temp_file:
            temp_file.write(data)

    def __del__(self):
        os.remove(self.path)


def http_request(method, suffix_url, data=None, files=None, query=None):
    # Returns the http request

    url = urljoin(BASE_URL, suffix_url)
    params = {"user": USERNAME, "pass": PASSWORD} if not TOKEN else {}
    if query:
        params.update(query)

    cert = TempFile(CERTIFICATE) if CERTIFICATE else None
    key = TempFile(PRIVATE_KEY) if PRIVATE_KEY else None
    cert_key_pair = (cert.path, key.path) if cert and key else None  # type: ignore[attr-defined]

    response = SESSION.request(method, url, data=data, params=params, files=files, headers=HEADERS, cert=cert_key_pair)

    # handle request failure
    if response.status_code not in {200}:
        message = parse_error_response(response)
        raise DemistoException(f"Error in API call with status code {response.status_code}\n{message}")

    return response


def parse_error_response(response):
    try:
        res = response.json()
        msg = res.get("message")
        if res.get("details") and res.get("details")[0].get("message"):
            msg = msg + "\n" + json.dumps(res.get("details")[0])
    except Exception:
        return response.text
    return msg


def parse_ticket_data(raw_query):
    raw_tickets = search_ticket_request(raw_query)
    headers = ["ID", "Subject", "Status", "Priority", "Created", "Queue", "Creator", "Owner", "InitialPriority", "FinalPriority"]
    search_context = []
    data = raw_tickets.text.split("\n")
    data = data[2:]
    for line in data:
        split_line = line.split(": ")
        search_ticket = get_ticket_request(split_line[0]).text
        search_ticket = search_ticket.split("\n")
        search_ticket = search_ticket[2:]
        id_ticket = search_ticket[0].upper()
        search_ticket[0] = id_ticket

        current_ticket_search = build_ticket(search_ticket)

        for key in search_ticket:  # Adding ticket custom fields to outputs
            if key.startswith("CF."):
                split_key = key.split(":")
                if split_key[0]:
                    custom_field_regex = re.findall(CURLY_BRACKETS_REGEX, key)[0].replace(
                        " ", ""
                    )  # Regex and removing white spaces
                    current_ticket_search[custom_field_regex] = split_key[1]
                    headers.append(custom_field_regex)

        if current_ticket_search:
            search_context.append(current_ticket_search)

    return search_context


""" FUNCTIONS """


def test_module():
    res = http_request("GET", "")
    if "401 Credentials required" in res.text:
        raise DemistoException("Error: Test failed. please check your credentials.")
    elif "200 Ok" in res.text:
        return_results("ok")


def create_ticket_request(encoded):
    suffix_url = "ticket/new"
    ticket_id = http_request("POST", suffix_url, data=encoded)
    return ticket_id


def edit_links(ticket_id, member_of, members, depends_on, depended_on_by, refers_to, referred_to_by):
    content = ""
    if member_of:
        content = f"MemberOf: {member_of}\n"
    if members:
        content += f"Members: {members}\n"
    if depends_on:
        content += f"DependsOn: {depends_on}\n"
    if depended_on_by:
        content += f"DependedOnBy: {depended_on_by}\n"
    if refers_to:
        content += f"RefersTo: {refers_to}\n"
    if referred_to_by:
        content += f"ReferredToBy: {referred_to_by}\n"

    data = f"content={urllib.parse.quote_plus(content)}"

    suffix_url = f"ticket/{ticket_id}/links"
    return http_request("POST", suffix_url, data=data)


def create_ticket_attachments_request(encoded, files_data):
    suffix_url = "ticket/new"
    ticket_id = http_request("POST", suffix_url, files=files_data)

    return ticket_id


def create_ticket():
    args = dict(demisto.args())
    args = {arg: value for arg, value in args.items() if isinstance(value, str)}
    queue = args.get("queue")
    data = f"id: ticket/new\nQueue: {queue}\n"

    subject = args.get("subject")
    if subject:
        data += f"Subject: {subject}\n"

    requestor = args.get("requestor")
    if requestor:
        data += f"Requestor: {requestor}\n"

    cc = args.get("cc", "")
    if cc:
        data += f"Cc: {cc}\n"

    admin_cc = args.get("admin-cc", "")
    if admin_cc:
        data += f"AdminCc: {admin_cc}\n"

    owner = args.get("owner")
    if owner:
        data += f"Owner: {owner}\n"

    status = args.get("status")
    if status:
        data += f"Status: {status}\n"

    priority = args.get("priority")
    if priority:
        data += f"Priority: {priority}\n"

    initial_priority = args.get("initial-priority")
    if initial_priority:
        data += f"Initial-priority: {initial_priority}\n"

    final_priority = args.get("final-priority")
    if final_priority:
        data += f"FinalPriority: {final_priority}\n"

    text = args.get("text")
    if text:
        data += f"Text: {text}\n"

    customfields = args.get("customfields")
    if customfields:
        cf_list = customfields.split(",")
        for cf in cf_list:
            equal_index = cf.index("=")
            key = f"CF-{cf[:equal_index]}: "
            value = cf[equal_index + 1 :]
            data = data + key + value + "\n"

    attachments = args.get("attachment", "")
    files_data = {}
    if attachments:
        if isinstance(attachments, list):  # Given as list
            attachments_list = attachments
        else:  # Given as string
            attachments_list = attachments.split(",")
        for i, file_pair in enumerate(attachments_list):
            file = demisto.getFilePath(file_pair)
            file_name = file["name"]
            files_data[f"attachment_{i + 1:d}"] = (file_name, open(file["path"], "rb"))
            data += f"Attachment: {file_name}"

    encoded = "content=" + urllib.parse.quote_plus(data)
    if attachments:
        files_data.update({"content": (None, data)})  # type: ignore
        raw_ticket_res = create_ticket_attachments_request(encoded, files_data)
    else:
        raw_ticket_res = create_ticket_request(encoded)

    if "409 Syntax Error" in raw_ticket_res.text:
        raise DemistoException(f"Ticket creation failed. Response: {raw_ticket_res.text}")

    ticket_id = re.findall(r"\d+", raw_ticket_res.text)[-1]
    demisto.debug(f"got ticket with id: {ticket_id}")
    if ticket_id == -1:
        raise DemistoException("Ticket creation failed")

    member_of = args.get("member-of")
    members = args.get("members")
    depends_on = args.get("depends-on")
    depended_on_by = args.get("depended-on-by")
    refers_to = args.get("refers-to")
    referred_to_by = args.get("referred-to-by")

    if members or member_of or depends_on or depended_on_by or refers_to or referred_to_by:
        edit_links(ticket_id, member_of, members, depends_on, depended_on_by, refers_to, referred_to_by)

    ticket_context = {
        "ID": ticket_id,
        "Subject": subject,
        "Creator": requestor,
        "InitialPriority": initial_priority,
        "Priority": priority,
        "FinalPriority": final_priority,
        "Owner": owner,
    }
    ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": ticket_context}
    hr = f"Ticket {ticket_id} was created successfully."
    demisto.results(
        {
            "Type": entryTypes["note"],
            "Contents": raw_ticket_res.text,
            "ContentsFormat": formats["text"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": hr,
            "EntryContext": ec,
        }
    )


def get_ticket_request(ticket_id):
    suffix_url = f"ticket/{ticket_id}/show"
    return http_request("GET", suffix_url)


def fix_query_suffix(query):
    new_query = query
    if new_query.endswith("+AND+"):
        new_query = new_query[:-5]
    elif new_query.endswith("+OR+"):
        new_query = new_query[:-4]
    return new_query


def build_search_query():
    raw_query = ""
    args = dict(demisto.args())
    args = {arg: value for arg, value in args.items() if isinstance(value, str)}
    ticket_id = args.get("ticket-id")
    if ticket_id:
        raw_query += f"id={apostrophe}{ticket_id}{apostrophe}+AND+"

    subject = args.get("subject")
    if subject:
        raw_query += f"Subject={apostrophe}{subject}{apostrophe}+AND+"

    status = args.get("status")
    if status:
        raw_query += f"Status={apostrophe}{status}{apostrophe}+AND+"

    creator = args.get("creator")
    if creator:
        raw_query += f"Creator={apostrophe}{creator}{apostrophe}+AND+"

    priority_equal_to = args.get("priority-equal-to")
    if priority_equal_to:
        raw_query += f"Priority={apostrophe}{priority_equal_to}{apostrophe}+AND+"

    priority_greater_than = args.get("priority-greater-than")
    if priority_greater_than:
        raw_query += f"Priority>{apostrophe}{priority_greater_than}{apostrophe}+AND+"

    created_after = args.get("created-after")
    if created_after:
        raw_query += f"Created>{apostrophe}{created_after}{apostrophe}+AND+"

    created_on = args.get("created-on")
    if created_on:
        raw_query += f"Created={apostrophe}{created_on}{apostrophe}+AND+"

    created_before = args.get("created-before")
    if created_before:
        raw_query += f"Created<{apostrophe}{created_before}{apostrophe}+AND+"

    owner = args.get("owner")
    if owner:
        raw_query += f"Created={apostrophe}{owner}{apostrophe}+AND+"

    due = args.get("due")
    if due:
        raw_query += f"Due={apostrophe}{due}{apostrophe}+AND+"

    queue = args.get("queue")
    if queue:
        raw_query += f"Queue={apostrophe}{queue}{apostrophe}+AND+"
    raw_query = fix_query_suffix(raw_query)
    return raw_query


def build_ticket(rtir_search_ticket):
    current_ticket_search = {}
    for entity in rtir_search_ticket:
        if ": " in entity:
            header, content = entity.split(": ", 1)
            if header == "ID":
                content = ticket_string_to_id(content)
            if header in {
                "ID",
                "Subject",
                "Status",
                "Priority",
                "Created",
                "Queue",
                "Creator",
                "Owner",
                "InitialPriority",
                "FinalPriority",
            }:
                current_ticket_search[header] = content
    return current_ticket_search


def search_ticket():
    raw_query = build_search_query()

    raw_tickets = search_ticket_request(raw_query)
    headers = ["ID", "Subject", "Status", "Priority", "Created", "Queue", "Creator", "Owner", "InitialPriority", "FinalPriority"]
    search_context = []
    data = raw_tickets.text.split("\n")
    data = data[2:]
    results_limit = int(demisto.args().get("results_limit", 0))
    data = data if (results_limit == 0) else data[:results_limit]
    for line in data:
        split_line = line.split(": ")
        empty_line_response = ["NO OBJECTS SPECIFIED.", ""]
        is_line_non_empty = split_line[0] != ""
        if is_line_non_empty:
            search_ticket = get_ticket_request(split_line[0]).text
            search_ticket = search_ticket.split("\n")
            search_ticket = search_ticket[2:]
            id_ticket = search_ticket[0].upper()
            search_ticket[0] = id_ticket
        else:
            search_ticket = empty_line_response

        current_ticket_search = build_ticket(search_ticket)

        for key in search_ticket:  # Adding ticket custom fields to outputs
            if key.startswith("CF."):
                split_key = key.split(":")
                if split_key[0]:
                    custom_field_regex = re.findall(CURLY_BRACKETS_REGEX, key)[0].replace(
                        " ", ""
                    )  # Regex and removing white spaces
                    current_ticket_search[custom_field_regex] = split_key[1]
                    headers.append(custom_field_regex)

        if current_ticket_search:
            search_context.append(current_ticket_search)
    if search_context:
        ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": search_context}
        title = "RTIR ticket search results"

        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": search_context,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": tableToMarkdown(title, search_context, headers, removeNull=True),
                "EntryContext": ec,
            }
        )
    else:
        demisto.results("No results found.")


def search_ticket_request(raw_query):
    suffix_url = "search/ticket"
    raw_tickets = http_request("GET", suffix_url, query={"query": raw_query})

    return raw_tickets


def close_ticket_request(ticket_id, encoded):
    suffix_url = f"ticket/{ticket_id}/edit"
    closed_ticket = http_request("POST", suffix_url, data=encoded)

    return closed_ticket


def close_ticket():
    ticket_id = demisto.args().get("ticket-id")
    content = "\nStatus: resolved"
    encoded = "content=" + urllib.parse.quote_plus(content)
    closed_ticket = close_ticket_request(ticket_id, encoded)
    if "200 Ok" in closed_ticket.text:
        ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": {"ID": int(ticket_id), "State": "resolved"}}
        hr = f"Ticket {ticket_id} was resolved successfully."
        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": hr,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": hr,
                "EntryContext": ec,
            }
        )
    else:
        raise DemistoException("Failed to resolve ticket")


def edit_ticket_request(ticket_id, encoded):
    suffix_url = f"ticket/{ticket_id}/edit"
    return http_request("POST", suffix_url, data=encoded)


def edit_ticket():
    args = demisto.args()
    arguments_given = False
    ticket_id = args.get("ticket-id")
    content = "ID: " + ticket_id
    kwargs = {}
    subject = args.get("subject")
    if subject:
        content += "\nSubject: " + subject
        arguments_given = True
        kwargs["Subject"] = subject

    owner = args.get("owner")
    if owner:
        content += "\nOwner: " + owner
        arguments_given = True
        kwargs["Owner"] = owner

    status = args.get("status")
    if status:
        content += "\nStatus: " + status
        arguments_given = True
        kwargs["Status"] = status

    priority = args.get("priority")
    if priority:
        content += "\nPriority: " + priority
        arguments_given = True
        kwargs["Priority"] = int(priority)

    final_priority = args.get("final-priority")
    if final_priority:
        content += "\nFinalPriority: " + final_priority
        arguments_given = True
        kwargs["FinalPriority"] = int(final_priority)

    due = args.get("due")
    if due:
        content += "\nDue: " + due
        arguments_given = True
        kwargs["Due"] = due

    customfields = args.get("customfields")
    if customfields:
        arguments_given = True
        cf_list = customfields.split(",")
        for cf in cf_list:
            equal_index = cf.index("=")
            key = f"CF-{cf[:equal_index]}: "
            value = cf[equal_index + 1 :]
            content += "\n" + key + value

    edit_succeeded = False
    member_of = args.get("member-of")
    members = args.get("members")
    depends_on = args.get("depends-on")
    depended_on_by = args.get("depended-on-by")
    refers_to = args.get("refers-to")
    referred_to_by = args.get("referred-to-by")

    if members or member_of or depends_on or depended_on_by or refers_to or referred_to_by:
        links = edit_links(ticket_id, member_of, members, depends_on, depended_on_by, refers_to, referred_to_by)
        if "200 Ok" in links.text:
            edit_succeeded = True

    if arguments_given:
        edited_ticket = edit_ticket_request(ticket_id, f"content={urllib.parse.quote_plus(content)}")
        if "200 Ok" in edited_ticket.text:
            edit_succeeded = True

    elif not members and not member_of:
        raise DemistoException("No arguments were given to edit the ticket.")

    if edit_succeeded:
        ticket_context = {
            "ID": ticket_id,
            "Subject": subject,
            "State": status,
            "Priority": priority,
            "FinalPriority": final_priority,
            "Owner": owner,
        }
        ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": ticket_context}

        hr = f"Ticket {ticket_id} was edited successfully."
        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": hr,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": hr,
                "EntryContext": ec,
            }
        )
    else:
        raise DemistoException("Failed to edit ticket")


def get_ticket_attachments(ticket_id):
    suffix_url = f"ticket/{ticket_id}/attachments"
    raw_attachments = http_request("GET", suffix_url).text

    attachments = []
    attachments_content = []
    attachments_list = parse_attachments_list(raw_attachments)
    for attachment_id, attachment_name, attachment_type, attachment_size in attachments_list:
        attachments.append({"ID": attachment_id, "Name": attachment_name, "Type": attachment_type, "Size": attachment_size})

        suffix_url = f"ticket/{ticket_id}/attachments/{attachment_id}"
        raw_attachment_content = http_request("GET", suffix_url).text
        attachment_content = parse_attachment_content(attachment_id, raw_attachment_content)
        attachments_content.append(fileResult(attachment_name, attachment_content))
    return attachments, attachments_content


def parse_attachments_list(raw_attachments):
    """
    Parses attachments details from raw attachments response.
    Example input:
        RT/4.4.2 200 Ok

        id: ticket/6325/attachments
        Attachments: 504: mimecast-get-remediation-incident.log (text/plain / 3.5k)
        505: mimecast-get-remediation-incident2.log (text/plain / 3.6k)

    Example output:
        [('504', 'mimecast-get-remediation-incident.log', 'text/plain', '3.5k'),
         ('505', 'mimecast-get-remediation-incident2.log', 'text/plain', '3.6k')]
    Args:
        raw_attachments: The raw attachments response
    Returns:
        A list of tuples containing the id, name, format and size of each attachment
    """
    attachments_regex = re.compile(r"(\d+): (.+) \((.+) \/ (.+)\)")
    attachments_list = attachments_regex.findall(raw_attachments)
    return attachments_list


def parse_attachment_content(attachment_id, raw_attachment_content):
    # type: (str, str) -> str
    """
    Parses raw attachment response into the attachment content
    Example input:
        From: root@localhost
        Subject: <ticket subject>
        X-RT-Interface: REST
        Content-Type: text/plain
        Content-Disposition: form-data;
        name="attachment_1";
        filename="mimecast-get-remediation-incident.log";
        filename="mimecast-get-remediation-incident.log"
        Content-Transfer-Encoding: binary
        Content-Length: <length of the content>

        Content: <the actual attachment content...>
    Example output:
        <the actual attachment content...>
    Args:
        attachment_id: The ID of the attachment
        raw_attachment_content: The raw attachment content, should be like the example input

    Returns:
        The actual content
    """
    attachment_content_pattern = re.compile(r"Content: (.*)", flags=re.DOTALL)
    attachment_content = attachment_content_pattern.findall(raw_attachment_content)
    if not attachment_content:
        raise DemistoException(f"Could not parse attachment content for attachment id {attachment_id}")
    return attachment_content[0]


def get_ticket_attachments_command():
    ticket_id = demisto.args().get("ticket-id")
    attachments, attachments_content = get_ticket_attachments(ticket_id)
    if attachments:
        ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": {"ID": int(ticket_id), "Attachment": attachments}}
        title = f"RTIR ticket {ticket_id} attachments"
        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": attachments,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": tableToMarkdown(title, attachments, removeNull=True),
                "EntryContext": ec,
            }
        )
        demisto.results(attachments_content)
    else:
        demisto.results("No attachments found.")


def get_ticket_history_by_id(ticket_id, history_id):
    """Accepts ticket ID and history ID as input and returns a dictionary of ticket history entry properties"""

    suffix_url = f"ticket/{ticket_id}/history/id/{history_id}"
    raw_history = http_request("GET", suffix_url)
    return parse_history_response(raw_history.text)


def parse_history_response(raw_history):
    # type: (str) -> dict
    """
    Parses raw history string into dict
    Example input:
        RT/4.4.2 200 Ok

        # 24/24 (id/80/total)

        id: 80
        Ticket: 5
        TimeTaken: 0
        Type: Create
        Field:
        OldValue:
        NewValue: some new value
        Data:
        Description: Ticket created by root
        Content: Some
        Multi line
        Content
        Creator: root
        Created: 2018-07-09 11:25:59
        Attachments:
    Example output:
        {'ID': '80',
         'Ticket': '5',
         'TimeTaken': '0',
         'Type': 'Create',
         'Field': '',
         'OldValue': '',
         'NewValue': 'some new value',
         'Data': '',
         'Description': 'Ticket created by root',
         'Content': 'Some\nMulti line\nContent',
         'Creator': 'root',
         'Created': '2018-07-09 11:25:59',
         'Attachments': ''}

    Args:
        raw_history: The raw ticket history string response

    Returns:
        A pasred dict with keys and values
    """
    keys = re.findall(r"^([a-z|A-Z]+):", raw_history, flags=re.MULTILINE)
    values = re.split(r"\n[a-z|A-Z]+:", raw_history)[1:]
    if len(keys) != len(values):
        return {}
    current_history_context = {key.upper() if key == "id" else key: value.strip() for key, value in zip(keys, values)}
    return current_history_context


def get_ticket_history(ticket_id):
    suffix_url = f"ticket/{ticket_id}/history"
    raw_history = http_request("GET", suffix_url)
    history_context = []
    headers = ["ID", "Created", "Creator", "Description", "Content"]
    data = raw_history.text.split("\n")
    data = data[4:]
    for line in data:
        history_id = line.split(": ")[0]
        if not history_id:
            continue
        history_response = get_ticket_history_by_id(ticket_id, history_id)
        history_context.append(history_response)
    return history_context, headers


def get_ticket_history_command():
    ticket_id = demisto.args().get("ticket-id")
    history_context, headers = get_ticket_history(ticket_id)
    if history_context:
        ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": {"ID": int(ticket_id), "History": history_context}}
        title = f"RTIR ticket {ticket_id} history"
        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": history_context,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": tableToMarkdown(title, history_context, headers, removeNull=True),
                "EntryContext": ec,
            }
        )
    else:
        demisto.results("No results found.")


def get_ticket():
    ticket_id = demisto.args().get("ticket-id")
    raw_ticket = get_ticket_request(ticket_id)
    if not raw_ticket or f"Ticket {ticket_id} does not exist" in raw_ticket.text:
        raise DemistoException("Failed to get ticket, possibly does not exist.")
    ticket_context = []
    data_list = raw_ticket.text
    data = data_list.split("\n")
    data = data[2:]
    current_ticket = {}
    for line in data:
        split_line = line.split(": ")
        if len(split_line) == 2:
            current_ticket[split_line[0]] = split_line[1]
    ticket = {
        "ID": ticket_string_to_id(current_ticket["id"]),
        "Subject": current_ticket.get("Subject"),
        "State": current_ticket.get("Status"),
        "Creator": current_ticket.get("Creator"),
        "Created": current_ticket.get("Created"),
        "Priority": current_ticket.get("Priority"),
        "InitialPriority": current_ticket.get("InitialPriority"),
        "FinalPriority": current_ticket.get("FinalPriority"),
        "Queue": current_ticket.get("Queue"),
        "Owner": current_ticket.get("Owner"),
    }

    for key in data:  # Adding ticket custom fields to outputs
        if key.startswith("CF."):
            split_key = key.split(":")
            if split_key[0]:
                custom_field_regex = re.findall(CURLY_BRACKETS_REGEX, key)[0].replace(" ", "")  # Regex and removing white spaces
                ticket[custom_field_regex] = split_key[1]

    suffix_url = f"ticket/{ticket_id}/links/show"
    raw_links = http_request("GET", suffix_url)
    links = parse_ticket_links(raw_links.text)
    ticket["LinkedTo"] = links

    ticket_context.append(ticket)
    ec = {"RTIR.Ticket(val.ID && val.ID === obj.ID)": ticket}
    title = f"RTIR ticket {ticket_id}"
    headers = [
        "ID",
        "Subject",
        "Status",
        "Priority",
        "Created",
        "Queue",
        "Creator",
        "Owner",
        "InitialPriority",
        "FinalPriority",
        "LinkedTo",
    ]
    demisto.results(
        {
            "Type": entryTypes["note"],
            "Contents": ticket_context,
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": tableToMarkdown(title, ticket, headers, removeNull=True),
            "EntryContext": ec,
        }
    )


def parse_ticket_links(raw_links):
    # type: (str) -> list
    """
    Parses the link IDs from the ticket link response
    An example to an expected 'raw_links' is:
    "RT/4.4.4 200 Ok

    id: ticket/68315/links

    Members: some-url.com/ticket/65461,
             some-url.com/ticket/65462,
             some-url.com/ticket/65463"

    For 'raw_links' as descripbed above- the output will be [{'ID': '65461'}, {'ID': '65462'}, {'ID': '65463'}]
    Args:
        raw_links: The raw links string response

    Returns:
        A list of parsed IDs
    """
    links = [{"ID": link} for link in re.findall(r"/ticket/(\d+)", raw_links)] if raw_links else []
    return links


def add_comment_request(ticket_id, encoded):
    suffix_url = f"ticket/{ticket_id}/comment"
    added_comment = http_request("POST", suffix_url, data=encoded)

    return added_comment


def add_comment_attachment(ticket_id, encoded, files_data):
    suffix_url = f"ticket/{ticket_id}/comment"
    comment = http_request("POST", suffix_url, files=files_data)

    return comment.text


def add_comment():
    ticket_id = demisto.args().get("ticket-id")
    content = "Action: comment\n"
    if text := demisto.args().get("text"):
        content += f"\nText: {text}"
    attachments = demisto.args().get("attachment", "")
    files_data = {}
    if attachments:
        if isinstance(attachments, list):
            attachments_list = attachments
        else:  # Given as string
            attachments_list = attachments.split(",")
        for i, file_pair in enumerate(attachments_list):
            file = demisto.getFilePath(file_pair)
            file_name = file["name"]
            files_data[f"attachment_{i + 1:d}"] = (file_name, open(file["path"], "rb"))
            content += f"Attachment: {file_name}\n"

    encoded = f"content={urllib.parse.quote_plus(content)}"
    if attachments:
        files_data.update({"content": (None, content)})  # type: ignore
        comment = add_comment_attachment(ticket_id, encoded, files_data)
        return_outputs(f"Added comment to ticket {ticket_id} successfully.", {}, comment)
    else:
        added_comment = add_comment_request(ticket_id, encoded)
        if "200" in added_comment.text:
            demisto.results(f"Added comment to ticket {ticket_id} successfully.")
        else:
            raise DemistoException("Failed to add comment")


def add_reply_request(ticket_id, encoded):
    suffix_url = f"ticket/{ticket_id}/comment"
    added_reply = http_request("POST", suffix_url, data=encoded)

    return added_reply


def add_reply():
    ticket_id = demisto.args().get("ticket-id")
    content = "Action: correspond\n"
    if text := demisto.args().get("text"):
        content += f"\nText: {text}"
    if cc := demisto.args().get("cc"):
        content += f"\nCc: {cc}"
    try:
        encoded = "content=" + urllib.parse.quote_plus(content)
        added_reply = add_reply_request(ticket_id, encoded)
        if "200" in added_reply.text:
            demisto.results(f"Replied successfully to ticket {ticket_id}.")
        else:
            raise DemistoException("Failed to reply")
    except Exception as e:
        demisto.error(str(e))
        raise DemistoException("Failed to reply")


def get_ticket_id(ticket):
    return int(ticket["ID"])


def fetch_incidents():
    last_run = demisto.getLastRun()
    last_ticket_id = last_run["ticket_id"] if (last_run and last_run["ticket_id"]) else 0
    raw_query = f"id>{last_ticket_id}+AND+Priority>{FETCH_PRIORITY}+AND+Queue={apostrophe}{FETCH_QUEUE}{apostrophe}"
    if FETCH_STATUS:
        status_list = FETCH_STATUS.split(",")
        status_query = "+AND+("
        for status in status_list:
            status_query += f"Status={apostrophe}{status}{apostrophe}+OR+"
        status_query = fix_query_suffix(status_query)
        raw_query += status_query + ")"
    tickets = parse_ticket_data(raw_query)
    tickets.sort(key=get_ticket_id)
    fetch_batch_limit = int(demisto.params().get("fetch_limit", 0))
    tickets = tickets if (fetch_batch_limit == 0) else tickets[:fetch_batch_limit]
    incidents = []
    max_ticket_id = last_ticket_id
    for ticket in tickets:
        ticket_id = ticket["ID"]
        history_context, _ = get_ticket_history(ticket_id)
        ticket["History"] = history_context
        incidents.append(ticket_to_incident(ticket))
        max_ticket_id = max(max_ticket_id, ticket_id)
    if tickets:
        demisto.setLastRun({"ticket_id": max_ticket_id})
    demisto.incidents(incidents)


""" EXECUTION CODE """


def main():
    handle_proxy()

    params = demisto.params()
    command = demisto.command()

    try:
        """ GLOBAL VARS """
        global \
            SERVER, \
            USERNAME, \
            PASSWORD, \
            BASE_URL, \
            USE_SSL, \
            FETCH_PRIORITY, \
            FETCH_STATUS, \
            FETCH_QUEUE, \
            HEADERS, \
            REFERER, \
            TOKEN, \
            CERTIFICATE, \
            PRIVATE_KEY
        SERVER = params.get("server", "")[:-1] if params.get("server", "").endswith("/") else params.get("server", "")
        USERNAME = params.get("credentials", {}).get("identifier", "")
        PASSWORD = params.get("credentials", {}).get("password", "")
        TOKEN = params.get("token", {}).get("password", "")
        if not (USERNAME and PASSWORD) and not TOKEN:
            raise DemistoException("Username and password or Token must be provided.")
        CERTIFICATE = replace_spaces_in_credential(params.get("certificate", {}).get("identifier"))
        PRIVATE_KEY = replace_spaces_in_credential(params.get("certificate", {}).get("password"))
        BASE_URL = urljoin(SERVER, "/REST/1.0/")
        USE_SSL = not params.get("unsecure", False)
        FETCH_PRIORITY = int(params.get("fetch_priority", "0")) - 1
        FETCH_STATUS = params.get("fetch_status")
        FETCH_QUEUE = params.get("fetch_queue")
        REFERER = params.get("referer")
        HEADERS = {"Authorization": f"token {TOKEN}"} if TOKEN else {}
        HEADERS |= {"Referer": REFERER} if REFERER else {}

        demisto.debug(f"Command being called is {command}")
        if command == "test-module":
            test_module()

        if command in {"fetch-incidents"}:
            fetch_incidents()

        elif command == "rtir-create-ticket":
            create_ticket()

        elif command == "rtir-search-ticket":
            search_ticket()

        elif command == "rtir-resolve-ticket":
            close_ticket()

        elif command == "rtir-edit-ticket":
            edit_ticket()

        elif command == "rtir-ticket-history":
            get_ticket_history_command()

        elif command == "rtir-ticket-attachments":
            get_ticket_attachments_command()

        elif command == "rtir-get-ticket":
            get_ticket()

        elif command == "rtir-add-comment":
            add_comment()

        elif command == "rtir-add-reply":
            add_reply()

    except Exception as e:
        return_error(f"Failed to execute {command} command. Error: {e!s}")


if __name__ in ("__builtin__", "builtins", "__main__"):
    main()