2023-02-13 12:41:30 +00:00
|
|
|
/* eslint-disable camelcase,@typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any */
|
|
|
|
|
import querystring from "querystring";
|
|
|
|
|
import Wreck from "@hapi/wreck";
|
|
|
|
|
|
|
|
|
|
export interface User {
|
|
|
|
|
id: number;
|
|
|
|
|
firstname?: string;
|
|
|
|
|
lastname?: string;
|
|
|
|
|
email?: string;
|
|
|
|
|
phone?: string;
|
|
|
|
|
}
|
|
|
|
|
export interface Ticket {
|
|
|
|
|
id: number;
|
|
|
|
|
title?: string;
|
|
|
|
|
group_id?: number;
|
|
|
|
|
customer_id?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ZammadClient {
|
|
|
|
|
ticket: {
|
|
|
|
|
create: (data: any) => Promise<Ticket>;
|
2025-10-27 21:02:19 +01:00
|
|
|
update: (id: number, data: any) => Promise<Ticket>;
|
2023-02-13 12:41:30 +00:00
|
|
|
};
|
|
|
|
|
user: {
|
|
|
|
|
search: (data: any) => Promise<User[]>;
|
|
|
|
|
create: (data: any) => Promise<User>;
|
|
|
|
|
};
|
2025-10-27 21:02:19 +01:00
|
|
|
get: (path: string) => Promise<any>;
|
2023-02-13 12:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type ZammadCredentials =
|
|
|
|
|
| { username: string; password: string }
|
|
|
|
|
| { token: string };
|
|
|
|
|
|
|
|
|
|
export interface ZammadClientOpts {
|
|
|
|
|
headers?: Record<string, any>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const formatAuth = (credentials: any) => {
|
|
|
|
|
if (credentials.username) {
|
|
|
|
|
return (
|
|
|
|
|
"Basic " +
|
|
|
|
|
Buffer.from(`${credentials.username}:${credentials.password}`).toString(
|
|
|
|
|
"base64"
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (credentials.token) {
|
|
|
|
|
return `Token ${credentials.token}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error("invalid zammad credentials type");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const Zammad = (
|
|
|
|
|
credentials: ZammadCredentials,
|
|
|
|
|
host: string,
|
|
|
|
|
opts?: ZammadClientOpts
|
|
|
|
|
): ZammadClient => {
|
|
|
|
|
const extraHeaders = (opts && opts.headers) || {};
|
|
|
|
|
|
|
|
|
|
const wreck = Wreck.defaults({
|
|
|
|
|
baseUrl: `${host}/api/v1/`,
|
|
|
|
|
headers: {
|
|
|
|
|
authorization: formatAuth(credentials),
|
|
|
|
|
...extraHeaders,
|
|
|
|
|
},
|
|
|
|
|
json: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
ticket: {
|
|
|
|
|
create: async (payload) => {
|
|
|
|
|
const { payload: result } = await wreck.post("tickets", { payload });
|
|
|
|
|
return result as Ticket;
|
|
|
|
|
},
|
2025-10-27 21:02:19 +01:00
|
|
|
update: async (id, payload) => {
|
|
|
|
|
const { payload: result } = await wreck.put(`tickets/${id}`, { payload });
|
|
|
|
|
return result as Ticket;
|
|
|
|
|
},
|
2023-02-13 12:41:30 +00:00
|
|
|
},
|
|
|
|
|
user: {
|
|
|
|
|
search: async (query) => {
|
|
|
|
|
const qp = querystring.stringify({ query });
|
|
|
|
|
const { payload: result } = await wreck.get(`users/search?${qp}`);
|
|
|
|
|
return result as User[];
|
|
|
|
|
},
|
|
|
|
|
create: async (payload) => {
|
|
|
|
|
const { payload: result } = await wreck.post("users", { payload });
|
|
|
|
|
return result as User;
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-10-27 21:02:19 +01:00
|
|
|
get: async (path) => {
|
|
|
|
|
const { payload: result } = await wreck.get(path);
|
|
|
|
|
return result;
|
|
|
|
|
},
|
2023-02-13 12:41:30 +00:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getUser = async (zammad: ZammadClient, phoneNumber: string) => {
|
|
|
|
|
const mungedNumber = phoneNumber.replace("+", "");
|
|
|
|
|
const results = await zammad.user.search(`phone:${mungedNumber}`);
|
|
|
|
|
if (results.length > 0) return results[0];
|
|
|
|
|
return undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getOrCreateUser = async (zammad: ZammadClient, phoneNumber: string) => {
|
|
|
|
|
const customer = await getUser(zammad, phoneNumber);
|
|
|
|
|
if (customer) return customer;
|
|
|
|
|
|
|
|
|
|
return zammad.user.create({
|
|
|
|
|
phone: phoneNumber,
|
|
|
|
|
note: "User created by Grabadora from incoming voice call",
|
|
|
|
|
});
|
|
|
|
|
};
|