Оператори
Управлявайте операторите за чат във вашите организации в ChatHub. Операторите са агенти, които се справят с разговорите с клиенти.
Преглед
Операторите в ChatHub:
- Обработват клиентски чат разговори
- Принадлежат към определени организации
- Имат активен, неактивен или изтрит статус
- Могат да имат индивидуални токени за удостоверяване
- Получават и отговарят на съобщения
Списък с оператори
Извлечете всички оператори за определена организация.
Крайна точка (Endpoint)
Заявка
curl -X GET "https://chatapi.smsbat.com/api/operator?organizationId=24" \
-H "Authorization: Bearer {company-token}" \
-H "Accept: text/plain"
Параметри
| Параметър | Тип | Местоположение | Задължителен | Описание |
|---|---|---|---|---|
organizationId |
integer | Query | Да | ID на организация |
Заглавки (Headers)
| Header | Стойност | Задължителен |
|---|---|---|
Authorization |
Bearer | Да |
Accept |
text/plain | Да |
Отговор
[
{
"id": 101,
"name": "Иван Иванов",
"status": 0,
"organization": {
"id": 24,
"name": "Customer Support"
}
},
{
"id": 102,
"name": "Сара Джонсън",
"status": 0,
"organization": {
"id": 24,
"name": "Customer Support"
}
},
{
"id": 103,
"name": "Майк Уилсън",
"status": 1,
"organization": {
"id": 24,
"name": "Customer Support"
}
}
]
Полета в отговора
| Поле | Тип | Описание |
|---|---|---|
id |
integer | Уникален идентификатор на оператора |
name |
string | Име на оператора за показване |
status |
integer | Статус на оператора (0=Активен, 1=Неактивен, 2=Изтрит) |
organization |
object | Подробности за родителската организация |
organization.id |
integer | ID на организация |
organization.name |
string | Име на организация |
Статус на оператора
| Статус | Стойност | Описание |
|---|---|---|
| Активен (Active) | 0 | Операторът работи в момента и може да приема чатове |
| Неактивен (Inactive) | 1 | Операторът е временно деактивиран |
| Изтрит (Deleted) | 2 | Операторът е премахнат от системата |
Добавяне на оператори
Добавете нови оператори към организации, използвайки крайната точка (endpoint) за синхронизация.
Крайна точка (Endpoint)
Заявка
curl -X POST https://chatapi.smsbat.com/api/operator/synchronize \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '[
{
"organizationId": 24,
"name": "Алекс Браун"
},
{
"organizationId": 24,
"name": "Ема Дейвис"
}
]'
Тяло на заявката
Масив от обекти за оператори:
Полета в заявката
| Поле | Тип | Задължителен | Описание |
|---|---|---|---|
organizationId |
integer | Да | ID на целевата организация |
name |
string | Да | Име на оператора за показване |
Отговор
[
{
"id": 104,
"name": "Алекс Браун",
"status": 0
},
{
"id": 105,
"name": "Ема Дейвис",
"status": 0
}
]
Промяна на статуса на оператор
Актуализирайте статуса на оператора (Активен/Неактивен/Изтрит).
Крайна точка (Endpoint)
Заявка
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 1
}'
Тяло на заявката
Полета в заявката
| Поле | Тип | Задължителен | Описание |
|---|---|---|---|
id |
integer | Да | ID на оператор |
status |
integer | Да | Нов статус (0=Активен, 1=Неактивен, 2=Изтрит) |
Отговор
Успехът връща HTTP 200 без тяло на отговора.
Стойности на статуса
| Статус | Стойност | Описание |
|---|---|---|
| Активен (Active) | 0 | Операторът може да обработва чатове |
| Неактивен (Inactive) | 1 | Операторът е временно деактивиран |
| Изтрит (Deleted) | 2 | Операторът е премахнат от системата |
Пример: Деактивиране на оператор
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 1
}'
Пример: Реактивиране на оператор
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 0
}'
Пример: Изтриване на оператор
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 2
}'
Примери за внедряване
Python
import requests
class OperatorManager:
def __init__(self, company_token, base_url='https://chatapi.smsbat.com'):
self.company_token = company_token
self.base_url = base_url
self.headers = {
'Authorization': f'Bearer {company_token}',
'Accept': 'text/plain'
}
def list_operators(self, organization_id):
"""Вземете всички оператори за организация"""
response = requests.get(
f'{self.base_url}/api/operator',
headers=self.headers,
params={'organizationId': organization_id}
)
response.raise_for_status()
return response.json()
def add_operators(self, operators):
"""Добавете нови оператори
Args:
operators: Списък с речници (dicts) с organizationId и name
"""
response = requests.post(
f'{self.base_url}/api/operator/synchronize',
headers={**self.headers, 'Content-Type': 'application/json'},
json=operators
)
response.raise_for_status()
return response.json()
def get_active_operators(self, organization_id):
"""Вземете само активни оператори"""
operators = self.list_operators(organization_id)
return [op for op in operators if op['status'] == 0]
def find_operator_by_name(self, organization_id, name):
"""Намерете оператор по име"""
operators = self.list_operators(organization_id)
for operator in operators:
if operator['name'].lower() == name.lower():
return operator
return None
def change_operator_status(self, operator_id, status):
"""Промяна на статуса на оператор
Args:
operator_id: ID на оператор
status: 0 (Активен), 1 (Неактивен), 2 (Изтрит)
"""
response = requests.post(
f'{self.base_url}/api/operator/status',
headers={**self.headers, 'Content-Type': 'application/json'},
json={'id': operator_id, 'status': status}
)
response.raise_for_status()
return response.status_code == 200
# Употреба
manager = OperatorManager('your-company-token')
# Списък с оператори
operators = manager.list_operators(organization_id=24)
print(f"Намерени {len(operators)} оператора")
# Вземете само активни оператори
active = manager.get_active_operators(24)
print(f"{len(active)} активни оператори")
# Добавете нови оператори
new_operators = manager.add_operators([
{'organizationId': 24, 'name': 'Джон Доу'},
{'organizationId': 24, 'name': 'Джейн Смит'}
])
print(f"Добавени {len(new_operators)} оператора")
# Намерете оператор по име
operator = manager.find_operator_by_name(24, 'Джон Доу')
if operator:
print(f"Намерен оператор: {operator['name']} (ID: {operator['id']})")
# Промяна на статуса на оператор
manager.change_operator_status(104, 1) # Деактивиране
print("Операторът е деактивиран")
manager.change_operator_status(104, 0) # Реактивиране
print("Операторът е реактивиран")
JavaScript (Node.js)
const axios = require('axios');
class OperatorManager {
constructor(companyToken, baseUrl = 'https://chatapi.smsbat.com') {
this.companyToken = companyToken;
this.baseUrl = baseUrl;
this.headers = {
'Authorization': `Bearer ${companyToken}`,
'Accept': 'text/plain'
};
}
async listOperators(organizationId) {
const response = await axios.get(
`${this.baseUrl}/api/operator`,
{
headers: this.headers,
params: { organizationId }
}
);
return response.data;
}
async addOperators(operators) {
const response = await axios.post(
`${this.baseUrl}/api/operator/synchronize`,
operators,
{
headers: {
...this.headers,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
async getActiveOperators(organizationId) {
const operators = await this.listOperators(organizationId);
return operators.filter(op => op.status === 0);
}
async findOperatorByName(organizationId, name) {
const operators = await this.listOperators(organizationId);
return operators.find(op =>
op.name.toLowerCase() === name.toLowerCase()
);
}
async getOperatorById(organizationId, operatorId) {
const operators = await this.listOperators(organizationId);
return operators.find(op => op.id === operatorId);
}
async changeOperatorStatus(operatorId, status) {
const response = await axios.post(
`${this.baseUrl}/api/operator/status`,
{ id: operatorId, status },
{
headers: {
...this.headers,
'Content-Type': 'application/json'
}
}
);
return response.status === 200;
}
}
// Употреба
const manager = new OperatorManager('your-company-token');
async function manageOperators() {
// Списък с оператори
const operators = await manager.listOperators(24);
console.log(`Намерени ${operators.length} оператора`);
// Вземете активни оператори
const active = await manager.getActiveOperators(24);
console.log(`${active.length} активни оператори`);
// Добавете нови оператори
const newOperators = await manager.addOperators([
{ organizationId: 24, name: 'Джон Доу' },
{ organizationId: 24, name: 'Джейн Смит' }
]);
console.log(`Добавени ${newOperators.length} оператора`);
// Намерете оператор по име
const operator = await manager.findOperatorByName(24, 'Джон Доу');
if (operator) {
console.log(`Намерен: ${operator.name} (ID: ${operator.id})`);
}
// Промяна на статуса на оператор
await manager.changeOperatorStatus(104, 1); // Деактивиране
console.log('Операторът е деактивиран');
await manager.changeOperatorStatus(104, 0); // Реактивиране
console.log('Операторът е реактивиран');
}
manageOperators();
PHP
<?php
class OperatorManager {
private $companyToken;
private $baseUrl;
public function __construct($companyToken, $baseUrl = 'https://chatapi.smsbat.com') {
$this->companyToken = $companyToken;
$this->baseUrl = $baseUrl;
}
public function listOperators($organizationId) {
$url = $this->baseUrl . '/api/operator?organizationId=' . $organizationId;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Accept: text/plain'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function addOperators($operators) {
$ch = curl_init($this->baseUrl . '/api/operator/synchronize');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Content-Type: application/json',
'Accept: text/plain'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($operators));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function getActiveOperators($organizationId) {
$operators = $this->listOperators($organizationId);
return array_filter($operators, function($op) {
return $op['status'] === 0;
});
}
public function findOperatorByName($organizationId, $name) {
$operators = $this->listOperators($organizationId);
foreach ($operators as $operator) {
if (strcasecmp($operator['name'], $name) === 0) {
return $operator;
}
}
return null;
}
public function changeOperatorStatus($operatorId, $status) {
$ch = curl_init($this->baseUrl . '/api/operator/status');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'id' => $operatorId,
'status' => $status
]));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode === 200;
}
}
// Употреба
$manager = new OperatorManager('your-company-token');
// Списък с оператори
$operators = $manager->listOperators(24);
echo "Намерени " . count($operators) . " оператора\n";
// Вземете активни оператори
$active = $manager->getActiveOperators(24);
echo count($active) . " активни оператори\n";
// Добавяне на нови оператори
$newOperators = $manager->addOperators([
['organizationId' => 24, 'name' => 'Джон Доу'],
['organizationId' => 24, 'name' => 'Джейн Смит']
]);
echo "Добавени " . count($newOperators) . " оператори\n";
// Намиране на оператор
$operator = $manager->findOperatorByName(24, 'Джон Доу');
if ($operator) {
echo "Намерен: " . $operator['name'] . " (ID: " . $operator['id'] . ")\n";
}
// Промяна на статуса
$manager->changeOperatorStatus(104, 1); // Деактивиране
echo "Операторът е деактивиран\n";
$manager->changeOperatorStatus(104, 0); // Реактивиране
echo "Операторът е реактивиран\n";
Често срещани случаи на употреба
Въвеждане на нови членове на екипа (Onboard)
async function onboardOperators(teamMembers, organizationId) {
const operators = teamMembers.map(member => ({
organizationId,
name: member.fullName
}));
const created = await addOperators(operators);
// Генериране на токени за всеки оператор
for (const operator of created) {
const token = await getOperatorToken(operator.id);
await sendWelcomeEmail(operator, token);
}
return created;
}
Наблюдаване на статуса на оператор
async function getOperatorStatistics(organizationId) {
const operators = await listOperators(organizationId);
return {
total: operators.length,
active: operators.filter(op => op.status === 0).length,
inactive: operators.filter(op => op.status === 1).length,
deleted: operators.filter(op => op.status === 2).length
};
}
Разпределение на натоварването (Load Balancing)
async function assignChatToOperator(organizationId, chatId) {
const activeOperators = await getActiveOperators(organizationId);
if (activeOperators.length === 0) {
throw new Error('Няма налични активни оператори');
}
// Просто кръгово (round-robin) присвояване
const operatorIndex = chatId % activeOperators.length;
return activeOperators[operatorIndex];
}
Масово импортиране
async function importOperatorsFromCSV(csvData, organizationId) {
const lines = csvData.split('\n').slice(1); // Спокойствие за заглавката
const operators = lines
.filter(line => line.trim())
.map(line => {
const [name] = line.split(',');
return { organizationId, name: name.trim() };
});
// Групово импортиране на порции (chunks) от 100
const chunkSize = 100;
const results = [];
for (let i = 0; i < operators.length; i += chunkSize) {
const chunk = operators.slice(i, i + chunkSize);
const imported = await addOperators(chunk);
results.push(...imported);
console.log(`Импортирани ${results.length}/${operators.length}`);
}
return results;
}
Добри практики
Обработка на грешки
async function addOperatorsSafely(operators) {
try {
return await addOperators(operators);
} catch (error) {
if (error.response?.status === 400) {
console.error('Невалидни данни за оператор:', error.response.data);
// Обработка на грешки при валидиране
} else if (error.response?.status === 401) {
console.error('Неуспешно удостоверяване');
// Опресняване на токена
} else {
console.error('Неочаквана грешка:', error);
}
throw error;
}
}
Кеширане
class CachedOperatorManager extends OperatorManager {
constructor(companyToken) {
super(companyToken);
this.cache = new Map();
this.cacheTTL = 60000; // 1 минута
}
async listOperators(organizationId, useCache = true) {
const cacheKey = `org_${organizationId}`;
const cached = this.cache.get(cacheKey);
if (useCache && cached && Date.now() - cached.time < this.cacheTTL) {
return cached.data;
}
const data = await super.listOperators(organizationId);
this.cache.set(cacheKey, {
data,
time: Date.now()
});
return data;
}
clearCache(organizationId = null) {
if (organizationId) {
this.cache.delete(`org_${organizationId}`);
} else {
this.cache.clear();
}
}
}
Валидиране
function validateOperatorData(operators) {
const errors = [];
operators.forEach((op, index) => {
if (!op.organizationId) {
errors.push(`Оператор ${index}: Липсва organizationId`);
}
if (!op.name || op.name.trim().length === 0) {
errors.push(`Оператор ${index}: Името е задължително`);
}
if (op.name && op.name.length > 100) {
errors.push(`Оператор ${index}: Името е твърде дълго (макс. 100 знака)`);
}
});
if (errors.length > 0) {
throw new Error('Валидирането е неуспешно:\n' + errors.join('\n'));
}
}
// Употреба
try {
validateOperatorData(operatorData);
await addOperators(operatorData);
} catch (error) {
console.error(error.message);
}
Ограничаване на скоростта (Rate Limiting)
class RateLimitedOperatorManager extends OperatorManager {
constructor(companyToken, requestsPerSecond = 5) {
super(companyToken);
this.minInterval = 1000 / requestsPerSecond;
this.lastRequest = 0;
}
async throttle() {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequest;
if (timeSinceLastRequest < this.minInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.minInterval - timeSinceLastRequest)
);
}
this.lastRequest = Date.now();
}
async listOperators(organizationId) {
await this.throttle();
return super.listOperators(organizationId);
}
async addOperators(operators) {
await this.throttle();
return super.addOperators(operators);
}
}