-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzammad.py
More file actions
169 lines (131 loc) · 5.1 KB
/
zammad.py
File metadata and controls
169 lines (131 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
from datetime import datetime
from django.conf import settings
from core.models import Webhook
from pydantic import BaseModel
class ZammadConfig:
url = settings.ZAMMAD_URL # servicedesk.europython.eu
billing_group = settings.ZAMMAD_GROUP_BILLING
refunds_group = settings.ZAMMAD_GROUP_REFUNDS
helpdesk_group = settings.ZAMMAD_GROUP_HELPDESK
programme_group = settings.ZAMMAD_GROUP_PROGRAMME
finaid_group = settings.ZAMMAD_GROUP_FINAID
sponsors_group = settings.ZAMMAD_GROUP_SPONSORS
grants_group = settings.ZAMMAD_GROUP_GRANTS
volunteers_group = settings.ZAMMAD_GROUP_VOLUNTEERS
class ZammadGroup(BaseModel):
id: int
name: str
class ZammadUser(BaseModel):
firstname: str
lastname: str
class ZammadTicket(BaseModel):
id: int
group: ZammadGroup
title: str
owner: ZammadUser
state: str
number: str
customer: ZammadUser
created_at: datetime
updated_at: datetime
updated_by: ZammadUser
article_ids: list[int]
class ZammadArticle(BaseModel):
sender: str
internal: bool
ticket_id: int
created_at: datetime
created_by: ZammadUser
subject: str
class ZammadWebhook(BaseModel):
ticket: ZammadTicket
article: ZammadArticle | None
JsonType = dict[str, str | int | float | list | dict]
class ZammadParser:
class Actions:
new_ticket_created = "new_ticket_created"
new_message_in_thread = "new_message_in_thread"
replied_in_thread = "replied_in_thread"
new_internal_note = "new_internal_note"
updated_ticket = "updated_ticket"
def __init__(self, content: JsonType):
self.content = content
# Ticket is always there, article is optional
# Example: change of status of the Ticket doesn't contain article
self.ticket = ZammadTicket.model_validate(self.content["ticket"])
self.article = (
ZammadArticle.model_validate(self.content["article"])
if self.content["article"]
else None
)
@property
def action(self):
"""
Zammad doesn't give us an action inside the webhook, so we can either
set custom triggers and URLs for every action, or we can try to infer
the action from the content of the webhook. For simplicity of the
overall setup, we are implementing the latter here.
"New Ticket created"? -- has article, and len(article_ids) == 1
-- state change will not have article associated with it.
"New message in the thread" -- article, sender==Customer
"We sent a new reply in the thread" -- article, sender==Agent
"New internal note in the thread" -- article, internal==true
"Updated the ticket ...", -- updated_by.firstname
"""
# Implementing this as cascading if statements here is part of the
# assumptions.
# For example the "sender == Customer" is going to be True also for their
# first message that originally creates the ticket. However first time
# we get a message, we will return "New ticket" and second time "New
# message in the thread".
if self.article:
if len(self.ticket.article_ids) == 1:
# This means we have an article, and it's a first one, therefore a
# ticket is new.
return self.Actions.new_ticket_created
elif self.article.internal is True:
return self.Actions.new_internal_note
elif self.article.sender == "Customer":
return self.Actions.new_message_in_thread
elif self.article.sender == "Agent":
return self.Actions.replied_in_thread
elif not self.article:
return self.Actions.updated_ticket
raise ValueError("Unsupported scenario")
@property
def updated_by(self):
return self.ticket.updated_by.firstname
@property
def group(self):
return self.ticket.group.name
@property
def url(self):
return f"https://{ZammadConfig.url}/#ticket/zoom/{self.ticket.id}"
def to_discord_message(self):
message = "{group}: {sender} {action} {details}".format
# Action
actions = {
self.Actions.new_ticket_created: "created new ticket",
self.Actions.new_message_in_thread: "sent a new message",
self.Actions.replied_in_thread: "replied to a ticket",
self.Actions.new_internal_note: "created internal note",
self.Actions.updated_ticket: "updated ticket",
}
action = actions[self.action]
return message(
group=self.group, sender=self.updated_by, action=action, details=self.url
)
def meta(self):
return {
"group": self.group,
"sender": self.updated_by,
"action": self.action,
"message": self.to_discord_message(),
}
def prep_zammad_webhook(wh: Webhook):
"""Parse and store some information for later"""
zp = ZammadParser(wh.content)
wh.event = zp.action
wh.extra = zp.meta()
wh.save()
return wh