Transforme a maneira como você lida com a tecnologia
A tecnologia deve ser uma aliada estratégica, impulsionando o crescimento do seu negócio. Na iQuest, oferecemos soluções que garantem eficiência contínua e segurança, permitindo que sua equipe se concentre em resultados enquanto cuidamos da infraestrutura técnica.
de Experiência
0 anos
Com uma trajetória de inovação e excelência, a iQuest transforma o suporte técnico em uma experiência descomplicada. Atendemos empresas que buscam otimizar operações, reduzir custos e eliminar preocupações com tecnologia. Nossa equipe especializada está pronta para responder rapidamente, desde a manutenção preventiva até a resolução de incidentes complexos.
Visão:
Ser referência em suporte técnico, antecipando necessidades e impulsionando a competitividade dos clientes.
Missão:
Oferecer suporte contínuo e proativo, garantindo que operações funcionem sem interrupções, com soluções preventivas e atendimento acessível.
Na iQuest, entendemos que cada empresa tem necessidades únicas. Por isso, oferecemos um portfólio completo de serviços que mantém sua TI sempre eficiente.
Monitoramento 24/7
Acompanhe o desempenho dos sistemas em tempo real e previna problemas antes que impactem suas operações.
Nosso processo é projetado para fornecer suporte eficiente e sem complicações, garantindo que sua empresa tenha tudo o que precisa para uma TI funcional.
Diagnóstico Rápido e Preciso
Identificação de problemas e avaliação de necessidades com agilidade.
Monitoramento Proativo
Acompanhamento contínuo para prevenir falhas.
Soluções Sob Medida
Ajuste de serviços conforme as especificidades de cada negócio.
Melhorias Contínuas
Atualizações e otimizações constantes para garantir eficiência.
A parceira ideal para empresas que buscam eficiência em TI.
vantagens de escolher a iquest
Por que escolher a iQuest?
Na iQuest, focamos em garantir segurança, eficiência e tranquilidade para nossos clientes, independentemente do tamanho do negócio.
Equipe Especializada
Profissionais experientes e atualizados, nas principais ferramentas de mercado.
Suporte 24/7
Suporte eficaz e humanizado.
Inovação e Atualização Constante
Investimento em tecnologia de ponta para otimizar sistemas.
Escolha a iQuest e leve sua tecnologia para o próximo nível com um suporte técnico que vai além das expectativas.
Depoimentos
O que nossos clientes dizem
A satisfação dos nossos clientes é o que nos motiva a continuar oferecendo suporte técnico de excelência. Veja o que alguns deles têm a dizer.
“A iQuest garante a continuidade das operações, permitindo que nossa equipe se concentre no crescimento dos negócios.”
“A iQuest superou nossas expectativas, sempre antecipando soluções para evitar problemas.”
“Com a iQuest, podemos escalar nossos projetos sabendo que estamos em boas mãos.”
// Widget de Chat CHANNEL
(function() {
// Cria o elemento do widget
const widgetHTML = `
WEBChat
`;
// Adiciona o widget ao documento
document.body.insertAdjacentHTML('beforeend', widgetHTML);
// Inicializa o widget
const chatButton = document.getElementById('channel-chat-button');
const chatContainer = document.getElementById('channel-chat-container');
const chatClose = document.getElementById('channel-chat-close');
const chatClear = document.getElementById('channel-chat-clear');
const messagesDiv = document.getElementById('channel-chat-messages');
const messageInput = document.getElementById('channel-chat-input');
const sendButton = document.getElementById('channel-chat-send');
const sessionSpan = document.getElementById('channel-chat-session');
const widgetDiv = document.getElementById('channel-chat-widget');
const fileInput = document.getElementById('channel-chat-file');
const attachButton = document.getElementById('channel-chat-attach');
// Variáveis globais para sessão e token
let webchatId = null;
let token = null;
let ws = null;
let chatLoaded = false;
const tenantId = '28';
// Para ativar logs do widget no console: window.WEBCHAT_DEBUG = true antes do load
const LOGGER_ENABLED = (typeof window !== 'undefined' && window.WEBCHAT_DEBUG === true);
// Funções de controle do widget
chatButton.addEventListener('click', async () => {
chatContainer.classList.add('show');
if (!chatLoaded) {
await loadMessageHistory();
connectWebSocket();
chatLoaded = true;
}
});
chatClose.addEventListener('click', () => {
chatContainer.classList.remove('show');
});
// Função para gerar ID único de sessão
function generateUniqueId() {
const timestamp = Date.now().toString(36);
const random = Math.random().toString(36).substring(2, 8);
return `${timestamp}-${random}`;
}
function generateSessionId() {
if (!sessionStorage.getItem('channelWebchatId')) {
sessionStorage.setItem('channelWebchatId', generateUniqueId());
}
return sessionStorage.getItem('channelWebchatId');
}
// Função para registrar o usuário no backend
async function registerWebchat() {
webchatId = generateSessionId();
const name = 'WebChat ' + webchatId;
const email = 'webchat@webchat.com';
const tenantId = '28';
const wabaId = 'a708cef4-0a43-4b09-b89b-e6f48398d759';
const websocketToken = '09e2902e-63fc-4780-84e7-a774f84b732d';
const response = await fetch(`https://api06.omniquestgo.com.br/webchat/register/${wabaId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-websocket-token': websocketToken
},
body: JSON.stringify({ webchatId, name, email, tenantId })
});
const data = await response.json();
token = data.token;
return { webchatId, token };
}
// Renova o JWT per-sessão se faltar < 5min para expirar
async function ensureFreshToken() {
if (!token) {
await registerWebchat();
return;
}
try {
const parts = token.split('.');
if (parts.length < 2) {
await registerWebchat();
return;
}
const payload = JSON.parse(atob(parts[1]));
const expMs = (payload.exp || 0) * 1000;
if (expMs - Date.now() < 5 * 60 * 1000) {
await registerWebchat();
}
} catch (e) {
await registerWebchat();
}
}
// Exibe o ID da sessão
async function showSessionId() {
const { webchatId } = await registerWebchat();
sessionSpan.textContent = `Sessão: ${webchatId}`;
}
showSessionId();
// Função para formatar hora
function formatTime(dateString) {
const date = new Date(dateString);
return date.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
}
// Função para formatar texto estilo WhatsApp
function formatWhatsapp(text) {
let formatted = String(text || '');
formatted = formatted.replace(/\*(.*?)\*/g, '$1');
formatted = formatted.replace(/NEW LINE/gi, ' ');
formatted = formatted.replace(/\\n/g, ' ');
formatted = formatted.replace(/\n/g, ' ');
return formatted;
}
function escapeHtml(text) {
return String(text || '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function parseMenuMessage(text) {
const raw = String(text || '').trim();
if (!raw.startsWith('#MENU')) return null;
const normalized = raw
.replace(/ /gi, '\n')
.replace(/NEW LINE/gi, '\n')
.replace(/\\n/g, '\n');
const lines = normalized.split('\n').map(l => l.trim()).filter(Boolean);
if (!lines.length) return null;
const title = lines[0].replace(/^#MENU\s*/i, '').trim() || 'Escolha uma opcao';
const items = lines.slice(1).map(l => l.replace(/^\d+\.\s*/, '').trim()).filter(Boolean);
if (!items.length) return null;
return { title, items };
}
function buildMenuHtml(menu) {
const buttonsHtml = menu.items.map(item => {
const safe = escapeHtml(item);
return ``;
}).join('');
return `
${escapeHtml(menu.title)}
${buttonsHtml}
`;
}
// Função para construir URL completa da mídia
function buildMediaUrl(mediaUrl) {
if (!mediaUrl) return null;
if (mediaUrl.startsWith('http://') || mediaUrl.startsWith('https://')) {
return mediaUrl;
}
const baseUrl = `https://api06.omniquestgo.com.br/public/${tenantId}`;
return `${baseUrl}/${mediaUrl}`;
}
// Função para adicionar mensagem
function appendMessage(text, type, time = '', ack = null, id = null, mediaType = null, mediaUrl = null) {
const messageDiv = document.createElement('div');
if (id) messageDiv.id = 'msg-' + id;
messageDiv.className = `channel-message ${type}`;
let ackHtml = '';
if (type === 'channel-sent' && ack !== null && ack !== undefined) {
ackHtml = `${getAckIcon(ack)}`;
}
let contentHtml = '';
let caption = '';
if (text && text.startsWith('caption: ')) {
caption = text.substring(9);
text = '';
}
if (mediaType === 'location') {
const mapsUrl = (text && /^https?:\/\//i.test(text)) ? text : '';
contentHtml = `📍Localização${mapsUrl ? ' Abrir no mapa' : ''}
`;
} else if (mediaType === 'vcard') {
const fnMatch = String(text || '').match(/FN:([^\n]+)/);
const telMatch = String(text || '').match(/TEL[^:]*:([^\n]+)/);
const fn = fnMatch ? fnMatch[1].trim() : 'Contato';
const tel = telMatch ? telMatch[1].trim() : '';
contentHtml = `
👤${fn}${tel ? ' ' + tel + '' : ''}
`;
} else if (mediaType && mediaUrl) {
const fullMediaUrl = buildMediaUrl(mediaUrl);
switch (mediaType.toLowerCase()) {
case 'image':
contentHtml = `
${caption ? `
${formatWhatsapp(caption)}
` : ''}
`;
break;
case 'video':
contentHtml = `
${caption ? `
${formatWhatsapp(caption)}
` : ''}
`;
break;
case 'audio':
contentHtml = `
${caption ? `
${formatWhatsapp(caption)}
` : ''}
`;
break;
case 'document':
contentHtml = `📄${caption || 'Documento'}
`;
break;
default:
contentHtml = `${formatWhatsapp(text)}`;
}
} else {
const menuData = parseMenuMessage(text);
if (menuData) {
contentHtml = buildMenuHtml(menuData);
} else {
contentHtml = `${formatWhatsapp(text)}`;
}
}
messageDiv.innerHTML = `${contentHtml} ${time} ${ackHtml}`;
messagesDiv.appendChild(messageDiv);
messageDiv.querySelectorAll('[data-menu-send]').forEach(btn => {
btn.addEventListener('click', () => {
messageInput.value = btn.getAttribute('data-menu-send');
sendButton.click();
});
});
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
// Função para atualizar o ack de uma mensagem
function updateMessageAck(messageId, ack) {
const msgDiv = document.getElementById('msg-' + messageId);
if (msgDiv) {
const ackSpan = msgDiv.querySelector('.channel-ack');
if (ackSpan) {
ackSpan.innerHTML = getAckIcon(ack);
}
}
}
// Função para obter ícone do ack
function getAckIcon(ack) {
if (ack === 0) return '🕓';
if (ack === 1) return '✔️';
if (ack === 2) return '✔️';
if (ack === 3) return '✔️✔️';
if (ack === -1) return '❌';
return '';
}
// Função para renderizar o histórico completo
function renderHistory(messages) {
messagesDiv.innerHTML = '';
messages.forEach(msg => {
appendMessage(
msg.body,
msg.fromMe ? 'channel-received' : 'channel-sent',
formatTime(msg.createdAt),
msg.ack,
msg.id,
msg.mediaType,
msg.mediaUrl
);
});
}
// Função para carregar histórico de mensagens
async function loadMessageHistory() {
try {
await ensureFreshToken();
const wabaId = 'a708cef4-0a43-4b09-b89b-e6f48398d759';
const response = await fetch(`https://api06.omniquestgo.com.br/webchat/messages/${wabaId}?from=${webchatId}&tenantId=28`, {
headers: {
'Authorization': 'Bearer ' + token
}
});
const data = await response.json();
if (Array.isArray(data)) {
renderHistory(data);
} else {
LOGGER_ENABLED && console.warn('[WebChat] Resposta da API não é um array:', data);
}
} catch (error) {
LOGGER_ENABLED && console.error('[WebChat] Erro ao carregar histórico:', error);
}
}
// Função para gerar um ID temporário para mensagens enviadas
function generateTempId() {
return 'temp-' + Math.random().toString(36).substr(2, 9);
}
// Função para atualizar o ID de uma mensagem no DOM
function updateMessageId(tempId, realId) {
const tempDiv = document.getElementById('msg-' + tempId);
if (tempDiv) {
tempDiv.id = 'msg-' + realId;
}
}
// Função para sanitizar o nome do arquivo
function sanitizeFileName(filename) {
if (!filename) return '';
return filename
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-zA-Z0-9.\-_]/g, '_')
.replace(/_+/g, '_')
.replace(/^_+|_+$/g, '');
}
// Classifica o file.type em image/video/audio/document (PDF, doc, etc. caem em document)
function classifyMediaType(file) {
const t = (file && file.type ? String(file.type) : '').toLowerCase();
if (t.indexOf('image/') === 0) return 'image';
if (t.indexOf('video/') === 0) return 'video';
if (t.indexOf('audio/') === 0) return 'audio';
return 'document';
}
// Função para enviar mídia
async function sendMedia(file) {
const sanitizedFileName = sanitizeFileName(file.name);
const formData = new FormData();
formData.append('medias', file, sanitizedFileName);
const data = {
body: 'caption: ' + (messageInput.value.trim() || 'Mídia enviada'),
from: webchatId,
name: webchatId,
email: webchatId + '@webchat.com',
tenantId: '28',
event: 'messages.upsert',
fromMe: false,
channel: 'webchat',
type: 'webchat',
webchatId: webchatId,
mediaType: classifyMediaType(file),
fileName: sanitizedFileName
};
formData.append('data', JSON.stringify(data));
try {
await ensureFreshToken();
const wabaId = 'a708cef4-0a43-4b09-b89b-e6f48398d759';
const response = await fetch(`https://api06.omniquestgo.com.br/webchat-webhook/${wabaId}`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token
},
body: formData
});
const responseText = await response.text();
let respData = {};
if (responseText) {
try {
respData = JSON.parse(responseText);
} catch (parseError) {
LOGGER_ENABLED && console.error('[WebChat] Erro ao fazer parse da resposta:', parseError);
throw new Error('Resposta inválida do servidor');
}
}
if (!response.ok) {
throw new Error(respData.message || 'Erro ao enviar mídia');
}
messageInput.value = '';
const tempId = generateTempId();
appendMessage(
data.body,
'channel-sent',
formatTime(new Date().toISOString()),
0,
tempId,
data.mediaType,
null
);
await loadMessageHistory();
} catch (error) {
LOGGER_ENABLED && console.error('[WebChat] Erro detalhado ao enviar mídia:', {
mensagem: error.message,
stack: error.stack,
erro: error
});
alert('Erro ao enviar mídia. Por favor, tente novamente.');
}
}
// Event listeners para envio de mensagem
sendButton.addEventListener('click', async () => {
const message = messageInput.value.trim();
if (message) {
const tempId = generateTempId();
appendMessage(message, 'channel-sent', formatTime(new Date().toISOString()), 0, tempId);
messageInput.value = '';
const data = {
body: message,
from: webchatId,
name: webchatId,
email: webchatId + '@webchat.com',
tenantId: '28',
event: 'messages.upsert',
fromMe: false,
channel: 'webchat',
type: 'webchat',
webchatId: webchatId
};
try {
await ensureFreshToken();
const wabaId = 'a708cef4-0a43-4b09-b89b-e6f48398d759';
const response = await fetch(`https://api06.omniquestgo.com.br/webchat-webhook/${wabaId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(data)
});
const respData = await response.json();
if (respData && respData.id) {
updateMessageId(tempId, respData.id);
}
if (respData && respData.mediaUrl) {
appendMessage(
respData.body,
'channel-sent',
formatTime(new Date().toISOString()),
0,
respData.id || tempId,
respData.mediaType,
respData.mediaUrl
);
}
await loadMessageHistory();
} catch (error) {
LOGGER_ENABLED && console.error('[WebChat] Erro ao enviar mensagem:', error);
}
}
});
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendButton.click();
}
});
// Event listener para o botão de anexo
attachButton.addEventListener('click', () => {
fileInput.click();
});
// Event listener para seleção de arquivo
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
sendMedia(file);
}
fileInput.value = '';
});
// WebSocket para receber mensagens e ack em tempo real
function connectWebSocket() {
if (!webchatId || !token) return;
let pingInterval;
let historyInterval;
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
const RECONNECT_DELAY = 5000;
const PING_INTERVAL = 30000;
const HISTORY_INTERVAL = 60000;
function connect() {
ws = new WebSocket(`wss://api06.omniquestgo.com.br/wss?from=${webchatId}&token=${token}`);
ws.onopen = () => {
LOGGER_ENABLED && console.log('[WebChat] WebSocket conectado!');
reconnectAttempts = 0;
pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, PING_INTERVAL);
historyInterval = setInterval(async () => {
if (ws.readyState === WebSocket.OPEN) {
await loadMessageHistory();
}
}, HISTORY_INTERVAL);
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'webhook' && data.payload && data.payload.message) {
const msg = data.payload.message;
appendMessage(
msg.body,
'channel-received',
formatTime(msg.createdAt),
msg.ack,
msg.id,
msg.mediaType,
msg.mediaUrl
);
if (msg.mediaType) {
loadMessageHistory();
}
}
if (data.type === 'ack_update' && data.payload) {
if (data.payload.mediaType) {
const msgDiv = document.getElementById('msg-' + data.payload.id);
if (msgDiv) {
msgDiv.remove();
appendMessage(
data.payload.body,
'channel-sent',
formatTime(data.payload.createdAt),
data.payload.ack,
data.payload.id,
data.payload.mediaType,
data.payload.mediaUrl
);
}
} else {
updateMessageAck(data.payload.messageId, data.payload.ack);
}
}
if (data.type === 'pong') {
LOGGER_ENABLED && console.log('[WebChat] Pong recebido');
}
} catch (error) {
LOGGER_ENABLED && console.error('[WebChat] Erro ao processar mensagem WebSocket:', error);
}
};
ws.onerror = (error) => {
LOGGER_ENABLED && console.error('[WebChat] Erro na conexão WebSocket:', error);
};
ws.onclose = () => {
LOGGER_ENABLED && console.log('[WebChat] Conexão WebSocket fechada');
clearInterval(pingInterval);
clearInterval(historyInterval);
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
reconnectAttempts++;
LOGGER_ENABLED && console.log(`[WebChat] Tentando reconectar (tentativa ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
setTimeout(connect, RECONNECT_DELAY);
} else {
LOGGER_ENABLED && console.error('[WebChat] Número máximo de tentativas de reconexão atingido');
}
};
}
connect();
}
// Função para limpar a sessão
async function clearSession() {
if (confirm('Tem certeza que deseja limpar a sessão e começar uma nova conversa?')) {
messagesDiv.innerHTML = '';
sessionStorage.removeItem('channelWebchatId');
if (ws) {
ws.close();
}
webchatId = null;
token = null;
chatLoaded = false;
await showSessionId();
await loadMessageHistory();
connectWebSocket();
}
}
chatClear.addEventListener('click', clearSession);
})();