Files
n8n-workflows/workflows/plane-singularity/update_workflow.py
Striker 531b6039c6 Add Plane→Singularity workflow docs and script
- README: описание репозитория, список workflows
- workflows/plane-singularity/README.md: полная документация по интеграции
  (схема, payload структура, маппинг 28 проектов, Singularity API)
- workflows/plane-singularity/update_workflow.py: скрипт обновления через n8n API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 04:05:11 +03:00

235 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json, urllib.request
N8N_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOTBmYzEwZS1jY2JmLTQwZWQtOWIzYy0wNmIwMGY2N2QwMjEiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwianRpIjoiNGM5M2IwNjMtNWQ3My00MTU0LWIyMjYtYWM0NDFmZmNmYTRiIiwiaWF0IjoxNzc0MjE3MDM2fQ.7uga-4ukI2uJSmRRep2ijlhy6x0I92nFVQl2nxMX1VU"
workflow = {
"name": "Plane → Singularity: назначение задачи",
"nodes": [
{
"id": "1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [240, 300],
"parameters": {
"httpMethod": "POST",
"path": "plane-assignment",
"responseMode": "onReceived",
"responseData": "firstEntryJson"
},
"webhookId": "plane-assignment"
},
{
"id": "2",
"name": "Тип события",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [480, 300],
"parameters": {
"mode": "rules",
"rules": {
"values": [
{
"conditions": {
"options": {"caseSensitive": True, "leftValue": "", "typeValidation": "strict"},
"conditions": [
{
"id": "a1",
"leftValue": "={{ $json.body.activity.field }}",
"rightValue": "assignee_ids",
"operator": {"type": "string", "operation": "equals"}
},
{
"id": "a2",
"leftValue": "={{ JSON.stringify($json.body.activity.new_value) }}",
"rightValue": "f7a5e9db-eaf0-4314-9440-4b28094f5db1",
"operator": {"type": "string", "operation": "contains"}
}
],
"combinator": "and"
},
"renameOutput": True,
"outputKey": "Назначена на меня"
},
{
"conditions": {
"options": {"caseSensitive": True, "leftValue": "", "typeValidation": "strict"},
"conditions": [
{
"id": "b1",
"leftValue": "={{ $json.body.activity.field }}",
"rightValue": "state_id",
"operator": {"type": "string", "operation": "equals"}
},
{
"id": "b2",
"leftValue": "={{ $json.body.data.state.group }}",
"rightValue": "completed",
"operator": {"type": "string", "operation": "equals"}
}
],
"combinator": "and"
},
"renameOutput": True,
"outputKey": "Задача завершена"
}
]
},
"options": {}
}
},
{
"id": "6",
"name": "Подготовить данные",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [720, 160],
"parameters": {
"jsCode": """const data = $json.body.data;
const activity = $json.body.activity;
const priorityMap = {
urgent: 0,
high: 0,
medium: 1,
low: 2,
none: 2
};
const projectMap = {
'0a821ae1-7564-431d-a78c-c76885d85a9a': 'P-b40d30ed-8480-47a0-b0fb-04005eab2028', // DRC - ДРЦ(Нагорное)
'87598e10-0c7d-4612-8a38-073085358580': 'P-00ddf477-f600-44fb-a177-db2af3861dfe', // NAVISCOPE
'1099af67-70b7-4260-90fd-31de5e82e05a': 'P-30340426-ff0e-40b7-af17-070372936101', // NAB - Прочее
'2d35134d-6055-4a88-be9e-a0ee49f018e9': 'P-5a0bd9ad-3df2-4d9c-a7bf-3f59621a2309', // MANYH - ИП Маняхин
'1f4bcaee-1ff7-4227-b160-8aa6561c55e7': 'P-638ec16a-9544-4161-ab39-014ca44772c2', // TCKT - HHIVP Инфра
'74d5b2f9-558c-4371-ab6a-21a082b33933': 'P-63c01156-e0fe-491d-a4f0-82fe4e0c4af3', // CIFRA - ЦифраЦифра
'26cd4aab-cad6-46ae-924b-57d911128506': 'P-39d04293-a3a3-4110-b246-db67dd4729cc', // VEHA - Веха
'66748cf3-1e67-4c7d-9c17-44e90854e7e3': 'P-b1a9d408-635e-4cf2-9b40-b830749fb996', // SINVS - Олимпийский 42а
'309e735f-5fd2-4090-a8ba-df5948532e21': 'P-8624fef6-01ec-44bf-b578-816d90ef56ec', // SHAUS - СтифтерХаус
'404b4c7a-cce0-4126-aa0e-3fd61ff53a14': 'P-ea61bc07-5c60-4b91-99e2-66d4c545338a', // YAR - Ярослав В.
'89133e7f-45d7-44ed-85b9-87f12634b0d4': 'P-e4c50922-162a-445f-a8e1-5621d2ef4124', // SBOR - Старый Большевик
'dd3495bf-3d49-4e30-b614-35e2362fad4f': 'P-cec926a1-8425-42cc-99e1-2079b7b1298f', // SMED - Северное Медведково
'94e0ddd0-200f-4eb7-8f89-a86ec8e63df4': 'P-e73fe9a8-ada5-46d4-9e8a-2ce5b889d95e', // SAMOI - А. Самойлов
'a3906157-53e4-47d0-b3c8-89b807d36ef7': 'P-32c6abd8-2246-4240-b1f0-c85c2a221331', // HG - Сад Здоровья
'ec70a115-6d14-43c9-8572-bd423bcf7289': 'P-281f799e-304b-4a19-bd9d-db0411e4335a', // ROMA - Ромашка
'04427cbb-d108-4558-8aa1-ad7a486e1f9b': 'P-30340426-ff0e-40b7-af17-070372936101', // NIIH - Прочее
'191c0a5b-e998-4503-84c7-aaee67093393': 'P-7377d35f-eb9e-4aa4-828a-1bc01b000624', // LIANZ - Лианозово
'5db759d0-a5fb-453a-90ca-30b0e6355635': 'P-db73597a-47b2-44b3-8282-f9f0e29d52df', // MOIS - ИП Моисеев
'60c0bd07-4867-4c8a-b6e3-b7c78701b36d': 'P-04b65df5-b055-4a30-9bb6-4ffbb3146523', // VONDI - Вондига
'c4cdd5fb-c5c7-4b80-b209-a5d6311090ec': 'P-30340426-ff0e-40b7-af17-070372936101', // 4101 - Прочее
'c6b2531b-7556-4f66-a646-9d4823ee0a82': 'P-30340426-ff0e-40b7-af17-070372936101', // BCOM - Прочее
'91a97d40-b389-49be-9887-0e791a3bb0f9': 'P-62de7005-33fd-4012-b6ac-7ce277cd0b5d', // DELTA - Дельта
'eaef875f-0d90-4828-93af-976199913078': 'P-638ec16a-9544-4161-ab39-014ca44772c2', // WEB - HHIVP Инфра
'51846118-c503-47a3-a94a-88aabf1b950a': 'P-a3f177b1-5078-4e05-9213-32d6af16827f', // REGRU - Домены
'910aa865-a8fa-416a-9c5c-c18cb4d2c653': 'P-05ca29bb-d9f8-4668-891f-92dfbe76fee9', // HARZL - ХарцЛабс
'dfd2a49f-5f5c-48c7-8000-476c56d39f62': 'P-181d5832-3468-4b4a-b1bd-92faa00d580b', // 3DRU - 3Д.РУ
'46f45846-116e-4dba-8ecc-6ba87a5b60f7': 'P-d7a92af2-06bf-4226-a7f4-fc1a2469fe0f', // IVA - Ива
'4df07960-f664-4aba-a757-94a1106c9bae': 'P-638ec16a-9544-4161-ab39-014ca44772c2' // HHVIP - HHIVP Инфра
};
const singularityProjectId = projectMap[data.project] || 'P-91f69023-bdd0-43c7-9caa-7994ec2b8cc8';
const result = {
title: `[${data.project_identifier}-${data.sequence_id}] ${data.name}`,
note: data.description_stripped || '',
priority: priorityMap[data.priority] ?? 1,
projectId: singularityProjectId,
issueName: data.name,
issueIdentifier: `${data.project_identifier}-${data.sequence_id}`
};
if (data.target_date) {
result.deadline = data.target_date;
}
return [{ json: result }];"""
}
},
{
"id": "3",
"name": "Создать задачу в Singularity",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [960, 160],
"parameters": {
"method": "POST",
"url": "https://api.singularity-app.com/v2/task",
"sendHeaders": True,
"headerParameters": {
"parameters": [{"name": "Authorization", "value": "Bearer 11a4eac1-3dd9-4448-99f6-0b3c58315a5d"}]
},
"sendBody": True,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n title: $json.title,\n note: $json.note,\n priority: $json.priority,\n projectId: $json.projectId,\n ...($json.deadline ? { deadline: $json.deadline } : {})\n}) }}"
}
},
{
"id": "4",
"name": "Найти задачу в Singularity",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [720, 440],
"parameters": {
"method": "GET",
"url": "=https://api.singularity-app.com/v2/task?search={{ encodeURIComponent($json.body.data.project_identifier + \"-\" + $json.body.data.sequence_id) }}",
"sendHeaders": True,
"headerParameters": {
"parameters": [{"name": "Authorization", "value": "Bearer 11a4eac1-3dd9-4448-99f6-0b3c58315a5d"}]
}
}
},
{
"id": "5",
"name": "Закрыть задачу в Singularity",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [960, 440],
"parameters": {
"method": "PATCH",
"url": "=https://api.singularity-app.com/v2/task/{{ $json.tasks[0].id }}",
"sendHeaders": True,
"headerParameters": {
"parameters": [{"name": "Authorization", "value": "Bearer 11a4eac1-3dd9-4448-99f6-0b3c58315a5d"}]
},
"sendBody": True,
"contentType": "json",
"body": "{\"complete\": 1}"
}
}
],
"connections": {
"Webhook": {"main": [[{"node": "Тип события", "type": "main", "index": 0}]]},
"Тип события": {
"main": [
[{"node": "Подготовить данные", "type": "main", "index": 0}],
[{"node": "Найти задачу в Singularity", "type": "main", "index": 0}]
]
},
"Подготовить данные": {"main": [[{"node": "Создать задачу в Singularity", "type": "main", "index": 0}]]},
"Найти задачу в Singularity": {"main": [[{"node": "Закрыть задачу в Singularity", "type": "main", "index": 0}]]}
},
"settings": {"executionOrder": "v1"}
}
body = json.dumps(workflow, ensure_ascii=False).encode('utf-8')
req = urllib.request.Request(
"https://n8n.striker.su/api/v1/workflows/zyO77cIUNdbc4xPH",
data=body,
method="PUT",
headers={
"X-N8N-API-KEY": N8N_KEY,
"Content-Type": "application/json; charset=utf-8"
}
)
with urllib.request.urlopen(req) as resp:
result = json.loads(resp.read())
print("OK, id:", result.get("id"))
# Check the switch node conditions
for node in result.get("nodes", []):
if node["name"] == "Тип события":
rules = node["parameters"]["rules"]["values"]
for r in rules:
print(f"Branch '{r['outputKey']}':")
for c in r["conditions"]["conditions"]:
print(f" {c['leftValue']} {c['operator']['operation']} {c['rightValue']}")