Passei um tempo tentando achar uma solução porem não achei
Criei utilizando IA, uma serviço que direciona automaticamente um ticket.

Esse código é responsável por atribuir automaticamente tickets (chamados) a agentes de suporte com base na disponibilidade de departamentos e na ordem de atendimento. Ele realiza as seguintes operações principais:

Leitura da Configuração do Banco:
A função read_db_config() abre e extrai os parâmetros de conexão (usuário, senha, host, banco de dados) a partir do arquivo de configuração PHP (ost-config.php). Caso alguma configuração esteja faltando, gera um erro.

Identificação de Departamentos Ativos e Emails:

get_active_departments(config): Consulta a tabela ost_active_departments para obter os departamentos que estão ativos.
get_emails_and_departments(config): Recupera da tabela ost_email um mapeamento entre emails e os IDs dos departamentos associados.
Seleção do Próximo Agente Disponível:
A função get_next_agent(department_id, config) busca na tabela ost_staff (em conjunto com as tabelas ost_team_member e ost_team) o agente que:

Pertence ao departamento informado;
Está ativo, não está de férias, e não é administrador;
Não é o líder da equipe;
Foi o menos recentemente designado (ordena por last_assigned).
Atribuição do Ticket:

assign_ticket(ticket_id, agent_id, config): Atualiza o ticket na tabela ost_ticket para definir o agente responsável e atualiza o campo last_assigned do agente na tabela ost_staff para o momento atual.
Processamento dos Tickets:
A função process_tickets(department_id, config):

Consulta a tabela ost_ticket para tickets não atribuídos (onde staff_id é 0) para um departamento específico;
Para cada ticket encontrado, obtém o próximo agente disponível e o atribui por meio de assign_ticket.
Loop de Execução:
O código entra num loop infinito (com pausa de 60 segundos) em que:

Lê os departamentos ativos e os emails associados;
Para cada email cujo departamento esteja ativo, processa os tickets não atribuídos para aquele departamento.
Log de Informações e Erros:
As funções log_info, log_error e log_message registram mensagens com timestamp num arquivo (auto_assign.log) e também imprimem as mensagens no console.

Funcionamento na Prática
Configuração: O código se conecta ao banco de dados utilizando as credenciais extraídas do arquivo de configuração.
Verificação Periódica: A cada 60 segundos, ele verifica quais departamentos estão ativos e, para cada email associado a um departamento ativo, busca por tickets sem agente atribuído.
Distribuição de Chamados: Para cada ticket encontrado, ele escolhe o agente mais adequado (baseado na ordem de last_assigned) e atualiza o ticket e o registro do agente para refletir a nova atribuição.
Registro de Logs: Cada ação ou eventual erro é registrado para facilitar a auditoria e a solução de problemas.
Esse código, portanto, implementa uma lógica simples de fila, garantindo que os tickets sejam distribuídos entre os agentes conforme a ordem de atendimento, sem repetir o agente até que todos tenham sido contemplados (por meio do campo last_assigned na tabela ost_staff). Se precisar de mais alguma coisa ou tiver dúvidas, estou à disposição!

