Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zendesk to replit #52

Merged
merged 8 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions run.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
(python -m wandbot.apps.slack -l en) & \
(python -m wandbot.apps.slack -l ja) & \
(python -m wandbot.apps.discord)
(python -m wandbot.apps.zendesk)
85 changes: 53 additions & 32 deletions src/wandbot/apps/zendesk/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from wandbot.api.client import AsyncAPIClient
from wandbot.apps.zendesk.config import ZendeskAppConfig
from wandbot.utils import get_logger
from wandbot.apps.zendesk.extract_by_type import *


logger = get_logger(__name__)
config = ZendeskAppConfig()
Expand All @@ -51,12 +53,14 @@ def extract_question(ticket: Ticket) -> str:
"""

description = ticket.description
# Convert the description to lower case and replace newline and carriage return characters with a space
question = description.lower().replace("\n", " ").replace("\r", "")
# Remove the string "[discourse post]" from the description
question = question.replace("[discourse post]", "")
# Truncate the description to a maximum length of 4095 characters
question = question[:4095]
if "forum" in ticket.tags:
return discourseExt(description)

elif "zopim_offline_message" in ticket.tags:
return offlineMessageExt(description)

elif "add_cc_note" in ticket.tags:
return emailMsgExt(description)

return question

Expand Down Expand Up @@ -105,12 +109,12 @@ def __init__(self) -> None:
initialized with the WandBot API URL from the configuration.
"""

user_creds = {
self.user_creds = {
"email": config.ZENDESK_EMAIL,
"password": config.ZENDESK_PASSWORD,
"subdomain": config.ZENDESK_SUBDOMAIN,
}
self.zenpy_client = Zenpy(**user_creds)
self.zenpy_client = Zenpy(**self.user_creds)
self.api_client = AsyncAPIClient(url=config.WANDBOT_API_URL)

def create_new_ticket(self, question_text: str) -> None:
Expand All @@ -127,6 +131,24 @@ def create_new_ticket(self, question_text: str) -> None:
None
"""

self.zenpy_client.tickets.create(
ArtsiomWB marked this conversation as resolved.
Show resolved Hide resolved
Ticket(
subject="WandbotTest4",
description=question_text,
status="new",
priority="low",
tags=["botTest", "zopim_offline_message"],
)
)
self.zenpy_client.tickets.create(
Ticket(
subject="WandbotTest4",
description=question_text,
status="new",
priority="low",
tags=["botTest", "zopim_offline_message"],
)
)
self.zenpy_client.tickets.create(
Ticket(
subject="WandbotTest4",
Expand All @@ -141,29 +163,21 @@ def fetch_new_tickets(self) -> List[Ticket]:
"""Fetches new tickets from the Zendesk system.

This method uses the Zenpy client to fetch new tickets from the Zendesk system. It filters the fetched
tickets based on specific requirements. The tickets are filtered if they have the tag "forum" and do not have
the tag "answered_by_bot".
tickets based on specific requirements. The tickets are filtered if they have the tags "forum", "zopim_offline_message" and do not have
the tag "answered_by_bot", "zopim_chat", "picked_up_by_bot".

Returns:
list: A list of filtered tickets that are new and have not been answered by the bot.
"""

exclude_tags = ["answered_by_bot", "zopim_chat", "picked_up_by_bot"]
new_tickets = self.zenpy_client.search(
type="ticket", status="new", group_id=config.ZDGROUPID
type="ticket",
status="new",
tags=["forum", "zopim_offline_message"],
minus=["tags:{}".format(tag) for tag in exclude_tags],
)
# Filtering based on specific requirements
filtered_tickets = [
ticket for ticket in new_tickets if "forum" in ticket.tags
]
# filtered_tickets = [ticket for ticket in new_tickets if 'bottest' in ticket.tags]
# for testing purposes only
filtered_tickets_not_answered = [
ticket
for ticket in filtered_tickets
if "answered_by_bot" not in ticket.tags
] # for testing purposes only

return filtered_tickets_not_answered
return new_tickets

async def generate_response(self, question: str) -> str:
"""Generates a response to a given question.
Expand Down Expand Up @@ -237,6 +251,8 @@ def gather_feedback(self, ticket: Ticket) -> None:
logger.error(f"Error: {e}")

async def run(self) -> None:
logger.info(f"WandBot + zendesk is running")

"""Runs the Zendesk AI Response System.

This method performs the following steps:
Expand All @@ -252,18 +268,20 @@ async def run(self) -> None:
"""

# Create a new ticket with a predefined question
self.create_new_ticket(
"Is there a way to programmatically list all projects for a given entity?"
)
# self.create_new_ticket(
ArtsiomWB marked this conversation as resolved.
Show resolved Hide resolved
# "Is there a way to programmatically list all projects for a given entity?"
# )

# Enter a loop where it fetches new tickets every 120 seconds
# Enter a loop where it fetches new tickets every 600 seconds
while True:
await asyncio.sleep(120)
await asyncio.sleep(600)

# Fetch new tickets
new_tickets = self.fetch_new_tickets()
logger.info(f"New unanswered Tickets: {len(new_tickets)}")
# restart the zenpy client because it times out after 3 minutes
self.zenpy_client = Zenpy(**self.user_creds)

# Fetch new tickets
new_tickets = list(self.fetch_new_tickets())
logger.info(f"New unanswered Zendesk tickets: {new_tickets}")
# For each new ticket, extract the question, generate a response, format the response,
# and update the ticket with the response
for ticket in new_tickets:
Expand All @@ -273,6 +291,9 @@ async def run(self) -> None:
formatted_response = format_response(response)
self.update_ticket(ticket, formatted_response)

if len(new_tickets) > 0:
logger.info(f"Done processing tickets: {len(new_tickets)}")


if __name__ == "__main__":
zd = ZendeskAIResponseSystem()
Expand Down
13 changes: 13 additions & 0 deletions src/wandbot/apps/zendesk/extract_by_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
def discourseExt(description) -> str:
ArtsiomWB marked this conversation as resolved.
Show resolved Hide resolved
question = description.lower().replace('\n', ' ').replace('\r', '')
question = question.replace('[discourse post]','')
question = question[:4095]
ArtsiomWB marked this conversation as resolved.
Show resolved Hide resolved
return question

def offlineMessageExt(description) -> str:
question = description.partition("Offline transcript:")[2]
return question

def emailMsgExt(description) -> str:
# its already clean but returning this for consistency
return description