amocrm-web/js/index.js
2025-03-31 02:32:19 +05:00

276 lines
8.5 KiB
JavaScript

const DOMAIN = 'bitheaven.amocrm.ru'
const CLIENT_ID = '8c5dbb32-197d-44ff-87dd-c52fb8ca51d6'
const CLIENT_SECRET = '2DsrI50jwoEMmTXmFCGDGebQ8ULo7PzKHsYNbKZfBGmy8MBEIOiby2HITqssXYIV'
const REDIRECT_URI = 'https://amocrm-web.sectorlambda.ru/'
const ACCESS_TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImNjN2Q4OTE0MjFkM2ZiZGQ2ZjE3ZmFkMGM5MDY1NzUxMWNhMDNlYWQzZTA1OWY4OTM3NmYzMzFmZmFlMjQxZWE0NGEzM2Q1MDY1Yzg2NGZmIn0.eyJhdWQiOiI4YzVkYmIzMi0xOTdkLTQ0ZmYtODdkZC1jNTJmYjhjYTUxZDYiLCJqdGkiOiJjYzdkODkxNDIxZDNmYmRkNmYxN2ZhZDBjOTA2NTc1MTFjYTAzZWFkM2UwNTlmODkzNzZmMzMxZmZhZTI0MWVhNDRhMzNkNTA2NWM4NjRmZiIsImlhdCI6MTc0MzM3MDI1NiwibmJmIjoxNzQzMzcwMjU2LCJleHAiOjE3NDM0NTY2NTYsInN1YiI6IjExOTQ5NDAyIiwiZ3JhbnRfdHlwZSI6IiIsImFjY291bnRfaWQiOjMyMzIyMTI2LCJiYXNlX2RvbWFpbiI6ImFtb2NybS5ydSIsInZlcnNpb24iOjIsInNjb3BlcyI6WyJwdXNoX25vdGlmaWNhdGlvbnMiLCJmaWxlcyIsImNybSIsImZpbGVzX2RlbGV0ZSIsIm5vdGlmaWNhdGlvbnMiXSwiaGFzaF91dWlkIjoiMDQyOWJmZTAtM2VhNC00YTQwLWIwYWUtMzc1M2M5ZWFhMjJiIiwiYXBpX2RvbWFpbiI6ImFwaS1iLmFtb2NybS5ydSJ9.Hw134gR6C6lgKhvKtbVg8WYs6-unAtjbgogpNCCwl6VNY5yxWaRfrOqTinKKFfPkJOCAB1t2LQ1VoqxeOoBB98YEI2PmG6wycTdrKzCBHrvoYY-4LINw-OYXEyqGYPg6jpx__GzzqA52EeTqzXjJyuMz3n50s7EDguRAQhWDVT1HGO4juoCEmUAizLm3bxUusm8p9v98YDJW41s0haZWypgIxbO9EI8fMhkBhUJtsUZEFv2IunCLiotxDjTWS6de02sCb1qAEeIh38W-kSIus3RdtPh1_0_y7YYnWEvJcJxA0SJ168H7LX1iV2cbB00whq7Iey_a9x-36U1XrCnRmw'
const REFRESH_TOKEN = 'def502004fc6f9cc7bceea986593b86fe46cc5655f86b952b9d2ac2602d5f53987921ccce5f07a697c0937099f7f811450d974496f7af36b15dee257cb9b51df1f78bcf9a3d9666c0d26352cd20b693d7fc67dd4053f15d48896f1a6f9397696c44d44ef9263ee571f7123451643493992e0dfa09edd68c1c9677a3563900c91ce951604f9a2bd71b71e9d835e76f100fb59c6fa72875e976f5c5fc8e506d2e69347cd7adb037a17f5d95a6558f86ce6d12cb61e84366aefe9328eb7ec88af48b107c8dcf44e3cba89529471cc70dee7f7d581f50c36f365c6376d372f5573ed957c9e312f9601500d918f836c1917210e66ff8c2ad2d3d9451c02d90823f8cd8305709a78a233e4dc297ec780a4d2236c5389eb6fd9cf7dfe3a570d864aa70bc539b0d27b649fa9c30452ecd0683b34960ec87cfad32d4e12240c9e66deb2f3aeea3b29ae6944a9265e2297443c78f53f960c09c0f841b7b6af916f588fd399fc13dcf870595c0185d03a7d474a2c5762a9fb445c4ee77f3223d4a11d33fe0727d5841b2d3b0738979d8dd9aada3b345a14624316949f02c44f5115deee8cd6f17c4ffad61392b6b800c4fc865d3139cdd0f8dc67fdacad3fa00690662fbcdeb1a3dd72ceb8140b37c0b782e18660af9c5f58f9c38c5c92d1e104447c859db86786c429130eea3dd1da60e312e9'
const TZ_OFFSET = new Date().getTimezoneOffset() / 60
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const time = () => Math.ceil(Date.now() / 1000)
const isToday = timestamp => {
const offsetSeconds = TZ_OFFSET * 3600000 // 60 * 60 * 1000
const date = new Date(timestamp * 1000 + offsetSeconds)
const today = new Date(Date.now() + offsetSeconds)
date.setHours(0, 0, 0, 0)
today.setHours(0, 0, 0, 0)
return date.getTime() === today.getTime()
}
const getDdMmYyyy = timestamp => {
const date = new Date(timestamp * 1000)
let day = date.getDate()
let month = date.getMonth() + 1
const year = date.getFullYear()
if (day < 10) {
day = '0' + day;
}
if (month < 10) {
month = '0' + month;
}
return `${day}.${month}.${year}`
}
const getTokens = async authCode => {
try {
const response = await fetch(`https://${DOMAIN}/oauth2/access_token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'authorization_code',
code: authCode,
redirect_uri: REDIRECT_URI
})
})
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`)
}
const data = await response.json()
return {
access_token: data.access_token,
refresh_token: data.refresh_token
}
} catch (error) {
console.error('Ошибка при получении токенов:', error)
throw error
}
}
const updateTokens = async () => {
try {
const response = await fetch(`https://${DOMAIN}/oauth2/access_token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: REFRESH_TOKEN,
redirect_uri: REDIRECT_URI
})
})
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`)
}
const data = await response.json()
return {
access_token: data.access_token,
refresh_token: data.refresh_token
}
} catch (error) {
console.error('Ошибка при получении токенов:', error)
throw error
}
}
const getContact = async id => {
try {
const response = await fetch(`https://${DOMAIN}/api/v4/contacts/${id}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`)
}
const data = await response.json()
return data
} catch (error) {
console.error('Ошибка при получении контактов:', error)
throw error
}
}
const getLead = async (id) => {
try {
const response = await fetch(`https://${DOMAIN}/api/v4/leads/${id}?with=catalog_elements`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`)
}
const data = await response.json()
return data
} catch (error) {
console.error('Ошибка при получении сделки:', error)
throw error
}
}
const getLeads = async (page = 1, limit = 2) => {
try {
const response = await fetch(`https://${DOMAIN}/api/v4/leads?page=${page}&limit=${limit}&with=contacts&order[id]=desc`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`)
}
const data = await response.json()
const leads = await Promise.all(data._embedded.leads.map(async lead => {
const contact =
lead._embedded.contacts.length
? await getContact(lead._embedded.contacts[0].id)
: null
return {
lead: lead,
contact: contact
}
}))
return leads
} catch (error) {
console.error('Сделок больше нет')
throw error
}
}
const getAllLeads = async () => {
let page = 1
while (true) {
try {
const leads = await getLeads(page++)
console.log(leads)
for (const lead of leads) {
console.log(lead)
const row = document.getElementById('table').insertRow()
row.id = `lead-${lead.lead.id}`
row.classList.add('main-row')
row.onclick = async () => await getLeadInfo(lead.lead.id)
const cell1 = row.insertCell()
const cell2 = row.insertCell()
const cell3 = row.insertCell()
const cell4 = row.insertCell()
const cell5 = row.insertCell()
cell1.textContent = lead.lead.id
cell2.textContent = lead.lead.name
cell3.textContent = lead.lead.price
cell4.textContent = lead.contact?.name
cell5.textContent = lead.contact
?.custom_fields_values[0]
?.values[0]?.value
}
} catch (error) {
document.getElementById('loader').classList.add('none')
console.log(error)
throw error
break
}
await sleep(1000)
}
}
const getLeadInfo = async id => {
const leadDiv = document.getElementById(`lead-${id}`)
const leadDivInfo = document.getElementById(`leadinfo-${id}`)
if (leadDivInfo) {
leadDivInfo.remove()
return
}
leadDiv.classList.add('loading')
const lead = await getLead(id)
const row = document.getElementById('table').insertRow(leadDiv.rowIndex + 1)
row.id = `leadinfo-${id}`
row.onclick = async () => await getLeadInfo(id)
const cell0 = row.insertCell()
const cell1 = row.insertCell()
const cell2 = row.insertCell()
const date = getDdMmYyyy(lead.closest_task_at)
cell1.textContent = `Дата: ${date}`
cell1.colSpan = 2
const status = lead.closest_task_at === null || lead.closest_task_at < time()
? '#f00'
: isToday(lead.closest_task_at)
? '#0f0'
: '#ff0'
const svgns = "http://www.w3.org/2000/svg"
const svg = document.createElementNS(svgns, "svg")
svg.setAttribute("width", "16")
svg.setAttribute("height", "16")
const circle = document.createElementNS(svgns, "circle")
circle.setAttribute("cx", "8")
circle.setAttribute("cy", "8")
circle.setAttribute("r", "8")
circle.setAttribute("fill", status)
svg.appendChild(circle)
cell2.textContent = 'Статус: '
cell2.append(svg)
cell2.colSpan = 2
leadDiv.classList.remove('loading')
}
(async () => {
await getAllLeads()
})()