diff --git a/zammad-duplicates-cleanup b/zammad-duplicates-cleanup new file mode 100755 index 0000000..89d0888 --- /dev/null +++ b/zammad-duplicates-cleanup @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +import base64 +import json +import os +import pickle +import sys +import html2text +import traceback +import time + +from zammad_py import ZammadAPI +from zammad_py.api import Resource, TagList, TicketArticle + +TEMPLATE = """{ +"zammad_url": "", +"zammad_user": "", +"zammad_password": "", +"rt_url": "", +"rt_user": "", +"rt_pass": "", +"rt_start": 1, +"rt_end": 1000, +} +""" + +COMMENT_TEMPLATE = """ +Ticket imported from Request Tracker (RT-#{numerical_id}) + +URL: https://support.ungleich.ch/Ticket/Display.html?id={numerical_id} +Created: {Created} +Resolved: {Resolved} +""" + +STATUSMAP = {"new": 1, "open": 2, "resolved": 4, "rejected": 4, "deleted": 4} +USERMAP = {} + +ZAMMAD_SESSIONS = {} + +### helpers ### + +def get_zammad_session(impersonated_user=None): + if impersonated_user in ZAMMAD_SESSIONS: + return ZAMMAD_SESSIONS[impersonated_user] + else: + kwargs = {} + if impersonated_user: + kwargs["on_behalf_of"] = impersonated_user + session = ZammadAPI( + url=config["zammad_host"], + username=config["zammad_user"], + password=config["zammad_password"], + **kwargs, + ) + ZAMMAD_SESSIONS[impersonated_user or config["zammad_user"]] = session + + return session + +def remove_duplicates_for(rt_id, zammad, retries=0): + try: + matching_zammad_tickets= zammad.ticket.search(f"title: \"\[RT-{rt_id}\]*\"") + matching_zammad_ids = [] + if len(matching_zammad_tickets) >= 2: + print(f"Found duplicates:") + for zt in matching_zammad_tickets: + print(f"{zt["id"]} {zt["title"]}") + matching_zammad_ids.append(zt["id"]) + + if len(matching_zammad_ids) >= 2: + matching_zammad_ids.sort() + matching_zammad_ids.pop() + for zt_id in matching_zammad_ids: + print(f"Deleting Zammad ticket {zt_id}") + zammad.ticket.destroy(zt_id) + + except: + print(f"Failed to process RT-{rt_id} .. ({retries} retries left)") + if retries > 0: + print("Sleeping 5 seconds to give Zammad some air...") + time.sleep(5) + remove_duplicates_for(id, retries - 1) + else: + traceback.print_exc() + raise RuntimeError + +### main logic ### + +if not os.path.exists("rt2zammad.json"): + print("Missing rt2zammad.json!") + print("Create one based on following template:") + print(TEMPLATE) + sys.exit(1) + +with open("rt2zammad.json") as handle: + config = json.load(handle) + +h2t = html2text.HTML2Text() +zammad = get_zammad_session() + +os.makedirs("tickets", exist_ok=True) + + +ticket_ids = os.listdir("tickets/") +print(f"Found {len(ticket_ids)} tickets on filesystem.") +ticket_ids = list(map(int, ticket_ids)) +ticket_ids.sort() + +for id in ticket_ids: + print(f"Processing RT-{id}...") + remove_duplicates_for(id, zammad)