` import mysql.connector
from mysql.connector import Error
import time
import re
import datetime

def read_db_config():
config = {}
try:
with open('/var/www/html/upload/include/ost-config.php', 'r') as file:
content = file.read()


        user_match = re.search(r"define\('DBUSER',\s*'([^']+)'\);", content)
        password_match = re.search(r"define\('DBPASS',\s*'([^']+)'\);", content)
        host_match = re.search(r"define\('DBHOST',\s*'([^']+)'\);", content)
        database_match = re.search(r"define\('DBNAME',\s*'([^']+)'\);", content)
        
        if user_match and password_match and host_match and database_match:
            config['user'] = user_match.group(1)
            config['password'] = password_match.group(1)
            config['host'] = host_match.group(1)
            config['database'] = database_match.group(1)
        else:
            missing = []
            if not user_match:
                missing.append("DBUSER")
            if not password_match:
                missing.append("DBPASS")
            if not host_match:
                missing.append("DBHOST")
            if not database_match:
                missing.append("DBNAME")
            raise ValueError(f"Faltam configuracoes: {', '.join(missing)}")
except Exception as e:
    log_error(f"Erro ao ler o arquivo de configuracao: {e}")
return config

def get_active_departments(config):
try:
connection = mysql.connector.connect(**config)
cursor = connection.cursor(dictionary=True)


    query = "SELECT dept_id FROM ost_active_departments"
    cursor.execute(query)
    results = cursor.fetchall()
    
    active_departments = [result['dept_id'] for result in results]
    return active_departments

except Error as e:
    log_error(f"Erro ao obter departamentos ativos: {e}")
    return []
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()

def get_emails_and_departments(config):
try:
connection = mysql.connector.connect(**config)
cursor = connection.cursor(dictionary=True)


    query = "SELECT email, dept_id FROM ost_email"
    cursor.execute(query)
    results = cursor.fetchall()
    
    email_dept_map = {result['email']: result['dept_id'] for result in results}
    return email_dept_map

except Error as e:
    log_error(f"Erro ao obter emails e departamentos: {e}")
    return {}
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()

def get_next_agent(department_id, config):
try:
connection = mysql.connector.connect(**config)
cursor = connection.cursor(dictionary=True)

    query = """
    SELECT staff.staff_id 
    FROM ost_staff staff
    JOIN ost_team_member member ON staff.staff_id = member.staff_id
    JOIN ost_team team ON member.team_id = team.team_id
    WHERE staff.dept_id = %s 
    AND staff.isadmin = 0
    AND staff.isactive = 1
    AND staff.onvacation = 0
    AND staff.staff_id != team.lead_id
    ORDER BY staff.last_assigned ASC 
    LIMIT 1
    """
    cursor.execute(query, (department_id,))
    result = cursor.fetchone()
    if result:
        log_info(f"Proximo agente encontrado para o departamento {department_id}: {result['staff_id']}")
        return result['staff_id']
    else:
        log_info(f"Nenhum agente encontrado para o departamento {department_id}")
        return None

except Error as e:
    log_error(f"Erro ao obter proximo agente: {e}")
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()

def assign_ticket(ticket_id, agent_id, config):
try:
connection = mysql.connector.connect(**config)
cursor = connection.cursor()

    update_ticket_query = "UPDATE ost_ticket SET staff_id = %s WHERE ticket_id = %s"
    cursor.execute(update_ticket_query, (agent_id, ticket_id))

    update_agent_query = "UPDATE ost_staff SET last_assigned = NOW() WHERE staff_id = %s"
    cursor.execute(update_agent_query, (agent_id,))

    connection.commit()
    log_info(f"Ticket {ticket_id} atribuido ao agente {agent_id}")

except Error as e:
    log_error(f"Erro ao atribuir ticket: {e}")
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()

def process_tickets(department_id, config):
try:
connection = mysql.connector.connect(**config)
cursor = connection.cursor(dictionary=True)

    query = "SELECT ticket_id FROM ost_ticket WHERE staff_id = 0 AND dept_id = %s"
    cursor.execute(query, (department_id,))
    tickets = cursor.fetchall()

    if tickets:
        log_info(f"Tickets nao atribuidos encontrados para o departamento {department_id}: {len(tickets)}")
        for ticket in tickets:
            agent_id = get_next_agent(department_id, config)
            if agent_id:
                assign_ticket(ticket['ticket_id'], agent_id, config)
    else:
        log_info(f"Nenhum ticket nao atribuido encontrado para o departamento {department_id}")

except Error as e:
    log_error(f"Erro ao processar tickets: {e}")
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()

def log_info(message):
log_message("INFO", message)

def log_error(message):
log_message("ERROR", message)

def log_message(level, message):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"{timestamp} [{level}] {message}"
log_file = '/var/www/html/upload/script/auto_assign.log'
with open(log_file, 'a') as file:
file.write(log_entry + '\n')
print(log_entry) # Para exibir no console também

Loop principal para processar todos os emails e seus respectivos departamentos

config = read_db_config()
if not config:
log_error("Erro ao ler as configuracoes do banco de dados. Verifique o arquivo ost-config.php.")
exit()

while True:
active_departments = get_active_departments(config)
if not active_departments:
log_info("Nenhum departamento ativo encontrado. Aguardando...")
time.sleep(60)
continue

email_dept_map = get_emails_and_departments(config)
for email, dept_id in email_dept_map.items():
    if dept_id in active_departments:
        log_info(f"Iniciando processamento para o email {email} e departamento {dept_id}")
        process_tickets(dept_id, config)
time.sleep(60)  # Verificar novos tickets a cada 60 segundos

`
Pagina

