shevernitskiy / amo Goto Github PK
View Code? Open in Web Editor NEW♿amoCRM API client
License: MIT License
♿amoCRM API client
License: MIT License
New update added long-term access tokens for private integrations. They are the same as the access token, but has longer expire period.
Do we need to update readme? Basically we can use new token as an access_token, but specify expired_at as 5 years or less (depends on real token lifetime)
This is a issue about fix error.
I expect that if the files are successfully deleted, my script should execute next.
Currently, the method await amo.file(amoAccount.drive_url).deleteFiles(fileIds)
throws an Error: 204 No Content
on successful execution.
Based on AmoCRM documentation
you can see that code 204 comes when files are successfully deleted.
I checked, the files are indeed deleted from amoCRM.
As I found out the error occurs because the checkError method of the RestClient object specifies that if the response code is 204, the code throws an error throw new NoContentError(`${res.status} ${res.statusText}, ${res.statusText}, ${res.url}`);
(line 75).
Because of this collision I do not get further script execution.
To be honest, I haven't found a more elegant solution yet except to check the method and URL directly } else if (res.status === 204 && !(res.url.includes('v1.0/files') && res.method === 'DELETE'))) {
Any ideas?
We need to update requests module so we can add Chats API support. Currently it's not possible even using raw amoCRM API requests.
Required methods:
Required webhooks:
Required integration tokens, received from amoCRM when registering new Chats API integration:
Required account data:
${AMOJO_UUID}_${ACCOUNT_AMOJO_ID}
)We also need to constuct complex headers using request body to work with chats API, example:
import { createHash } from "https://deno.land/[email protected]/hash/mod.ts";
import { hmac } from "https://deno.land/x/[email protected]/mod.ts";
private getHeaders(body: object): Headers {
const contentType = "application/json";
const date = new Date().toUTCString().replace(
/(\w+), (\d+) (\w+) (\d+) (\d+):(\d+):(\d+) GMT/,
"$1, $2 $3 $4 $5:$6:$7 +0000",
);
const requestBody = JSON.stringify(body);
const checkSum = createHash("md5").update(requestBody).toString("hex");
const signature = hmac("sha1", AMOJO_SECRET, requestBody, "utf8", "hex").toString();
const headers = new Headers();
headers.append("Date", date);
headers.append("Content-Type", contentType);
headers.append("Content-MD5", checkSum);
headers.append("X-Signature", signature);
return headers;
}
Any help is appreciated
При попытке запросить
const field = await amo.custom_fields.getCustomFieldById(707045, 'leads');
console.log(`found field 707045 leads`, field);
возвращается
found field 707045 leads undefined
this.rest.get('/api/v4/leads/custom_fields/707045') при отладке возвращает тело ответа:
{
id: 707045,
name: 'Врач',
type: 'select',
account_id: 30091462,
code: null,
sort: 510,
is_api_only: false,
enums: [
{ id: 630339, value: 'Фомин Игорь Викторович', sort: 500 },
{ id: 630341, value: 'Кизарьянц Анна Альбертовна', sort: 1 },
{ id: 630343, value: 'Тупатилов Кирилл Валерьевич', sort: 2 },
{ id: 630345, value: 'Бриштен Виктория Леонидовна', sort: 3 },
{ id: 630347, value: 'Фроловичев Кирилл Андреевич', sort: 4 },
{ id: 630349, value: 'Фроловичева Марина Владимировна', sort: 5 },
{ id: 630351, value: 'Колотиков Павел Александрович', sort: 6 },
{ id: 630353, value: 'Родивилова Наталия Александровна', sort: 7 },
{ id: 630355, value: 'Фомина Ольга Леонтьевна', sort: 8 },
{ id: 630357, value: 'Мартынова Мария Игоревна', sort: 9 },
{ id: 630359, value: 'Байтокова Амина Джашарбековна', sort: 10 },
{ id: 630361, value: 'Чаниева Зара Ахмедовна', sort: 11 },
{ id: 630363, value: 'Еварницкая Наталья Ростиславовна', sort: 12 },
{ id: 630365, value: 'Сапрыкина Юлия Александровна', sort: 13 },
{ id: 630367, value: 'Липунова Валерия Константиновна', sort: 14 },
{ id: 630369, value: 'Петракова Алтына Юрьевна', sort: 15 },
{ id: 630371, value: 'Савченко Кристина Сергеевна', sort: 16 },
{ id: 631039, value: 'Рентген Рентген', sort: 17 }
],
group_id: null,
required_statuses: [],
is_deletable: true,
is_predefined: false,
entity_type: 'leads',
tracking_callback: null,
remind: null,
triggers: [],
currency: null,
hidden_statuses: [],
chained_lists: null,
_links: {
self: {
href: 'https://kprodent.amocrm.ru/api/v4/leads/custom_fields/707045'
}
}
}
По непонятной причине оно возвращается в вызывающую функцию как undefined
Hi, please add to the readme that at least nodejs >=18 is required.
As of only Node.js version 18, the fetch API is natively available.
Query for method GET /api/v4/${entity}/custom_fields supports params limit (до 50) и page for receiving all entity fields
Currently method getCustomFields doesn't support query params, so we can get only up to 50 fields
Example:
https://example.amocrm.ru/api/v4/leads/custom_fields?limit=2&page=2
I'd like to recommend switching to Bottleneck as way more stable and flexible approach to request queue, just adding a delay for request is way too unpredictable when using on production
export class RestClient {
private url_base: string;
private _token?: OAuth;
private limiter: Bottleneck;
constructor(
private base_url: string,
private auth: OAuthCode | OAuth & Pick<OAuthRefresh, "client_id" | "client_secret" | "redirect_uri">,
private options?: Options,
) {
this.url_base = `https://${this.base_url}`;
if (this.isOAuth(this.auth)) {
this._token = {
token_type: this.auth.token_type,
expires_in: this.auth.expires_in,
access_token: this.auth.access_token,
refresh_token: this.auth.refresh_token,
expires_at: this.auth.expires_at ?? 0,
};
}
this.limiter = new Bottleneck(this.limiterOptions);
}
private get limiterOptions(): BottleneckTypes.ConstructorOptions {
return this.options?.limiterOptions ?? {
reservoir: 7,
reservoirRefreshAmount: 7,
reservoirRefreshInterval: 1000,
maxConcurrent: 5,
minTime: 300
};
}
...
}
and then using it as
const res = await this.limiter.schedule(() => fetch(target, {
method: method,
headers: init.headers ?? {
"Authorization": `${this._token?.token_type} ${this._token?.access_token}`,
"Content-Type": "application/json",
},
body: init.payload ? JSON.stringify(init.payload) : undefined,
}));
Just a small problem before I prepare a pull request to review.
Bottleneck is external depedency, and this project is currently loading all external depedencies using CDNs without use of package.json
How to correctly load it then?
Для создания нового поля используется другой набор полей, а именно:
interface CustomFieldsValue = Partial<{
/** Тип поля. Список доступных полей */
type: CustomFieldsValueTypes;
/** Название поля */
name: string;
/** Код поля, по-которому можно обновлять значение в сущности, без передачи ID поля */
code: string;
/** Сортировка поля */
sort: number;
/** ID группы полей, в которой состоит данное поле. Данный ключ возвращается только при получении списка полей контактов, сделок, покупателей и компаний */
group_id: string;
/** Доступно ли поле для редактирования только через API. Данный ключ возвращается только при получении списка полей контактов, сделок и компаний */
is_api_only: boolean;
required_statuses: {
/** ID статуса, для перехода в который данное поле обязательно к заполнению. Данный ключ возвращается только при получении списка полей контактов, сделок и компаний */
status_id: number;
/** ID воронки, для перехода в который данное поле обязательно к заполнению. Данный ключ возвращается только при получении списка полей контактов, сделок и компаний */
pipeline_id: number;
}[] | null;
/** Настройки поля. Данный ключ возвращается только при получении списка полей списков (каталогов) */
settings: any[] | null;
/** Отображается ли поле в интерфейсе списка. Данный ключ возвращается только при получении списка полей списков (каталогов) */
is_visible: boolean;
/** Обязательно ли поле для заполнения при создании элемента списка. Данный ключ возвращается только при получении списка полей списков (каталогов) */
is_required: boolean;
/** Когда напоминать о дне рождения (never – никогда, day – за день, week – за неделю, month – за месяц). Значение данного поля доступно только для поля типа birthday. Данный ключ возвращается только при получении списка полей контактов, сделок и компаний */
remind: string;
/** Доступные значения для поля. Значение данного поля доступно только для полей с поддержкой enum */
enums: Partial<{
/** Значение */
value: string;
/** Сортировка значения */
sort: number;
/** Символьный код значения */
code: string | null;
}>[] | null;
nested: {
/** ID вложенного значения. Данные ключ возвращается только при получении списка полей каталогов и значение доступно только для поля category */
id: number;
/** ID родительского вложенного значения. Данные ключ возвращается только при получении списка полей каталогов и значение доступно только для поля category */
parent_id: number;
/** Значение вложенного значения. Данные ключ возвращается только при получении списка полей каталогов и значение доступно только для поля category */
value: string;
/** Сортировка вложенного значения. Данные ключ возвращается только при получении списка полей каталогов и значение доступно только для поля category */
sort: number;
}[] | null;
/** Сallback js-функция, которая будет выполнена на странице с CRM Plugin и формой amoCRM при отправке данных. Данное значение возвращается для полей типа tracking_data */
tracking_callback: string;
/** Настройка скрытия полей. Поля скрываются в интерфейсе в зависимости от статуса. Данный ключ возвращается только при получении списка полей сделок. */
hidden_statuses: {
/** ID статуса, в котором поле скрыто */
status_id: number;
/** ID воронки, в котором поле скрыто */
pipeline_id: number;
}[];
/** Настройка поля типа chained_list. Данный ключ возвращается только при получении списка полей сделок и покупателей. */
chained_lists: {
/** Модель настройки связанного списка. */
/** Название связанного списка, которое отображается в карточке */
title: string | null;
/** ID каталога */
catalog_id: number;
/** ID родительского каталога */
parent_catalog_id: number;
}[] | null;
/** ID списка или символьный код (contacts, companies, contacts_and_companies) для поля типа Связь с другим элементов (linked_entity). */
search_in: string | null;
/** Код валюты поля. Применимо только для типа поля – monetary. Для других типов полей – null. */
currency: string | null;
}>;
The _embedded property now supports the same entities as the CreateLeadComplex
export type RequestAddComplex = Partial<Pick<Lead, "name" | "price" | "status_id" | "pipeline_id" | "created_by" | "updated_by" | "closed_at" | "created_at" | "updated_at" | "loss_reason_id" | "responsible_user_id" | "custom_fields_values">> & {
_embedded?: {
tags?: {
id?: number;
name?: string;
}[];
contacts?: Partial<Contact>[];
companies?: Partial<Company>[];
metadata?: Partial<UnsrotedMetadataForm> | Partial<UnsrotedMetadataSip>;
source?: {
external_id?: number;
type?: string;
};
};
};
see Leads API
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.