`<?php
require('admin.inc.php');
require_once INCLUDE_DIR.'class.csrf.php';

global $ost, $cfg;

// Function to fetch departments from the database
function fetch_departments() {
$departments = [];
$query = "SELECT id, name FROM " . DEPT_TABLE;
if ($res = db_query($query)) {
while ($row = db_fetch_array($res)) {
$departments[$row['id']] = $row['name'];
}
}
return $departments;
}

// Function to fetch active departments
function fetch_active_departments() {
$active_departments = [];
$query = "SELECT dept_id FROM ost_active_departments";
if ($res = db_query($query)) {
while ($row = db_fetch_array($res)) {
$active_departments[] = $row['dept_id'];
}
}
return $active_departments;
}

$saved = false; // Variable to check if the form was saved

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Check CSRF token
if (!$ost->checkCSRFToken()) {
Http::response(400, __('Valid CSRF Token Required'));
exit;
}

// Get selected active and inactive departments
$selected_active_departments = isset($_POST['active_departments']) ? $_POST['active_departments'] : [];
$selected_inactive_departments = isset($_POST['inactive_departments']) ? $_POST['inactive_departments'] : [];

// Fetch current active departments
$current_active_departments = fetch_active_departments();

// Calculate which departments to add or remove
$to_add = array_diff($selected_active_departments, $current_active_departments);
$to_remove = array_intersect($selected_inactive_departments, $current_active_departments);

// Add new active departments to the database
foreach ($to_add as $dept_id) {
    db_query("INSERT INTO ost_active_departments (dept_id) VALUES (" . db_input($dept_id) . ")");
}

// Remove departments no longer active
foreach ($to_remove as $dept_id) {
    db_query("DELETE FROM ost_active_departments WHERE dept_id = " . db_input($dept_id));
}

$saved = true; // Set the variable to true when saved

}

$departments = fetch_departments();
$active_departments = fetch_active_departments();

include(STAFFINC_DIR.'header.inc.php');
?>

<h2>Configurações de Atribuição Automática de Tickets</h2>
<form method="post" action="auto_assign.php">
<?php csrf_token(); ?>
<div style="display: flex;">
<div style="margin-right: 20px;">
<label for="inactive_departments">Departamentos Inativos:</label>
<select id="inactive_departments" name="inactive_departments[]" multiple style="width: 200px; height: 300px;">
<?php
foreach ($departments as $dept_id => $dept_name) {
if (!in_array($dept_id, $active_departments)) {
echo "<option value=\"$dept_id\">$dept_name</option>";
}
}
?>
</select>
</div>
<div style="display: flex; flex-direction: column; justify-content: center;">
<button type="button" onclick="moveItems('inactive_departments', 'active_departments')">>>></button>
<button type="button" onclick="moveItems('active_departments', 'inactive_departments')"><<<</button>
</div>
<div>
<label for="active_departments">Departamentos Ativos:</label>
<select id="active_departments" name="active_departments[]" multiple style="width: 200px; height: 300px;">
<?php
foreach ($departments as $dept_id => $dept_name) {
if (in_array($dept_id, $active_departments)) {
echo "<option value=\"$dept_id\">$dept_name</option>";
}
}
?>
</select>
</div>
</div>
<br>
<input type="submit" value="Salvar">
<?php
if ($saved) {
echo '<div id="notification" class="msg success">Configurações salvas com sucesso!</div>';
}
?>
</form>

<script>
function moveItems(sourceId, destinationId) {
var source = document.getElementById(sourceId);
var destination = document.getElementById(destinationId);
var selectedOptions = [...source.selectedOptions];
selectedOptions.forEach(option => {
destination.appendChild(option);
});
}

// Notification timeout
document.addEventListener('DOMContentLoaded', function() {
const notification = document.getElementById('notification');
if (notification) {
setTimeout(() => {
notification.style.display = 'none';
}, 3000); // Hide after 3 seconds
}
});
</script>

<style>
.msg.success {
color: green;
font-weight: bold;
margin-top: 10px;
}
</style>

<?php
include(STAFFINC_DIR.'footer.inc.php');
?>
Menuif ($nav) {
$nav->addSubMenu(array(
'desc' => __('Auto Assign Settings'),
'href' => 'auto_assign.php',
'iconclass' => 'fa-cogs'
));
}
`

    EstrelaEx

    Please do not post mods on this Forum. If you would like to share mods please create your own Github repo and advertise elsewhere.

    Cheers.

    Write a Reply...