Compare commits

...

113 Commits

Author SHA1 Message Date
Kasia
8f09681771 feat: introduce connections page in the admin panel 2023-11-30 14:45:08 +01:00
Ali BARIN
3801f9cfa0 feat: add shareConnection mutation 2023-11-30 14:45:08 +01:00
Ali BARIN
b6eea0b5fc feat: add getSharedConnectionRoleIds query 2023-11-30 14:45:08 +01:00
Ali BARIN
099a8ea2cf feat: add shared connection capability 2023-11-30 14:45:08 +01:00
Ali BARIN
aefff5c861 Merge pull request #1449 from automatisch/AUT-414
feat(zendesk): add new users trigger
2023-11-30 12:55:02 +01:00
Rıdvan Akca
a296b5e645 feat(zendesk): add new users trigger 2023-11-30 14:42:25 +03:00
Ali BARIN
eb486a3a07 Merge pull request #1456 from automatisch/AUT-431
feat(notion): add updated database items trigger
2023-11-29 16:54:45 +01:00
Ali BARIN
062b8521ba Merge pull request #1454 from automatisch/AUT-418
feat(zendesk): add delete user action
2023-11-29 14:37:42 +01:00
Rıdvan Akca
1b07f3195a feat(zendesk): add delete user action 2023-11-29 16:30:05 +03:00
Rıdvan Akca
dfa7d4cb8d feat(notion): add updated database items trigger 2023-11-29 16:18:38 +03:00
Ali BARIN
a14dd9666c Merge pull request #1451 from automatisch/AUT-415
feat(zendesk): add create user action
2023-11-29 14:10:46 +01:00
Rıdvan Akca
b07bd4374f feat(zendesk): add create user action 2023-11-29 16:01:55 +03:00
Ali BARIN
b4e12b0ea8 Merge pull request #1448 from automatisch/AUT-413
feat(zendesk): add delete ticket action
2023-11-29 13:35:58 +01:00
Ali BARIN
ee5c17bb85 Merge pull request #1447 from automatisch/AUT-412
feat(zendesk): add find ticket action
2023-11-29 13:32:29 +01:00
Rıdvan Akca
16c9d3400c feat(zendesk): add delete ticket action 2023-11-29 14:27:54 +03:00
Rıdvan Akca
4dd994348d feat(zendesk): add find ticket action 2023-11-29 13:17:04 +03:00
Ali BARIN
f0cbfafc24 Merge pull request #1443 from automatisch/AUT-411
feat(zendesk): add update ticket action
2023-11-28 15:02:21 +01:00
Ali BARIN
d3f38f5488 Merge pull request #1455 from automatisch/rename-discord-scheduled-event-action
feat(discord/create-scheduled-event): remove new prefix
2023-11-28 13:14:25 +01:00
Ali BARIN
737090a67a feat(discord/create-scheduled-event): remove new prefix 2023-11-28 11:56:36 +00:00
Ali BARIN
4f66a4d090 Merge pull request #1446 from automatisch/AUT-465
feat: embed external fonts used in the codebase
2023-11-28 12:47:32 +01:00
Rıdvan Akca
df54f909c1 feat(zendesk): add update ticket action 2023-11-28 14:40:26 +03:00
Ali BARIN
772b195eca Merge pull request #1441 from automatisch/AUT-409
feat(zendesk): add new tickets trigger
2023-11-28 11:52:22 +01:00
Ali BARIN
87866e34ed Merge pull request #1450 from felifluid/feature/action-discord-create-event
feat(discord): add createEvent action
2023-11-27 18:08:20 +01:00
Ali BARIN
c98ac05097 feat(discord/create-new-scheduled-event): rework fields 2023-11-27 17:01:40 +00:00
DerKlobold
36f991b6f9 feat: discord createNewEvent action in docs
adds a simple entry in the docs for the createNewEvent action
2023-11-26 18:59:01 +01:00
DerKlobold
a81c5164fc feat: discord createNewEvent action
adds the action of creating a new event on a discord server. provides some sort of logic-check to make sure the correct fields have been filled, depending of the type given.
2023-11-26 18:58:09 +01:00
DerKlobold
5942482690 feat: list discord voice channel dynamic data
adds a helper function to provide dynamic data of voice and stage channels of a discord server
2023-11-26 18:56:50 +01:00
Rıdvan Akca
4f538ca2fc feat(zendesk): add new tickets trigger 2023-11-24 15:22:35 +03:00
Kasia
9f2281a3e2 feat: embed external fonts used in the codebase 2023-11-24 12:20:28 +00:00
Ali BARIN
b0d2f28c78 Merge pull request #1444 from automatisch/AUT-487
fix(zendesk): get after_cursor from meta field
2023-11-23 13:42:01 +01:00
Rıdvan Akca
d4380a4426 fix(zendesk): get after_cursor from meta field 2023-11-23 15:17:11 +03:00
Ali BARIN
ae2738d4cc Merge pull request #1435 from mohammedzaher/removebg
feat(removebg): add `remove image background` action
2023-11-22 16:28:55 +01:00
Ali BARIN
aa5ae028b2 feat(removebg/remove-image-background): update wording 2023-11-22 15:01:02 +00:00
Mohammed Zaher
7ab8c76aa0 docs(removebg): Add Remove image background action 2023-11-16 15:43:22 +00:00
Mohammed Zaher
8075b65e14 feat(removebg): Add Remove image background action 2023-11-16 15:39:46 +00:00
Ali BARIN
073ce3bf1b Merge pull request #1433 from automatisch/AUT-445
feat(reddit): provide user-agent header
2023-11-14 17:25:29 +01:00
Rıdvan Akca
80fcbfe01b feat(reddit): provide user-agent header 2023-11-14 14:08:01 +03:00
Ali BARIN
dba0041e5f Merge pull request #1430 from automatisch/dependabot/npm_and_yarn/axios-1.6.0
chore(deps): bump axios from 0.24.0 to 1.6.0
2023-11-13 17:56:57 +01:00
Ali BARIN
b8a44afd25 refactor: re-type interceptors for axios@1.6.0 2023-11-13 16:14:03 +00:00
dependabot[bot]
e2445bf585 chore(deps): bump axios from 0.24.0 to 1.6.0
Bumps [axios](https://github.com/axios/axios) from 0.24.0 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.24.0...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 16:14:03 +00:00
Ali BARIN
50706c524e Merge pull request #1431 from QAComet/qacomet/admin-roles-loader
test: add page title test ids to await and await mounting loader components
2023-11-13 10:52:49 +01:00
QAComet
11e0cb9398 test: add page title test ids to await and await mounting loader components 2023-11-12 16:46:38 -07:00
Ali BARIN
1e82e40802 Merge pull request #1428 from automatisch/AUT-398
feat(reddit): add create link post action
2023-11-10 16:27:25 +01:00
Rıdvan Akca
ff00644e62 feat(reddit): add create link post action 2023-11-10 18:21:04 +03:00
Ali BARIN
97bcd3792b Merge pull request #1427 from automatisch/AUT-397
feat(reddit): add new posts matching search trigger
2023-11-10 16:03:35 +01:00
Rıdvan Akca
5738a09771 feat(reddit): add new posts matching search trigger 2023-11-10 16:50:26 +03:00
kattoczko
c461cc4878 feat: introduce application auth clients tab in the admin panel (#1423)
* feat: introduce application auth clients tab in the admin panel

* feat: introduce improvements

* feat: use loading state returned from useMutation

* feat: use error returned by useMutation hook
2023-11-10 14:09:23 +01:00
Ali BARIN
878fab347a Merge pull request #1426 from automatisch/AUT-396
feat(reddit): add reddit integration
2023-11-10 11:16:46 +01:00
Rıdvan Akca
354b331b08 feat(reddit): add reddit integration 2023-11-10 12:51:52 +03:00
Ali BARIN
3b9aadb90f Merge pull request #1421 from automatisch/AUT-392
feat(xero): add new payments trigger
2023-11-09 16:30:42 +01:00
Ali BARIN
7193d018ce Merge pull request #1425 from automatisch/add-sf-execute-query-in-docs
docs(salesforce): list execute query in actions
2023-11-09 16:09:43 +01:00
Ali BARIN
d5cea034ac docs(salesforce): list execute query in actions 2023-11-09 15:02:58 +00:00
Ömer Faruk Aydın
a2760c10b3 Merge pull request #1422 from automatisch/release/0.10.0
Release v0.10.0
2023-11-09 16:12:19 +03:00
Faruk AYDIN
5492fae213 Release v0.10.0 2023-11-09 15:48:33 +03:00
Faruk AYDIN
490a23ae0a chore: Update version to 0.10.0 in Dockerfiles 2023-11-09 15:46:42 +03:00
Rıdvan Akca
3593cf3808 feat(xero): add new payments trigger 2023-11-09 11:57:47 +03:00
Ali BARIN
6ea7400ff4 Merge pull request #1419 from automatisch/AUT-391
feat(xero): add new bank transactions trigger
2023-11-08 17:07:14 +01:00
Rıdvan Akca
1a4ba35ef4 feat(xero): add new bank transactions trigger 2023-11-08 18:59:50 +03:00
Ali BARIN
2d52cab693 fix: let permitted users delete others' flows (#1417) 2023-11-08 16:55:30 +01:00
Ömer Faruk Aydın
e1fac78aba Merge pull request #1412 from automatisch/aut-311
fix: let permitted users create step in not-owned flows
2023-11-08 17:55:53 +03:00
Ali BARIN
e79fc9cae4 Merge pull request #1418 from automatisch/AUT-390
feat(xero): add xero integration
2023-11-08 15:37:03 +01:00
Rıdvan Akca
9200e1011b feat(xero): add xero integration 2023-11-08 17:03:33 +03:00
moaaz
373d29eeab docs(carbone): Add add-template actions 2023-11-08 11:27:17 +01:00
moaaz
bc337c588a feat(carbone): add add-template action 2023-11-08 11:27:17 +01:00
QAComet
112b05f7ad test: add wait for roles loader to detach 2023-11-08 10:39:02 +01:00
Moaaz Elsayed
9f84af95f6 docs(removebg): fix connection link (#1416) 2023-11-08 10:03:34 +01:00
Ali BARIN
0873cfa997 fix: let permitted users create step in not-owned flows 2023-11-07 15:51:24 +00:00
Ali BARIN
94d7162782 docs(carbone): list in available apps (#1411) 2023-11-07 15:30:47 +01:00
Mohammed Zaher
5db62679fa feat(removebg): add remove-bg integration (#1406)
* feat(remove-bg): add remove-bg integration

* feat(removebg): update name and icon

* docs(removebg): update name and icon

* docs: add remove.bg in available apps

* docs(removebg): correct path

---------

Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-11-07 13:52:55 +01:00
Moaaz Elsayed
a4a0102679 feat(carbone): add carbone integration (#1405)
* feat(carbone): add carbone integration

* Update list-apps.spec.js to ensure Carbone is the first application in the alphabetically sorted list

Previously, the test expected DeepL to be the first in the list. Now, this change ensures that Carbone will take precedence as it will come first in the alphabetical order.
2023-11-07 13:00:22 +01:00
Rıdvan Akca
2afcfbb4bc feat(ghost): add new post published trigger (#1403) 2023-11-07 12:55:35 +01:00
Rıdvan Akca
f0e8f070a8 feat(ghost): add ghost integration (#1401) 2023-11-07 11:30:26 +01:00
Rıdvan Akca
c42374e031 feat(notion/create-page): support variables in parentPageId field (#1410) 2023-11-07 09:33:57 +01:00
Rıdvan Akca
be610c7fa9 feat(zendesk): add create ticket action 2023-11-06 16:02:26 +01:00
Rıdvan Akca
4ff824663b test: skip admin role is not deletable 2023-11-06 14:21:19 +01:00
QAComet
1581b5ac0a test: write tests for role management (#1396) 2023-11-06 10:35:20 +01:00
Ömer Faruk Aydın
5fb48ed54b Merge pull request #1402 from automatisch/tests/get-flow
test: Implement graphQL getFlow query tests
2023-10-31 16:12:26 +01:00
Faruk AYDIN
903e9e6093 test: Implement graphQL getFlow query tests 2023-10-31 15:57:44 +01:00
Faruk AYDIN
d30e491817 test: Delete formattedData from connection factory before persisting 2023-10-31 15:57:22 +01:00
Faruk AYDIN
aa727e3260 test: Adjust step factory to assign correct appKey 2023-10-31 15:56:49 +01:00
Ömer Faruk Aydın
1cad3a7149 Merge pull request #1398 from automatisch/tests/disable-typescript
test: Disable ts check for test files
2023-10-31 12:08:30 +01:00
Ali BARIN
3b7f6740bb chore: exclude test files from eslint 2023-10-31 11:35:53 +01:00
Ali BARIN
2febc5efad fix(mutations/delete-flow): cover incomplete trigger 2023-10-30 15:19:31 +01:00
Faruk AYDIN
903616bef6 test: Disable ts check for test files 2023-10-30 14:25:22 +01:00
Rıdvan Akca
c944193fb4 feat(trello): add create card action 2023-10-30 11:48:19 +01:00
Ömer Faruk Aydın
4f2155ea63 Merge pull request #1394 from automatisch/tests/coverage
test: Show coverage for graphQL queries folder
2023-10-30 10:23:42 +01:00
Faruk AYDIN
4bda1edda7 test: Show coverage for graphQL queries folder 2023-10-29 13:13:01 +01:00
QAComet
1a55cc8604 test: update snackbar with variant and data-test attributes 2023-10-27 19:48:40 +02:00
Ali BARIN
bf7ab475ee feat: unregister webhook upon deleting flow 2023-10-27 19:19:13 +02:00
Ömer Faruk Aydın
2f39efb935 Merge pull request #1381 from automatisch/add-filters-in-get-executions-query
feat(queries/get-executions): add filter support
2023-10-26 17:56:10 +02:00
Ali BARIN
9f8eb985e4 refactor: assert entry dates as string 2023-10-26 15:43:11 +00:00
Ali BARIN
3549fef71c feat(ExecutionRow): use createdAt instead of updatedAt 2023-10-26 15:43:10 +00:00
Ali BARIN
2cfa64c2a3 test(queries/get-executions): use createdAt in filter test cases 2023-10-26 15:43:08 +00:00
Ali BARIN
7245a0a599 refactor(queries/get-executions): use createdAt in filters 2023-10-26 15:29:28 +00:00
Ali BARIN
0633da3244 fix(mutations/duplicate-flow): correct webhook path 2023-10-26 17:21:28 +02:00
Faruk AYDIN
96341976f5 test: Add graphQL query test for getExecutions 2023-10-26 17:19:37 +02:00
Faruk AYDIN
9abfaec4d5 test: Adjust permission factory to pass all values 2023-10-26 17:17:12 +02:00
Faruk AYDIN
945c52dd6b test: Add createdAt and updatedAt defaultst to execution and flow 2023-10-26 17:16:42 +02:00
Faruk AYDIN
6567d24760 test: Adjust app key of step depending on type 2023-10-26 17:15:50 +02:00
Faruk AYDIN
ffaf9b6e0c chore: Add date types to IFlow and IExecution 2023-10-26 17:15:10 +02:00
QAComet
463e6908b1 test: write tests for user management (#1316)
* chore: add data-test attributes

* test: add github connection test, add applications modal

* test: write tests for user management
2023-10-26 15:12:37 +02:00
Ali BARIN
e185ceb385 fix(queries/get-executions): recover flow and steps relations 2023-10-26 13:52:20 +02:00
Ali BARIN
1b21bbe5b7 feat(queries/get-executions): accept timestamp instead of ISO datetime 2023-10-26 13:52:20 +02:00
Ali BARIN
14b7053ed8 feat: add updated_at index in executions 2023-10-26 13:52:20 +02:00
Ali BARIN
2760526def feat: add flow_id index in executions 2023-10-26 13:52:20 +02:00
Ali BARIN
d851db22d0 feat(queries/get-executions): add updatedAt filter support 2023-10-26 13:52:20 +02:00
Ali BARIN
2fa360e400 feat(queries/get-executions): add status filter support 2023-10-26 13:52:20 +02:00
Ali BARIN
e4eb146169 feat(queries/get-executions): add flowId filter support 2023-10-26 13:52:20 +02:00
Moaaz Elsayed
86611453b5 feat(zendesk): add zendesk integration (#1385)
* feat(zendesk): add zendesk integration

* Add Zendesk connection documentation

* docs(zendesk/connection): add missing steps

* feat(zendesk): add more auth scopes for planned triggers/actions

* fix(zendesk): fix instanceUrl

---------

Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-10-26 12:47:13 +02:00
dependabot[bot]
65f9d1b6b9 chore(deps): bump crypto-js from 4.1.1 to 4.2.0
Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0.
- [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: crypto-js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 10:00:04 +02:00
QAComet
2fceaf2cf4 test: fix flakiness in GH connection test case (#1383) 2023-10-25 11:04:04 +02:00
Ömer Faruk Aydın
d82b50fcdb Merge pull request #1382 from automatisch/fix/delete-current-user
fix: Guard lowercase email for delete user operation
2023-10-25 01:17:52 +02:00
Faruk AYDIN
ab6e49bf4f fix: Guard lowercase email for delete user operation 2023-10-25 01:00:43 +02:00
276 changed files with 9543 additions and 511 deletions

View File

@@ -7,4 +7,12 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'prettier',
],
overrides: [
{
files: ['**/*.test.ts', '**/test/**/*.ts'],
rules: {
'@typescript-eslint/ban-ts-comment': ['off'],
},
},
],
};

View File

@@ -4,7 +4,7 @@ WORKDIR /automatisch
RUN \
apk --no-cache add --virtual build-dependencies python3 build-base && \
yarn global add @automatisch/cli@0.9.3 --network-timeout 1000000 && \
yarn global add @automatisch/cli@0.10.0 --network-timeout 1000000 && \
rm -rf /usr/local/share/.cache/ && \
apk del build-dependencies

View File

@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM automatischio/automatisch:0.9.3
FROM automatischio/automatisch:0.10.0
WORKDIR /automatisch
RUN apk add --no-cache openssl dos2unix

View File

@@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.9.3",
"version": "0.10.0",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {

View File

@@ -4,4 +4,6 @@ module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['./test/setup/global-hooks.ts'],
globalTeardown: './test/setup/global-teardown.ts',
collectCoverage: true,
collectCoverageFrom: ['src/graphql/queries/*.ts'],
};

View File

@@ -1,6 +1,6 @@
{
"name": "@automatisch/backend",
"version": "0.9.3",
"version": "0.10.0",
"license": "See LICENSE file",
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
"scripts": {
@@ -23,7 +23,7 @@
"prebuild": "rm -rf ./dist"
},
"dependencies": {
"@automatisch/web": "^0.9.3",
"@automatisch/web": "^0.10.0",
"@bull-board/express": "^3.10.1",
"@casl/ability": "^6.5.0",
"@graphql-tools/graphql-file-loader": "^7.3.4",
@@ -38,7 +38,7 @@
"@types/xmlrpc": "^1.3.7",
"accounting": "^0.4.1",
"ajv-formats": "^2.1.1",
"axios": "0.24.0",
"axios": "1.6.0",
"bcrypt": "^5.0.1",
"bullmq": "^3.0.0",
"copyfiles": "^2.4.1",
@@ -59,8 +59,8 @@
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"jsonwebtoken": "^9.0.0",
"knex": "^2.4.0",
"libphonenumber-js": "^1.10.48",
"knex": "^2.5.1",
"lodash.get": "^4.4.2",
"luxon": "2.5.2",
"memory-cache": "^0.2.0",
@@ -69,7 +69,7 @@
"node-html-markdown": "^1.3.0",
"nodemailer": "6.7.0",
"oauth-1.0a": "^2.2.6",
"objection": "^3.0.0",
"objection": "^3.1.1",
"passport": "^0.6.0",
"pg": "^8.7.1",
"php-serialize": "^4.0.2",
@@ -116,7 +116,7 @@
"url": "https://github.com/automatisch/automatisch/issues"
},
"devDependencies": {
"@automatisch/types": "^0.9.3",
"@automatisch/types": "^0.10.0",
"@faker-js/faker": "^8.1.0",
"@types/bcrypt": "^5.0.0",
"@types/bull": "^3.15.8",

View File

@@ -0,0 +1,35 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Add Template',
key: 'addTemplate',
description:
'Creates an attachment of a specified object by given parent ID.',
arguments: [
{
label: 'Templete Data',
key: 'templateData',
type: 'string' as const,
required: true,
variables: true,
description: 'The content of your new Template in XML/HTML format.',
},
],
async run($) {
const templateData = $.step.parameters.templateData as string;
const base64Data = Buffer.from(templateData).toString('base64');
const dataURI = `data:application/xml;base64,${base64Data}`;
const body = JSON.stringify({ template: dataURI });
const response = await $.http.post('/template', body, {
headers: {
'Content-Type': 'application/json',
},
});
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,3 @@
import addTemplate from './add-template';
export default [addTemplate];

View File

@@ -0,0 +1,444 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 1173 1173" enable-background="new 0 0 1173 1173" xml:space="preserve">
<path fill="#73348B" opacity="1.000000" stroke="none"
d="
M1174.000000,208.000000
C1174.000000,529.969421 1174.000000,851.938782 1174.000000,1173.954102
C783.067810,1173.954102 392.135559,1173.954102 1.101677,1173.954102
C1.101677,783.104980 1.101677,392.209930 1.101677,1.000000
C322.697510,1.000000 644.395142,1.000000 966.516235,1.467920
C966.918274,68.235474 966.895691,134.535110 966.877258,200.834747
C966.875183,208.314804 966.885986,208.322311 974.623108,208.322571
C1038.924072,208.324631 1103.225098,208.326889 1167.526123,208.310104
C1169.684082,208.309540 1171.842041,208.107910 1174.000000,208.000000
M824.469116,403.972260
C830.640320,396.844513 836.811584,389.716736 843.084839,382.471130
C813.026611,353.649353 780.256958,329.661011 741.486328,314.979523
C680.713013,291.966217 618.102173,287.454407 554.867737,301.133331
C463.817413,320.829376 397.312988,373.276550 356.951416,457.320282
C326.804779,520.093689 319.957764,586.465027 335.155365,654.111511
C358.404633,757.597168 419.946594,829.052856 520.224731,865.190979
C575.706055,885.185242 632.887451,885.395691 690.185303,873.820862
C753.158081,861.099548 803.501465,826.733521 847.328979,780.124756
C825.522522,758.991638 803.799194,737.939087 781.866882,716.683960
C775.039185,722.581055 767.314697,729.376404 759.459290,736.016846
C716.790833,772.086792 668.088501,791.050049 611.649048,788.182129
C555.974548,785.353088 510.796692,761.934998 476.366638,718.347046
C450.585236,685.708313 437.160706,648.031494 433.752258,606.690613
C430.193268,563.523926 437.309692,522.406311 457.904602,484.052490
C496.945190,411.347198 573.378845,374.580780 654.712402,388.765686
C691.388062,395.162079 723.361389,411.383057 752.404175,434.001740
C761.432556,441.033173 770.043213,448.601013 779.130859,456.160980
C794.123657,438.910675 809.069336,421.714569 824.469116,403.972260
M401.126434,1003.500000
C401.122375,996.333740 401.039398,989.166199 401.152191,982.001770
C401.198700,979.046997 400.149170,977.760315 397.091583,977.847290
C392.096008,977.989441 387.061035,978.231079 382.102264,977.778320
C376.825531,977.296387 375.754608,979.441406 375.775360,984.315125
C375.949005,1025.145630 375.873474,1065.977417 375.874237,1106.808716
C375.874329,1113.141724 375.994049,1119.477539 375.836487,1125.806519
C375.755646,1129.053101 376.993805,1130.258667 380.217560,1130.162231
C385.712311,1129.997559 391.222198,1129.953491 396.712982,1130.175049
C400.378265,1130.322998 401.327179,1128.650757 401.160339,1125.375610
C400.987305,1121.979004 401.121826,1118.566772 401.121826,1114.051758
C403.029083,1115.849609 404.038116,1116.655273 404.875366,1117.611572
C423.297913,1138.649780 456.085083,1135.565308 472.987305,1118.665161
C494.109619,1097.545654 495.745544,1057.582886 476.289124,1034.845947
C458.642456,1014.223816 423.619873,1010.312439 404.695007,1033.663574
C403.933044,1034.603882 402.896790,1035.321899 401.126709,1036.922485
C401.126709,1025.377563 401.126709,1014.938782 401.126434,1003.500000
M1021.364685,1102.039429
C1034.668091,1125.202515 1054.950562,1134.733032 1081.114746,1132.114380
C1116.971191,1128.525757 1140.394165,1093.509888 1130.352783,1058.871704
C1120.842163,1026.064697 1084.911865,1009.039368 1051.827393,1021.662964
C1020.072083,1033.779419 1006.094421,1069.964478 1021.364685,1102.039429
M513.859009,1053.461792
C508.981232,1065.614258 508.319275,1078.089722 511.612946,1090.667114
C518.928467,1118.602539 545.601379,1135.665771 576.077942,1132.138428
C615.868713,1127.533081 639.243103,1085.250000 621.290771,1049.613525
C609.925598,1027.053101 590.627502,1017.015625 565.767517,1017.961548
C541.582703,1018.881775 524.357849,1030.779541 513.859009,1053.461792
M853.945312,1105.526489
C844.979370,1111.390137 835.234131,1113.082764 824.789001,1110.928833
C812.187134,1108.330078 801.815186,1096.424561 801.983032,1084.128296
C803.915649,1084.128296 805.872803,1084.128418 807.829956,1084.128296
C827.828918,1084.128174 847.827942,1084.128174 867.826904,1084.128052
C883.022278,1084.128052 883.416565,1084.089355 881.911011,1068.743774
C877.873352,1027.588623 845.873840,1010.303040 812.589294,1020.461792
C788.140564,1027.923828 771.315674,1053.597778 776.625183,1086.417480
C782.995667,1125.795166 822.767029,1141.211426 855.517578,1128.350586
C864.166870,1124.953979 871.321899,1119.494873 877.282959,1111.812744
C872.197815,1107.281738 867.508911,1103.103882 862.714783,1098.832153
C859.777344,1101.088501 857.158447,1103.100098 853.945312,1105.526489
M183.035294,1022.439941
C178.726364,1024.044678 174.417435,1025.649414 169.759094,1027.384277
C172.080597,1034.339355 174.168320,1040.594116 176.396927,1047.270874
C178.710007,1046.498047 180.428452,1045.982422 182.104675,1045.354126
C192.414581,1041.489502 203.049866,1039.981567 214.012650,1041.017212
C228.197418,1042.357178 237.230057,1053.265137 234.370621,1065.622192
C233.945877,1065.733521 233.456528,1066.014160 233.019272,1065.948486
C231.544067,1065.726807 230.081848,1065.406616 228.624466,1065.080322
C213.432663,1061.679199 198.251007,1060.834473 183.260117,1065.925537
C168.246078,1071.024536 160.203674,1082.376099 160.159256,1097.991577
C160.116211,1113.124634 168.617416,1125.277100 183.512024,1129.868774
C201.272339,1135.343872 217.762161,1132.975952 231.922165,1119.980591
C232.681229,1119.283936 233.598526,1118.759888 234.998825,1117.756470
C234.998825,1120.660034 235.167923,1122.828491 234.961670,1124.960815
C234.595810,1128.743408 235.836517,1130.435547 239.938004,1130.198853
C245.253296,1129.892090 250.601852,1130.021362 255.931290,1130.154541
C258.904449,1130.228760 260.057526,1129.146118 260.038300,1126.091797
C259.899628,1104.095337 260.293976,1082.087280 259.708466,1060.104492
C259.149353,1039.110596 247.823425,1025.003418 228.752792,1020.447083
C213.662979,1016.841919 198.651810,1018.154236 183.035294,1022.439941
M652.674988,1084.500000
C652.676819,1097.990356 652.774353,1111.481812 652.619568,1124.970337
C652.575684,1128.797485 653.756165,1130.430420 657.763428,1130.184448
C662.243225,1129.909424 666.753052,1130.130493 671.249817,1130.123413
C677.988098,1130.112915 677.994995,1130.108398 677.998840,1123.280029
C678.009644,1104.127197 677.813293,1084.971313 678.088196,1065.822388
C678.289795,1051.782104 687.650574,1041.728882 700.475464,1040.969238
C714.725037,1040.125244 723.562256,1047.280884 725.706665,1061.495361
C726.002808,1063.458374 725.985596,1065.479614 725.987732,1067.474121
C726.007690,1086.627075 726.075562,1105.780396 725.937012,1124.932373
C725.910034,1128.659790 726.763428,1130.436401 730.909912,1130.197876
C736.388123,1129.882690 741.898499,1130.048462 747.393127,1130.132080
C750.111389,1130.173340 751.333618,1129.085205 751.318970,1126.315796
C751.187561,1101.504028 751.651367,1076.672607 750.778564,1051.887817
C750.329590,1039.136963 743.986084,1028.674927 732.321106,1022.254761
C716.863159,1013.746887 695.690918,1017.041931 683.479919,1029.807129
C681.911743,1031.446533 680.418274,1033.157227 677.994934,1035.817871
C677.994934,1031.179810 677.847046,1027.753906 678.037964,1024.347046
C678.215881,1021.172424 677.140076,1019.820496 673.800659,1019.952393
C668.645813,1020.156006 663.460022,1020.260315 658.319153,1019.922363
C653.942261,1019.634521 652.544373,1021.128174 652.590515,1025.542358
C652.792175,1044.859741 652.675354,1064.180542 652.674988,1084.500000
M119.317238,1046.101074
C122.268578,1048.446411 125.219917,1050.791626 127.387344,1052.514038
C132.109711,1048.247925 136.374313,1044.289062 140.759842,1040.468872
C144.010681,1037.637085 143.275986,1035.555298 140.351669,1032.810791
C125.930077,1019.275879 108.562424,1015.442383 89.835030,1018.619995
C58.671745,1023.907654 39.333969,1050.496826 43.188984,1083.193359
C45.926426,1106.411377 59.195229,1122.338257 81.478561,1129.875244
C103.477272,1137.315918 128.440613,1130.652588 142.387802,1114.456543
C144.090958,1112.478760 144.008331,1111.254517 142.279175,1109.447876
C138.256943,1105.245117 134.418365,1100.866577 130.299011,1096.332031
C121.771889,1103.853394 113.867172,1109.740845 103.144859,1110.191895
C92.244728,1110.650269 83.239876,1107.153076 76.138634,1098.826416
C65.223991,1086.028564 65.297775,1064.157593 76.319031,1051.184937
C87.046638,1038.557983 103.476791,1036.430054 119.317238,1046.101074
M353.950317,1043.008423
C354.006683,1036.186401 353.906464,1029.358154 354.180176,1022.544922
C354.335571,1018.677124 352.771240,1017.653687 349.151764,1017.919495
C335.330078,1018.934265 325.588013,1026.116211 318.604279,1037.643921
C317.750885,1039.052734 316.890594,1040.457275 316.033447,1041.863770
C315.121674,1035.918945 314.900055,1030.423950 315.194824,1024.956665
C315.413696,1020.897034 313.799438,1019.733093 309.981384,1019.938782
C305.168030,1020.198242 300.312347,1020.256714 295.507935,1019.924500
C291.198883,1019.626587 289.697205,1020.959900 289.777374,1025.465820
C290.040833,1040.276245 289.874969,1055.094116 289.875153,1069.909180
C289.875397,1088.219849 289.984222,1106.531616 289.796387,1124.840332
C289.753448,1129.024658 291.144775,1130.494019 295.243988,1130.181030
C298.884857,1129.903076 302.564056,1130.154053 306.225861,1130.119141
C316.127838,1130.024902 314.970276,1131.203003 315.137146,1121.384644
C315.383026,1106.919067 314.932556,1092.386353 316.201233,1078.007690
C317.784821,1060.059814 328.779388,1048.418091 344.942749,1045.376465
C347.832062,1044.832642 350.733795,1044.354858 353.950317,1043.008423
M970.561462,1130.149170
C976.347717,1130.149170 982.133972,1130.149170 988.190430,1130.149170
C988.190430,1093.234131 988.190430,1056.859741 988.190430,1020.333618
C979.702515,1020.333618 971.488037,1020.333618 963.325317,1020.333618
C963.325317,1057.058838 963.325317,1093.449585 963.325317,1130.149170
C965.622131,1130.149170 967.600586,1130.149170 970.561462,1130.149170
M931.087708,1129.856323
C931.087708,1120.373291 931.087708,1110.890259 931.087708,1101.511963
C922.010315,1101.511963 913.093201,1101.511963 904.095581,1101.511963
C904.095581,1111.126099 904.095581,1120.382812 904.095581,1130.103516
C912.953064,1130.103516 921.562012,1130.103516 931.087708,1129.856323
M989.143494,1000.348511
C989.143494,993.401855 989.143494,986.455139 989.143494,979.396606
C979.834900,979.396606 970.989380,979.396606 962.400879,979.396606
C962.400879,987.527710 962.400879,995.258179 962.400879,1003.146362
C970.452820,1003.146362 978.106445,1003.221008 985.755371,1003.064941
C986.887451,1003.041870 987.997314,1001.928955 989.143494,1000.348511
z"/>
<path fill="#B497C6" opacity="1.000000" stroke="none"
d="
M1174.000000,207.750000
C1171.842041,208.107910 1169.684082,208.309540 1167.526123,208.310104
C1103.225098,208.326889 1038.924072,208.324631 974.623108,208.322571
C966.885986,208.322311 966.875183,208.314804 966.877258,200.834747
C966.895691,134.535110 966.918274,68.235474 966.969849,1.467920
C967.000000,1.000000 967.500000,1.000000 967.892212,1.300326
C968.620972,1.969603 968.943176,2.352815 969.295898,2.705561
C1036.645020,70.060425 1103.995605,137.413895 1171.356201,204.757263
C1172.170776,205.571564 1173.115845,206.255310 1174.000000,207.000000
C1174.000000,207.000000 1174.000000,207.500000 1174.000000,207.750000
z"/>
<path fill="#FFFFFF" opacity="1.000000" stroke="none"
d="
M1174.000000,206.533020
C1173.115845,206.255310 1172.170776,205.571564 1171.356201,204.757263
C1103.995605,137.413895 1036.645020,70.060425 969.295898,2.705561
C968.943176,2.352815 968.620972,1.969603 968.142212,1.300326
C1036.594238,1.000000 1105.188599,1.000000 1174.000000,1.000000
C1174.000000,69.355194 1174.000000,137.710617 1174.000000,206.533020
z"/>
<path fill="#FFFFFF" opacity="1.000000" stroke="none"
d="
M824.242065,404.245361
C809.069336,421.714569 794.123657,438.910675 779.130859,456.160980
C770.043213,448.601013 761.432556,441.033173 752.404175,434.001740
C723.361389,411.383057 691.388062,395.162079 654.712402,388.765686
C573.378845,374.580780 496.945190,411.347198 457.904602,484.052490
C437.309692,522.406311 430.193268,563.523926 433.752258,606.690613
C437.160706,648.031494 450.585236,685.708313 476.366638,718.347046
C510.796692,761.934998 555.974548,785.353088 611.649048,788.182129
C668.088501,791.050049 716.790833,772.086792 759.459290,736.016846
C767.314697,729.376404 775.039185,722.581055 781.866882,716.683960
C803.799194,737.939087 825.522522,758.991638 847.328979,780.124756
C803.501465,826.733521 753.158081,861.099548 690.185303,873.820862
C632.887451,885.395691 575.706055,885.185242 520.224731,865.190979
C419.946594,829.052856 358.404633,757.597168 335.155365,654.111511
C319.957764,586.465027 326.804779,520.093689 356.951416,457.320282
C397.312988,373.276550 463.817413,320.829376 554.867737,301.133331
C618.102173,287.454407 680.713013,291.966217 741.486328,314.979523
C780.256958,329.661011 813.026611,353.649353 843.084839,382.471130
C836.811584,389.716736 830.640320,396.844513 824.242065,404.245361
z"/>
<path fill="#B496C6" opacity="1.000000" stroke="none"
d="
M401.126587,1004.000000
C401.126709,1014.938782 401.126709,1025.377563 401.126709,1036.922485
C402.896790,1035.321899 403.933044,1034.603882 404.695007,1033.663574
C423.619873,1010.312439 458.642456,1014.223816 476.289124,1034.845947
C495.745544,1057.582886 494.109619,1097.545654 472.987305,1118.665161
C456.085083,1135.565308 423.297913,1138.649780 404.875366,1117.611572
C404.038116,1116.655273 403.029083,1115.849609 401.121826,1114.051758
C401.121826,1118.566772 400.987305,1121.979004 401.160339,1125.375610
C401.327179,1128.650757 400.378265,1130.322998 396.712982,1130.175049
C391.222198,1129.953491 385.712311,1129.997559 380.217560,1130.162231
C376.993805,1130.258667 375.755646,1129.053101 375.836487,1125.806519
C375.994049,1119.477539 375.874329,1113.141724 375.874237,1106.808716
C375.873474,1065.977417 375.949005,1025.145630 375.775360,984.315125
C375.754608,979.441406 376.825531,977.296387 382.102264,977.778320
C387.061035,978.231079 392.096008,977.989441 397.091583,977.847290
C400.149170,977.760315 401.198700,979.046997 401.152191,982.001770
C401.039398,989.166199 401.122375,996.333740 401.126587,1004.000000
M401.102875,1069.011719
C400.098053,1078.886963 401.329559,1088.256470 407.101685,1096.684570
C420.591766,1116.381714 449.957458,1114.592529 459.843262,1092.900513
C462.811829,1086.386719 463.929352,1078.394409 463.659058,1071.163086
C462.954895,1052.325073 448.612427,1039.179199 431.024689,1039.892334
C416.229248,1040.492432 405.168213,1050.977661 401.102875,1069.011719
z"/>
<path fill="#FEFDFE" opacity="1.000000" stroke="none"
d="
M1021.202576,1101.701294
C1006.094421,1069.964478 1020.072083,1033.779419 1051.827393,1021.662964
C1084.911865,1009.039368 1120.842163,1026.064697 1130.352783,1058.871704
C1140.394165,1093.509888 1116.971191,1128.525757 1081.114746,1132.114380
C1054.950562,1134.733032 1034.668091,1125.202515 1021.202576,1101.701294
M1063.986572,1041.223145
C1048.638306,1046.359863 1041.596069,1057.693604 1040.531982,1073.203491
C1038.714111,1099.699341 1064.766113,1118.165283 1088.315552,1107.193970
C1103.746460,1100.005005 1111.074951,1080.915771 1105.178711,1063.268677
C1099.385498,1045.930298 1083.597778,1037.239746 1063.986572,1041.223145
z"/>
<path fill="#B396C5" opacity="1.000000" stroke="none"
d="
M513.996948,1053.104736
C524.357849,1030.779541 541.582703,1018.881775 565.767517,1017.961548
C590.627502,1017.015625 609.925598,1027.053101 621.290771,1049.613525
C639.243103,1085.250000 615.868713,1127.533081 576.077942,1132.138428
C545.601379,1135.665771 518.928467,1118.602539 511.612946,1090.667114
C508.319275,1078.089722 508.981232,1065.614258 513.996948,1053.104736
M562.260620,1040.444092
C546.615784,1043.979858 537.547485,1054.070801 535.297913,1070.447632
C533.121765,1086.289917 541.953796,1102.308838 555.941284,1107.888672
C570.341492,1113.633301 586.898376,1109.268066 595.135681,1096.926514
C603.733704,1084.044556 604.355286,1070.265991 596.774170,1056.799438
C589.497620,1043.873901 577.771179,1038.791748 562.260620,1040.444092
z"/>
<path fill="#B396C5" opacity="1.000000" stroke="none"
d="
M854.242432,1105.319092
C857.158447,1103.100098 859.777344,1101.088501 862.714783,1098.832153
C867.508911,1103.103882 872.197815,1107.281738 877.282959,1111.812744
C871.321899,1119.494873 864.166870,1124.953979 855.517578,1128.350586
C822.767029,1141.211426 782.995667,1125.795166 776.625183,1086.417480
C771.315674,1053.597778 788.140564,1027.923828 812.589294,1020.461792
C845.873840,1010.303040 877.873352,1027.588623 881.911011,1068.743774
C883.416565,1084.089355 883.022278,1084.128052 867.826904,1084.128052
C847.827942,1084.128174 827.828918,1084.128174 807.829956,1084.128296
C805.872803,1084.128418 803.915649,1084.128296 801.983032,1084.128296
C801.815186,1096.424561 812.187134,1108.330078 824.789001,1110.928833
C835.234131,1113.082764 844.979370,1111.390137 854.242432,1105.319092
M807.682800,1067.153442
C823.922729,1067.153442 840.162659,1067.153442 856.360352,1067.153442
C857.024231,1051.490723 843.821167,1038.060181 828.868347,1038.415405
C814.209839,1038.763672 800.809204,1052.746582 801.885498,1066.903320
C803.478333,1066.985229 805.112427,1067.069336 807.682800,1067.153442
z"/>
<path fill="#B396C6" opacity="1.000000" stroke="none"
d="
M183.418549,1022.317383
C198.651810,1018.154236 213.662979,1016.841919 228.752792,1020.447083
C247.823425,1025.003418 259.149353,1039.110596 259.708466,1060.104492
C260.293976,1082.087280 259.899628,1104.095337 260.038300,1126.091797
C260.057526,1129.146118 258.904449,1130.228760 255.931290,1130.154541
C250.601852,1130.021362 245.253296,1129.892090 239.938004,1130.198853
C235.836517,1130.435547 234.595810,1128.743408 234.961670,1124.960815
C235.167923,1122.828491 234.998825,1120.660034 234.998825,1117.756470
C233.598526,1118.759888 232.681229,1119.283936 231.922165,1119.980591
C217.762161,1132.975952 201.272339,1135.343872 183.512024,1129.868774
C168.617416,1125.277100 160.116211,1113.124634 160.159256,1097.991577
C160.203674,1082.376099 168.246078,1071.024536 183.260117,1065.925537
C198.251007,1060.834473 213.432663,1061.679199 228.624466,1065.080322
C230.081848,1065.406616 231.544067,1065.726807 233.019272,1065.948486
C233.456528,1066.014160 233.945877,1065.733521 234.370621,1065.622192
C237.230057,1053.265137 228.197418,1042.357178 214.012650,1041.017212
C203.049866,1039.981567 192.414581,1041.489502 182.104675,1045.354126
C180.428452,1045.982422 178.710007,1046.498047 176.396927,1047.270874
C174.168320,1040.594116 172.080597,1034.339355 169.759094,1027.384277
C174.417435,1025.649414 178.726364,1024.044678 183.418549,1022.317383
M222.614487,1110.282959
C232.280441,1105.292969 236.480362,1097.197876 235.760620,1086.535889
C235.661087,1085.061401 234.448975,1082.775879 233.263031,1082.397217
C220.997086,1078.481323 208.516418,1076.766968 196.064621,1081.398193
C188.557663,1084.190308 184.559586,1090.745972 185.061066,1098.121948
C185.541321,1105.185791 190.386749,1110.687134 198.052612,1112.792114
C206.277649,1115.050659 214.217789,1113.966431 222.614487,1110.282959
z"/>
<path fill="#B497C6" opacity="1.000000" stroke="none"
d="
M652.674561,1084.000000
C652.675354,1064.180542 652.792175,1044.859741 652.590515,1025.542358
C652.544373,1021.128174 653.942261,1019.634521 658.319153,1019.922363
C663.460022,1020.260315 668.645813,1020.156006 673.800659,1019.952393
C677.140076,1019.820496 678.215881,1021.172424 678.037964,1024.347046
C677.847046,1027.753906 677.994934,1031.179810 677.994934,1035.817871
C680.418274,1033.157227 681.911743,1031.446533 683.479919,1029.807129
C695.690918,1017.041931 716.863159,1013.746887 732.321106,1022.254761
C743.986084,1028.674927 750.329590,1039.136963 750.778564,1051.887817
C751.651367,1076.672607 751.187561,1101.504028 751.318970,1126.315796
C751.333618,1129.085205 750.111389,1130.173340 747.393127,1130.132080
C741.898499,1130.048462 736.388123,1129.882690 730.909912,1130.197876
C726.763428,1130.436401 725.910034,1128.659790 725.937012,1124.932373
C726.075562,1105.780396 726.007690,1086.627075 725.987732,1067.474121
C725.985596,1065.479614 726.002808,1063.458374 725.706665,1061.495361
C723.562256,1047.280884 714.725037,1040.125244 700.475464,1040.969238
C687.650574,1041.728882 678.289795,1051.782104 678.088196,1065.822388
C677.813293,1084.971313 678.009644,1104.127197 677.998840,1123.280029
C677.994995,1130.108398 677.988098,1130.112915 671.249817,1130.123413
C666.753052,1130.130493 662.243225,1129.909424 657.763428,1130.184448
C653.756165,1130.430420 652.575684,1128.797485 652.619568,1124.970337
C652.774353,1111.481812 652.676819,1097.990356 652.674561,1084.000000
z"/>
<path fill="#B396C5" opacity="1.000000" stroke="none"
d="
M119.017403,1045.897461
C103.476791,1036.430054 87.046638,1038.557983 76.319031,1051.184937
C65.297775,1064.157593 65.223991,1086.028564 76.138634,1098.826416
C83.239876,1107.153076 92.244728,1110.650269 103.144859,1110.191895
C113.867172,1109.740845 121.771889,1103.853394 130.299011,1096.332031
C134.418365,1100.866577 138.256943,1105.245117 142.279175,1109.447876
C144.008331,1111.254517 144.090958,1112.478760 142.387802,1114.456543
C128.440613,1130.652588 103.477272,1137.315918 81.478561,1129.875244
C59.195229,1122.338257 45.926426,1106.411377 43.188984,1083.193359
C39.333969,1050.496826 58.671745,1023.907654 89.835030,1018.619995
C108.562424,1015.442383 125.930077,1019.275879 140.351669,1032.810791
C143.275986,1035.555298 144.010681,1037.637085 140.759842,1040.468872
C136.374313,1044.289062 132.109711,1048.247925 127.387344,1052.514038
C125.219917,1050.791626 122.268578,1048.446411 119.017403,1045.897461
z"/>
<path fill="#B497C6" opacity="1.000000" stroke="none"
d="
M353.790070,1043.427490
C350.733795,1044.354858 347.832062,1044.832642 344.942749,1045.376465
C328.779388,1048.418091 317.784821,1060.059814 316.201233,1078.007690
C314.932556,1092.386353 315.383026,1106.919067 315.137146,1121.384644
C314.970276,1131.203003 316.127838,1130.024902 306.225861,1130.119141
C302.564056,1130.154053 298.884857,1129.903076 295.243988,1130.181030
C291.144775,1130.494019 289.753448,1129.024658 289.796387,1124.840332
C289.984222,1106.531616 289.875397,1088.219849 289.875153,1069.909180
C289.874969,1055.094116 290.040833,1040.276245 289.777374,1025.465820
C289.697205,1020.959900 291.198883,1019.626587 295.507935,1019.924500
C300.312347,1020.256714 305.168030,1020.198242 309.981384,1019.938782
C313.799438,1019.733093 315.413696,1020.897034 315.194824,1024.956665
C314.900055,1030.423950 315.121674,1035.918945 316.033447,1041.863770
C316.890594,1040.457275 317.750885,1039.052734 318.604279,1037.643921
C325.588013,1026.116211 335.330078,1018.934265 349.151764,1017.919495
C352.771240,1017.653687 354.335571,1018.677124 354.180176,1022.544922
C353.906464,1029.358154 354.006683,1036.186401 353.790070,1043.427490
z"/>
<path fill="#FFFFFF" opacity="1.000000" stroke="none"
d="
M970.070190,1130.149170
C967.600586,1130.149170 965.622131,1130.149170 963.325317,1130.149170
C963.325317,1093.449585 963.325317,1057.058838 963.325317,1020.333618
C971.488037,1020.333618 979.702515,1020.333618 988.190430,1020.333618
C988.190430,1056.859741 988.190430,1093.234131 988.190430,1130.149170
C982.133972,1130.149170 976.347717,1130.149170 970.070190,1130.149170
z"/>
<path fill="#FCFBFD" opacity="1.000000" stroke="none"
d="
M930.629333,1129.979980
C921.562012,1130.103516 912.953064,1130.103516 904.095581,1130.103516
C904.095581,1120.382812 904.095581,1111.126099 904.095581,1101.511963
C913.093201,1101.511963 922.010315,1101.511963 931.087708,1101.511963
C931.087708,1110.890259 931.087708,1120.373291 930.629333,1129.979980
z"/>
<path fill="#FFFFFF" opacity="1.000000" stroke="none"
d="
M989.130493,1000.834351
C987.997314,1001.928955 986.887451,1003.041870 985.755371,1003.064941
C978.106445,1003.221008 970.452820,1003.146362 962.400879,1003.146362
C962.400879,995.258179 962.400879,987.527710 962.400879,979.396606
C970.989380,979.396606 979.834900,979.396606 989.143494,979.396606
C989.143494,986.455139 989.143494,993.401855 989.130493,1000.834351
z"/>
<path fill="#73358B" opacity="1.000000" stroke="none"
d="
M401.167480,1068.572754
C405.168213,1050.977661 416.229248,1040.492432 431.024689,1039.892334
C448.612427,1039.179199 462.954895,1052.325073 463.659058,1071.163086
C463.929352,1078.394409 462.811829,1086.386719 459.843262,1092.900513
C449.957458,1114.592529 420.591766,1116.381714 407.101685,1096.684570
C401.329559,1088.256470 400.098053,1078.886963 401.167480,1068.572754
z"/>
<path fill="#74368C" opacity="1.000000" stroke="none"
d="
M1064.389893,1041.130493
C1083.597778,1037.239746 1099.385498,1045.930298 1105.178711,1063.268677
C1111.074951,1080.915771 1103.746460,1100.005005 1088.315552,1107.193970
C1064.766113,1118.165283 1038.714111,1099.699341 1040.531982,1073.203491
C1041.596069,1057.693604 1048.638306,1046.359863 1064.389893,1041.130493
z"/>
<path fill="#73358B" opacity="1.000000" stroke="none"
d="
M562.688843,1040.359863
C577.771179,1038.791748 589.497620,1043.873901 596.774170,1056.799438
C604.355286,1070.265991 603.733704,1084.044556 595.135681,1096.926514
C586.898376,1109.268066 570.341492,1113.633301 555.941284,1107.888672
C541.953796,1102.308838 533.121765,1086.289917 535.297913,1070.447632
C537.547485,1054.070801 546.615784,1043.979858 562.688843,1040.359863
z"/>
<path fill="#74358C" opacity="1.000000" stroke="none"
d="
M807.214600,1067.153320
C805.112427,1067.069336 803.478333,1066.985229 801.885498,1066.903320
C800.809204,1052.746582 814.209839,1038.763672 828.868347,1038.415405
C843.821167,1038.060181 857.024231,1051.490723 856.360352,1067.153442
C840.162659,1067.153442 823.922729,1067.153442 807.214600,1067.153320
z"/>
<path fill="#74358C" opacity="1.000000" stroke="none"
d="
M222.282150,1110.464844
C214.217789,1113.966431 206.277649,1115.050659 198.052612,1112.792114
C190.386749,1110.687134 185.541321,1105.185791 185.061066,1098.121948
C184.559586,1090.745972 188.557663,1084.190308 196.064621,1081.398193
C208.516418,1076.766968 220.997086,1078.481323 233.263031,1082.397217
C234.448975,1082.775879 235.661087,1085.061401 235.760620,1086.535889
C236.480362,1097.197876 232.280441,1105.292969 222.282150,1110.464844
z"/>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,33 @@
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'screenName',
label: 'Screen Name',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description:
'Screen name of your connection to be used on Automatisch UI.',
clickToCopy: false,
},
{
key: 'apiKey',
label: 'API Key',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: 'Carbone API key of your account.',
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import verifyCredentials from './verify-credentials';
const isStillVerified = async ($: IGlobalVariable) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,12 @@
import { IGlobalVariable } from '@automatisch/types';
const verifyCredentials = async ($: IGlobalVariable) => {
await $.http.get('/templates');
await $.auth.set({
screenName: $.auth.data.screenName,
apiKey: $.auth.data.apiKey,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,12 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data?.apiKey) {
requestConfig.headers.Authorization = `Bearer ${$.auth.data.apiKey}`;
requestConfig.headers['carbone-version'] = '4';
}
return requestConfig;
};
export default addAuthHeader;

View File

View File

@@ -0,0 +1,18 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import actions from './actions';
export default defineApp({
name: 'Carbone',
key: 'carbone',
iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/carbone/connection',
supportsConnections: true,
baseUrl: 'https://carbone.io',
apiBaseUrl: 'https://api.carbone.io',
primaryColor: '6f42c1',
beforeRequest: [addAuthHeader],
auth,
actions,
});

View File

@@ -0,0 +1,102 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Create a scheduled event',
key: 'createScheduledEvent',
description: 'Creates a scheduled event',
arguments: [
{
label: 'Type',
key: 'entityType',
type: 'dropdown' as const,
required: true,
variables: true,
options: [
{ label: 'Stage channel', value: 1 },
{ label: 'Voice channel', value: 2 },
{ label: 'External', value: 3 }
],
additionalFields: {
type: 'query',
name: 'getDynamicFields',
arguments: [
{
name: 'key',
value: 'listExternalScheduledEventFields',
},
{
name: 'parameters.entityType',
value: '{parameters.entityType}',
},
],
},
},
{
label: 'Name',
key: 'name',
type: 'string' as const,
required: true,
variables: true,
},
{
label: 'Description',
key: 'description',
type: 'string' as const,
required: false,
variables: true,
},
{
label: 'Image',
key: 'image',
type: 'string' as const,
required: false,
description: 'Image as DataURI scheme [data:image/<jpeg/png/gif>;base64,BASE64_ENCODED_<JPEG/PNG/GIF>_IMAGE_DATA]',
variables: true,
},
],
async run($) {
type entity_metadata = {
location: string
}
type guild_event = {
channel_id: number,
name: string,
privacy_level: number,
scheduled_start_time: string,
scheduled_end_time?: string,
description?: string,
entity_type?: number,
entity_metadata?: entity_metadata,
image?: string, //data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA
}
const data: guild_event = {
channel_id: $.step.parameters.channel_id as number,
name: $.step.parameters.name as string,
privacy_level: 2,
scheduled_start_time: $.step.parameters.scheduledStartTime as string,
scheduled_end_time: $.step.parameters.scheduledEndTime as string,
description: $.step.parameters.description as string,
entity_type: $.step.parameters.entityType as number,
image: $.step.parameters.image as string,
};
const isExternal = $.step.parameters.entityType === 3;
if (isExternal) {
data.entity_metadata = {
location: $.step.parameters.location as string,
};
data.channel_id = null;
}
const response = await $.http?.post(
`/guilds/${$.auth.data.guildId}/scheduled-events`,
data
);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -1,3 +1,4 @@
import sendMessageToChannel from './send-message-to-channel';
import createScheduledEvent from './create-scheduled-event';
export default [sendMessageToChannel];
export default [sendMessageToChannel, createScheduledEvent];

View File

@@ -1,3 +1,4 @@
import listChannels from './list-channels';
import listVoiceChannels from './list-voice-channels';
export default [listChannels];
export default [listChannels, listVoiceChannels];

View File

@@ -0,0 +1,34 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List voice channels',
key: 'listVoiceChannels',
async run($: IGlobalVariable) {
const channels: {
data: IJSONObject[];
error: IJSONObject | null;
} = {
data: [],
error: null,
};
const response = await $.http.get(
`/guilds/${$.auth.data.guildId}/channels`
);
channels.data = response.data
.filter((channel: IJSONObject) => {
// filter in voice and stage channels only
return channel.type === 2 || channel.type === 13;
})
.map((channel: IJSONObject) => {
return {
value: channel.id,
name: channel.name,
};
});
return channels;
},
};

View File

@@ -0,0 +1,3 @@
import listExternalScheduledEventFields from './list-external-scheduled-event-fields';
export default [listExternalScheduledEventFields];

View File

@@ -0,0 +1,83 @@
import { IGlobalVariable } from '@automatisch/types';
export default {
name: 'List external scheduled event fields',
key: 'listExternalScheduledEventFields',
async run($: IGlobalVariable) {
const isExternal = $.step.parameters.entityType === 3;
if (isExternal) {
return [
{
label: 'Location',
key: 'location',
type: 'string' as const,
required: true,
description: 'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
variables: true,
},
{
label: 'Start-Time',
key: 'scheduledStartTime',
type: 'string' as const,
required: true,
description: 'The time the event will start [ISO8601]',
variables: true,
},
{
label: 'End-Time',
key: 'scheduledEndTime',
type: 'string' as const,
required: true,
description: 'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
variables: true,
},
];
}
return [
{
label: 'Channel',
key: 'channel_id',
type: 'dropdown' as const,
required: true,
description: 'Pick a voice or stage channel to link the event to. This will be omitted if type is EXTERNAL',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listVoiceChannels',
},
],
},
},
{
label: 'Location',
key: 'location',
type: 'string' as const,
required: false,
description: 'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
variables: true,
},
{
label: 'Start-Time',
key: 'scheduledStartTime',
type: 'string' as const,
required: true,
description: 'The time the event will start [ISO8601]',
variables: true,
},
{
label: 'End-Time',
key: 'scheduledEndTime',
type: 'string' as const,
required: false,
description: 'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
variables: true,
},
];
},
};

View File

@@ -4,6 +4,7 @@ import auth from './auth';
import dynamicData from './dynamic-data';
import actions from './actions';
import triggers from './triggers';
import dynamicFields from './dynamic-fields';
export default defineApp({
name: 'Discord',
@@ -17,6 +18,7 @@ export default defineApp({
beforeRequest: [addAuthHeader],
auth,
dynamicData,
dynamicFields,
triggers,
actions,
});

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,400.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1829 3850 c-287 -26 -616 -144 -862 -308 -135 -89 -408 -322 -475
-405 -165 -201 -294 -475 -339 -717 -21 -117 -24 -472 -5 -630 36 -288 104
-524 220 -753 106 -210 196 -343 306 -452 106 -105 195 -159 348 -213 57 -19
135 -56 173 -80 217 -139 452 -205 725 -205 150 0 238 15 378 65 214 75 380
175 522 314 89 87 101 95 175 120 161 54 277 127 419 263 217 210 362 458 437
749 67 261 49 465 -78 862 -100 313 -183 445 -468 739 -202 208 -366 324 -650
462 -328 159 -565 213 -826 189z m206 -29 c12 -5 -13 -10 -70 -14 -203 -13
-587 -89 -701 -137 -26 -11 -49 -19 -51 -16 -5 5 128 61 217 91 81 28 235 64
315 75 78 10 265 11 290 1z m-62 -142 c233 -14 324 -28 142 -22 -258 10 -585
-22 -805 -77 -63 -16 -116 -28 -117 -26 -6 6 167 85 217 100 120 35 289 42
563 25z m-773 -34 c0 -2 -13 -11 -30 -20 -38 -19 -40 -11 -2 9 31 17 32 18 32
11z m1120 -68 c217 -49 376 -104 575 -203 199 -99 255 -141 363 -274 253 -311
380 -563 411 -815 19 -158 15 -239 -23 -389 -42 -164 -94 -275 -208 -436 -44
-63 -121 -173 -171 -245 -108 -156 -188 -258 -269 -344 -47 -49 -80 -72 -143
-102 -98 -46 -457 -169 -494 -169 -14 0 -86 31 -161 69 -144 74 -183 88 -355
127 -78 17 -138 39 -210 75 -439 223 -697 460 -867 798 -56 112 -64 114 -13 4
68 -147 187 -314 301 -424 136 -130 382 -300 565 -390 46 -23 131 -69 189
-103 127 -75 183 -99 297 -130 60 -15 89 -28 91 -39 6 -31 -34 -54 -144 -81
-60 -14 -115 -33 -123 -41 -23 -23 -8 -20 212 34 l198 50 110 -12 c60 -6 109
-14 109 -18 0 -6 -116 -62 -191 -92 -133 -54 -382 -104 -564 -113 -104 -5
-108 -4 -82 11 15 9 27 17 27 19 0 2 -42 -6 -92 -16 -89 -19 -97 -19 -178 -3
-203 39 -488 226 -664 435 -172 204 -336 516 -409 780 -49 176 -61 269 -62
485 0 195 1 203 33 330 78 301 191 585 277 690 l24 30 -20 -63 c-29 -96 -22
-109 9 -17 52 155 125 242 300 356 59 39 116 62 300 123 394 130 430 136 717
132 191 -3 235 -7 335 -29z m-1941 -729 c-88 -175 -129 -307 -164 -523 -21
-135 -22 -138 -30 -91 -10 64 1 226 20 297 21 81 70 194 115 271 37 62 86 128
96 128 2 0 -14 -37 -37 -82z m100 -28 c-43 -99 -99 -284 -123 -400 -28 -138
-46 -386 -36 -495 13 -146 38 -303 65 -413 29 -119 185 -437 300 -612 75 -116
86 -140 62 -140 -40 0 -208 164 -283 276 -71 106 -93 173 -98 293 -4 83 -10
110 -45 201 -46 116 -56 166 -70 348 -16 194 13 437 74 617 44 132 174 415
190 415 1 0 -15 -41 -36 -90z m3340 -622 c83 -383 16 -750 -195 -1076 -98
-150 -283 -323 -435 -406 -60 -33 -169 -76 -191 -76 -19 0 86 111 228 241 172
157 253 254 313 376 24 48 67 125 96 169 69 105 108 184 141 281 35 103 43
263 20 412 -18 115 -21 216 -7 191 5 -8 18 -59 30 -112z m-152 -310 c-3 -8 -6
-5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-22 -167 c-42 -233 -142 -442
-285 -594 -60 -65 -204 -180 -285 -228 l-40 -24 31 40 c18 22 94 130 171 240
77 110 173 248 214 306 89 126 148 237 180 339 20 61 25 69 27 45 2 -16 -4
-72 -13 -124z m-2954 -986 c77 -64 259 -248 259 -262 0 -26 -70 5 -166 73 -88
62 -174 176 -174 231 0 18 22 7 81 -42z m1310 -11 c26 -8 55 -24 64 -34 18
-19 17 -20 -8 -20 -25 0 -118 31 -146 49 -7 5 -11 13 -7 19 7 11 25 8 97 -14z
m805 -33 c-3 -4 -30 -27 -60 -50 -52 -40 -57 -42 -117 -39 l-62 3 119 47 c138
54 127 50 120 39z m-1515 -324 c49 -23 89 -45 89 -49 0 -21 -187 27 -235 61
-37 26 -135 136 -135 152 0 4 43 -22 97 -58 53 -35 136 -83 184 -106z m1501
125 c-147 -175 -408 -321 -672 -377 -92 -19 -292 -19 -403 0 -190 33 -394 119
-371 156 4 6 41 8 97 4 98 -7 299 6 487 31 274 37 429 79 600 162 92 45 127
54 288 71 8 0 -4 -21 -26 -47z"/>
<path d="M632 2890 c0 -14 2 -19 5 -12 2 6 2 18 0 25 -3 6 -5 1 -5 -13z"/>
<path d="M622 2825 c0 -16 2 -22 5 -12 2 9 2 23 0 30 -3 6 -5 -1 -5 -18z"/>
<path d="M691 1840 c0 -8 4 -22 9 -30 12 -18 12 -2 0 25 -6 13 -9 15 -9 5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,32 @@
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'instanceUrl',
label: 'Instance URL',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'apiKey',
label: 'Admin API Key',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import verifyCredentials from './verify-credentials';
const isStillVerified = async ($: IGlobalVariable) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,16 @@
import { IGlobalVariable } from '@automatisch/types';
const verifyCredentials = async ($: IGlobalVariable) => {
const site = await $.http.get('/admin/site/');
const screenName = [site.data.site.title, site.data.site.url]
.filter(Boolean)
.join(' @ ');
await $.auth.set({
screenName,
});
await $.http.get('/admin/pages/');
};
export default verifyCredentials;

View File

@@ -0,0 +1,23 @@
import { TBeforeRequest } from '@automatisch/types';
import jwt from 'jsonwebtoken';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
const key = $.auth.data?.apiKey as string;
if (key) {
const [id, secret] = key.split(':');
const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
keyid: id,
algorithm: 'HS256',
expiresIn: '1h',
audience: `/admin/`,
});
requestConfig.headers.Authorization = `Ghost ${token}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,12 @@
import { TBeforeRequest } from '@automatisch/types';
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
const instanceUrl = $.auth.data.instanceUrl as string;
if (instanceUrl) {
requestConfig.baseURL = `${instanceUrl}/ghost/api`;
}
return requestConfig;
};
export default setBaseUrl;

View File

View File

@@ -0,0 +1,19 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import setBaseUrl from './common/set-base-url';
import auth from './auth';
import triggers from './triggers';
export default defineApp({
name: 'Ghost',
key: 'ghost',
baseUrl: 'https://ghost.org',
apiBaseUrl: '',
iconUrl: '{BASE_URL}/apps/ghost/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/ghost/connection',
primaryColor: '15171A',
supportsConnections: true,
beforeRequest: [setBaseUrl, addAuthHeader],
auth,
triggers,
});

View File

@@ -0,0 +1,3 @@
import newPostPublished from './new-post-published';
export default [newPostPublished];

View File

@@ -0,0 +1,55 @@
import Crypto from 'crypto';
import isEmpty from 'lodash/isEmpty';
import defineTrigger from '../../../../helpers/define-trigger';
export default defineTrigger({
name: 'New post published',
key: 'newPostPublished',
type: 'webhook',
description: 'Triggers when a new post is published.',
async run($) {
const dataItem = {
raw: $.request.body,
meta: {
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
},
async testRun($) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
},
});
}
},
async registerHook($) {
const payload = {
webhooks: [
{
event: 'post.published',
target_url: $.webhookUrl,
name: `Flow ID: ${$.flow.id}`,
},
],
};
const response = await $.http.post('/admin/webhooks/', payload);
const id = response.data.webhooks[0].id;
await $.flow.setRemoteWebhookId(id);
},
async unregisterHook($) {
await $.http.delete(`/admin/webhooks/${$.flow.remoteWebhookId}/`);
},
});

View File

@@ -17,6 +17,7 @@ export default defineAction({
key: 'parentPageId',
type: 'dropdown' as const,
required: true,
variables: true,
source: {
type: 'query',
name: 'getDynamicData',

View File

@@ -1,3 +1,4 @@
import newDatabaseItems from './new-database-items';
import updatedDatabaseItems from './updated-database-items';
export default [newDatabaseItems];
export default [newDatabaseItems, updatedDatabaseItems];

View File

@@ -0,0 +1,33 @@
import defineTrigger from '../../../../helpers/define-trigger';
import updatedDatabaseItems from './updated-database-items';
export default defineTrigger({
name: 'Updated database items',
key: 'updatedDatabaseItems',
pollInterval: 15,
description:
'Triggers when there is an update to an item in a chosen database',
arguments: [
{
label: 'Database',
key: 'databaseId',
type: 'dropdown' as const,
required: false,
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDatabases',
},
],
},
},
],
async run($) {
await updatedDatabaseItems($);
},
});

View File

@@ -0,0 +1,51 @@
import { IGlobalVariable } from '@automatisch/types';
type DatabaseItem = {
id: string;
last_edited_time: string;
};
type ResponseData = {
results: DatabaseItem[];
next_cursor?: string;
};
type Payload = {
sorts: [
{
timestamp: 'created_time' | 'last_edited_time';
direction: 'ascending' | 'descending';
}
];
start_cursor?: string;
};
const updatedDatabaseItems = async ($: IGlobalVariable) => {
const payload: Payload = {
sorts: [
{
timestamp: 'last_edited_time',
direction: 'descending',
},
],
};
const databaseId = $.step.parameters.databaseId as string;
const path = `/v1/databases/${databaseId}/query`;
do {
const response = await $.http.post<ResponseData>(path, payload);
payload.start_cursor = response.data.next_cursor;
for (const databaseItem of response.data.results) {
$.pushTriggerItem({
raw: databaseItem,
meta: {
internalId: `${databaseItem.id}-${databaseItem.last_edited_time}`,
},
});
}
} while (payload.start_cursor);
};
export default updatedDatabaseItems;

View File

@@ -0,0 +1,53 @@
import defineAction from '../../../../helpers/define-action';
import { URLSearchParams } from 'url';
export default defineAction({
name: 'Create link post',
key: 'createLinkPost',
description: 'Create a new link post within a subreddit.',
arguments: [
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description:
'Heading for the recent post. Limited to 300 characters or less.',
variables: true,
},
{
label: 'Subreddit',
key: 'subreddit',
type: 'string' as const,
required: true,
description: 'The subreddit for posting. Note: Exclude /r/.',
variables: true,
},
{
label: 'Url',
key: 'url',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
],
async run($) {
const { title, subreddit, url } = $.step.parameters;
const params = new URLSearchParams({
kind: 'link',
api_type: 'json',
title: title as string,
sr: subreddit as string,
url: url as string,
});
const { data } = await $.http.post('/api/submit', params.toString());
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,3 @@
import createLinkPost from './create-link-post';
export default [createLinkPost];

View File

@@ -0,0 +1 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="_1O4jTk-dZ-VIxsCuYB6OR8 " width="40" height="48" ><g><circle fill="#FF4500" cx="10" cy="10" r="10"></circle><path fill="#FFFFFF" d="M16.67,10A1.46,1.46,0,0,0,14.2,9a7.12,7.12,0,0,0-3.85-1.23L11,4.65,13.14,5.1a1,1,0,1,0,.13-0.61L10.82,4a0.31,0.31,0,0,0-.37.24L9.71,7.71a7.14,7.14,0,0,0-3.9,1.23A1.46,1.46,0,1,0,4.2,11.33a2.87,2.87,0,0,0,0,.44c0,2.24,2.61,4.06,5.83,4.06s5.83-1.82,5.83-4.06a2.87,2.87,0,0,0,0-.44A1.46,1.46,0,0,0,16.67,10Zm-10,1a1,1,0,1,1,1,1A1,1,0,0,1,6.67,11Zm5.81,2.75a3.84,3.84,0,0,1-2.47.77,3.84,3.84,0,0,1-2.47-.77,0.27,0.27,0,0,1,.38-0.38A3.27,3.27,0,0,0,10,14a3.28,3.28,0,0,0,2.09-.61A0.27,0.27,0,1,1,12.48,13.79Zm-0.18-1.71a1,1,0,1,1,1-1A1,1,0,0,1,12.29,12.08Z"></path></g></svg>

After

Width:  |  Height:  |  Size: 813 B

View File

@@ -0,0 +1,26 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
import authScope from '../common/auth-scope';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const state = Math.random().toString() as string;
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
response_type: 'code',
redirect_uri: redirectUri,
duration: 'permanent',
scope: authScope.join(' '),
state,
});
const url = `https://www.reddit.com/api/v1/authorize?${searchParams.toString()}`;
await $.auth.set({
url,
originalState: state,
});
}

View File

@@ -0,0 +1,48 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import refreshToken from './refresh-token';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/reddit/connections/add',
placeholder: null,
description:
'When asked to input a redirect URL in Reddit, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const isStillVerified = async ($: IGlobalVariable) => {
const currentUser = await getCurrentUser($);
return !!currentUser.id;
};
export default isStillVerified;

View File

@@ -0,0 +1,29 @@
import { URLSearchParams } from 'node:url';
import { IGlobalVariable } from '@automatisch/types';
const refreshToken = async ($: IGlobalVariable) => {
const headers = {
Authorization: `Basic ${Buffer.from(
$.auth.data.clientId + ':' + $.auth.data.clientSecret
).toString('base64')}`,
};
const params = new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: $.auth.data.refreshToken as string,
});
const { data } = await $.http.post(
'https://www.reddit.com/api/v1/access_token',
params.toString(),
{ headers }
);
await $.auth.set({
accessToken: data.access_token,
expiresIn: data.expires_in,
scope: data.scope,
tokenType: data.token_type,
});
};
export default refreshToken;

View File

@@ -0,0 +1,48 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
import { URLSearchParams } from 'url';
const verifyCredentials = async ($: IGlobalVariable) => {
if ($.auth.data.originalState !== $.auth.data.state) {
throw new Error(`The 'state' parameter does not match.`);
}
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const headers = {
Authorization: `Basic ${Buffer.from(
$.auth.data.clientId + ':' + $.auth.data.clientSecret
).toString('base64')}`,
};
const params = new URLSearchParams({
grant_type: 'authorization_code',
code: $.auth.data.code as string,
redirect_uri: redirectUri,
});
const { data } = await $.http.post(
'https://www.reddit.com/api/v1/access_token',
params.toString(),
{ headers }
);
await $.auth.set({
accessToken: data.access_token,
tokenType: data.token_type,
});
const currentUser = await getCurrentUser($);
const screenName = currentUser?.name;
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
scope: $.auth.data.scope,
expiresIn: data.expires_in,
refreshToken: data.refresh_token,
screenName,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,23 @@
import { TBeforeRequest } from '@automatisch/types';
import appConfig from '../../../config/app';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
const screenName = $.auth.data?.screenName as string;
if ($.auth.data?.accessToken) {
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
}
if (screenName) {
requestConfig.headers[
'User-Agent'
] = `web:automatisch:${appConfig.version} (by /u/${screenName})`;
} else {
requestConfig.headers[
'User-Agent'
] = `web:automatisch:${appConfig.version}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,3 @@
const authScope: string[] = ['identity', 'read', 'account', 'submit'];
export default authScope;

View File

@@ -0,0 +1,8 @@
import { IGlobalVariable } from '@automatisch/types';
const getCurrentUser = async ($: IGlobalVariable) => {
const { data: currentUser } = await $.http.get('/api/v1/me');
return currentUser;
};
export default getCurrentUser;

View File

View File

@@ -0,0 +1,20 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import triggers from './triggers';
import actions from './actions';
export default defineApp({
name: 'Reddit',
key: 'reddit',
baseUrl: 'https://www.reddit.com',
apiBaseUrl: 'https://oauth.reddit.com',
iconUrl: '{BASE_URL}/apps/reddit/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/reddit/connection',
primaryColor: 'FF4500',
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
triggers,
actions,
});

View File

@@ -0,0 +1,3 @@
import newPostsMatchingSearch from './new-posts-matching-search';
export default [newPostsMatchingSearch];

View File

@@ -0,0 +1,48 @@
import defineTrigger from '../../../../helpers/define-trigger';
export default defineTrigger({
name: 'New posts matching search',
key: 'newPostsMatchingSearch',
pollInterval: 15,
description: 'Triggers when a search string matches a new post.',
arguments: [
{
label: 'Search Query',
key: 'searchQuery',
type: 'string' as const,
required: true,
description:
'The term or expression to look for, restricted to 512 characters. If your query contains periods (e.g., automatisch.io), ensure it is enclosed in quotes ("automatisch.io").',
variables: true,
},
],
async run($) {
const { searchQuery } = $.step.parameters;
const params = {
q: searchQuery,
type: 'link',
sort: 'new',
limit: 100,
after: undefined as unknown as string,
};
do {
const { data } = await $.http.get('/search', {
params,
});
params.after = data.data.after;
if (data.data.children?.length) {
for (const item of data.data.children) {
$.pushTriggerItem({
raw: item,
meta: {
internalId: item.data.id,
},
});
}
}
} while (params.after);
},
});

View File

@@ -0,0 +1,3 @@
import removeImageBackground from './remove-image-background';
export default [removeImageBackground];

View File

@@ -0,0 +1,82 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Remove image background',
key: 'removeImageBackground',
description:
'Removes the background of an image.',
arguments: [
{
label: 'Image file',
key: 'imageFileB64',
type: 'string' as const,
required: true,
variables: true,
description: 'Provide a JPG or PNG file in Base64 format, up to 12 MB (see remove.bg/supported-images)',
},
{
label: 'Size',
key: 'size',
type: 'dropdown' as const,
required: true,
value: 'auto',
options: [
{ label: 'Auto', value: 'auto' },
{ label: 'Preview (up to 0.25 megapixels)', value: 'preview' },
{ label: 'Full (up to 10 megapixels)', value: 'full' },
]
},
{
label: 'Background color',
key: 'bgColor',
type: 'string' as const,
description: 'Adds a solid color background. Can be a hex color code (e.g. 81d4fa, fff) or a color name (e.g. green)',
required: false,
},
{
label: 'Background image URL',
key: 'bgImageUrl',
type: 'string' as const,
description: 'Adds a background image from a URL.',
required: false,
},
{
label: 'Output image format',
key: 'outputFormat',
type: 'dropdown' as const,
description: 'Note: Use PNG to preserve transparency',
required: true,
value: 'auto',
options: [
{ label: 'Auto', value: 'auto' },
{ label: 'PNG', value: 'png' },
{ label: 'JPG', value: 'jpg' },
{ label: 'ZIP', value: 'zip' }
]
}
],
async run($) {
const imageFileB64 = $.step.parameters.imageFileB64 as string;
const size = $.step.parameters.size as string;
const bgColor = $.step.parameters.bgColor as string;
const bgImageUrl = $.step.parameters.bgImageUrl as string;
const outputFormat = $.step.parameters.outputFormat as string;
const body = JSON.stringify({
image_file_b64: imageFileB64,
size: size,
bg_color: bgColor,
bg_image_url: bgImageUrl,
format: outputFormat
});
const response = await $.http.post('/removebg', body, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
$.setActionItem({ raw: response.data });
}
});

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="78.88" height="77" viewBox="0 0 78.88 77"><g transform="translate(-561 -471)"><g transform="translate(-479.103 381)"><g transform="translate(1040.104 94)"><g transform="translate(113.835 10.269)"><path d="M3640.414,2731.809l-5.131-3.025-31.2,18.389a3.848,3.848,0,0,1-3.906,0l-31.2-18.385-5.125,3.021a2.355,2.355,0,0,0,0,4.059l36.329,21.41a3.847,3.847,0,0,0,3.905,0l36.328-21.41a2.355,2.355,0,0,0,0-4.059Z" transform="translate(-3676.527 -2699.322)" fill="#bbc0c4"/><path d="M3640.414,2717.247l-36.328,21.41a3.847,3.847,0,0,1-3.905,0l-36.329-21.41a2.356,2.356,0,0,1,0-4.06l36.329-21.409a3.842,3.842,0,0,1,3.9,0l36.328,21.409a2.356,2.356,0,0,1,0,4.06Z" transform="translate(-3676.527 -2694.961)" fill="#55626d"/></g></g><rect width="420" height="77" transform="translate(1040.104 90)" fill="none"/></g></g></svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@@ -0,0 +1,33 @@
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'screenName',
label: 'Screen Name',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description:
'Screen name of your connection to be used on Automatisch UI.',
clickToCopy: false,
},
{
key: 'apiKey',
label: 'API Key',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: 'API key of the remove.bg API service.',
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import verifyCredentials from './verify-credentials';
const isStillVerified = async ($: IGlobalVariable) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,12 @@
import { IGlobalVariable } from '@automatisch/types';
const verifyCredentials = async ($: IGlobalVariable) => {
await $.http.get('/account');
await $.auth.set({
screenName: $.auth.data.screenName,
apiKey: $.auth.data.apiKey,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,11 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data?.apiKey) {
requestConfig.headers['X-API-Key'] = `${$.auth.data.apiKey}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

View File

@@ -0,0 +1,18 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import actions from './actions';
export default defineApp({
name: 'Remove.bg',
key: 'removebg',
iconUrl: '{BASE_URL}/apps/removebg/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/removebg/connection',
supportsConnections: true,
baseUrl: 'https://www.remove.bg',
apiBaseUrl: 'https://api.remove.bg/v1.0',
primaryColor: '55636c',
beforeRequest: [addAuthHeader],
auth,
actions,
});

View File

@@ -0,0 +1,189 @@
import { IJSONArray, IJSONObject } from '@automatisch/types';
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Create card',
key: 'createCard',
description: 'Creates a new card within a specified board and list.',
arguments: [
{
label: 'Board',
key: 'boardId',
type: 'dropdown' as const,
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBoards',
},
],
},
},
{
label: 'List',
key: 'listId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.boardId'],
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBoardLists',
},
{
name: 'parameters.boardId',
value: '{parameters.boardId}',
},
],
},
},
{
label: 'Name',
key: 'name',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Description',
key: 'description',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
{
label: 'Label',
key: 'label',
type: 'dropdown' as const,
required: false,
dependsOn: ['parameters.boardId'],
description: 'Select a color tag to attach to the card.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBoardLabels',
},
{
name: 'parameters.boardId',
value: '{parameters.boardId}',
},
],
},
},
{
label: 'Card Position',
key: 'cardPosition',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
options: [
{
label: 'top',
value: 'top',
},
{
label: 'bottom',
value: 'bottom',
},
],
},
{
label: 'Members',
key: 'memberIds',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Member',
key: 'memberId',
type: 'dropdown' as const,
required: false,
dependsOn: ['parameters.boardId'],
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listMembers',
},
{
name: 'parameters.boardId',
value: '{parameters.boardId}',
},
],
},
},
],
},
{
label: 'Due Date',
key: 'dueDate',
type: 'string' as const,
required: false,
variables: true,
description: 'Format: mm-dd-yyyy HH:mm:ss or yyyy-MM-dd HH:mm:ss.',
},
{
label: 'URL Attachment',
key: 'urlSource',
type: 'string' as const,
required: false,
variables: true,
description: 'A URL to attach to the card.',
},
],
async run($) {
const {
listId,
name,
description,
cardPosition,
dueDate,
label,
urlSource,
} = $.step.parameters;
const memberIds = $.step.parameters.memberIds as IJSONArray;
const idMembers = memberIds.map(
(memberId: IJSONObject) => memberId.memberId
);
const fields = {
name,
desc: description,
idList: listId,
pos: cardPosition,
due: dueDate,
idMembers: idMembers.join(','),
idLabels: label,
urlSource,
};
const response = await $.http.post('/1/cards', fields);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,3 @@
import createCard from './create-card';
export default [createCard];

View File

@@ -5,6 +5,8 @@ const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
requestConfig.headers.Authorization = `OAuth oauth_consumer_key="${$.auth.data.apiKey}", oauth_token="${$.auth.data.token}"`;
}
requestConfig.headers.Accept = 'application/json';
return requestConfig;
};

View File

@@ -0,0 +1,6 @@
import listBoardLabels from './list-board-labels';
import listBoardLists from './list-board-lists';
import listBoards from './list-boards';
import listMembers from './listMembers';
export default [listBoardLabels, listBoardLists, listBoards, listMembers];

View File

@@ -0,0 +1,39 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List board labels',
key: 'listBoardLabels',
async run($: IGlobalVariable) {
const boardLabels: {
data: IJSONObject[];
} = {
data: [],
};
const boardId = $.step.parameters.boardId;
if (!boardId) {
return boardLabels;
}
const params = {
fields: 'color',
};
const { data } = await $.http.get(`/1/boards/${boardId}/labels`, {
params,
});
if (data?.length) {
for (const boardLabel of data) {
boardLabels.data.push({
value: boardLabel.id,
name: boardLabel.color,
});
}
}
return boardLabels;
},
};

View File

@@ -0,0 +1,33 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List board lists',
key: 'listBoardLists',
async run($: IGlobalVariable) {
const boards: {
data: IJSONObject[];
} = {
data: [],
};
const boardId = $.step.parameters.boardId;
if (!boardId) {
return boards;
}
const { data } = await $.http.get(`/1/boards/${boardId}/lists`);
if (data?.length) {
for (const list of data) {
boards.data.push({
value: list.id,
name: list.name,
});
}
}
return boards;
},
};

View File

@@ -0,0 +1,27 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List boards',
key: 'listBoards',
async run($: IGlobalVariable) {
const boards: {
data: IJSONObject[];
} = {
data: [],
};
const { data } = await $.http.get(`/1/members/me/boards`);
if (data?.length) {
for (const board of data) {
boards.data.push({
value: board.id,
name: board.name,
});
}
}
return boards;
},
};

View File

@@ -0,0 +1,33 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List members',
key: 'listMembers',
async run($: IGlobalVariable) {
const members: {
data: IJSONObject[];
} = {
data: [],
};
const boardId = $.step.parameters.boardId;
if (!boardId) {
return members;
}
const { data } = await $.http.get(`/1/boards/${boardId}/members`);
if (data?.length) {
for (const member of data) {
members.data.push({
value: member.id,
name: member.fullName,
});
}
}
return members;
},
};

View File

@@ -1,6 +1,8 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import actions from './actions';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Trello',
@@ -13,4 +15,6 @@ export default defineApp({
primaryColor: '0079bf',
beforeRequest: [addAuthHeader],
auth,
actions,
dynamicData,
});

View File

@@ -0,0 +1 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="1-XeroLogoTitle-2 1-XeroLogoDesc-3" width="53" height="53" viewBox="0 0 53 53" fill="none"><title id="1-XeroLogoTitle-2">Xero homepage</title><desc id="1-XeroLogoDesc-3" fill="none">Beautiful business</desc><path d="M26.4992 0.0527344C11.8623 0.0527344 0 11.7617 0 26.204C0 40.6452 11.8623 52.3554 26.4992 52.3554C41.1318 52.3554 53 40.6452 53 26.204C53 11.7617 41.1318 0.0527344 26.4992 0.0527344Z" class="Xero__LogoPath-sc-1jj6kwd-2 bSnsZq" fill="#13B5EA"></path><path d="M19.4255 25.1079C19.9011 23.2503 21.5775 21.9536 23.5036 21.9536C25.4396 21.9536 27.108 23.247 27.5858 25.1079H19.4255ZM29.0672 26.2366C29.3402 25.9015 29.4429 25.4631 29.3557 25.001C29.0006 23.3265 28.0873 21.9852 26.7147 21.1221C25.7719 20.5251 24.6717 20.2097 23.5327 20.2097C22.2761 20.2097 21.0798 20.5863 20.0728 21.299C18.499 22.4136 17.5596 24.2343 17.5596 26.1698C17.5596 26.6556 17.6199 27.1379 17.7387 27.6031C18.3436 29.9599 20.3778 31.7424 22.8007 32.0386C23.035 32.0665 23.2696 32.0807 23.4979 32.0807C23.9833 32.0807 24.4556 32.0191 24.9409 31.8933C25.572 31.7423 26.1732 31.4877 26.7284 31.1371C27.2529 30.7986 27.736 30.3425 28.244 29.7064L28.2773 29.6721C28.4464 29.4618 28.5251 29.1915 28.493 28.9307C28.4641 28.6972 28.352 28.4957 28.1774 28.3634C28.0118 28.2363 27.8144 28.1663 27.622 28.1663C27.4343 28.1663 27.1588 28.234 26.9095 28.5576L26.89 28.5834C26.8077 28.6928 26.7225 28.8058 26.6246 28.9179C26.2888 29.2936 25.9028 29.603 25.4783 29.8374C24.8705 30.1602 24.2137 30.3263 23.5279 30.3312C21.3734 30.3077 20.054 28.8796 19.5384 27.5532C19.4575 27.3144 19.3997 27.0964 19.3625 26.8943C19.3619 26.8738 19.3603 26.8523 19.359 26.8313L27.7006 26.8298C28.28 26.8175 28.7654 26.6068 29.0672 26.2366Z" class="Xero__LogoPath-sc-1jj6kwd-2 cNYYwL" fill="#FFFFFF"></path><path d="M41.2233 24.5579C40.3858 24.5579 39.7043 25.2348 39.7043 26.0667C39.7043 26.8988 40.3858 27.5758 41.2233 27.5758C42.0593 27.5758 42.7394 26.8988 42.7394 26.0667C42.7394 25.2348 42.0593 24.5579 41.2233 24.5579Z" class="Xero__LogoPath-sc-1jj6kwd-2 cNYYwL" fill="#FFFFFF"></path><path d="M35.6896 21.1567C35.6896 20.6828 35.3007 20.2974 34.8238 20.2974L34.58 20.2938C33.84 20.2938 33.1404 20.5197 32.5509 20.9479C32.4382 20.6012 32.1081 20.3574 31.7358 20.3574C31.256 20.3574 30.878 20.7319 30.875 21.2102L30.8778 31.1105C30.881 31.5821 31.2664 31.9658 31.7373 31.9658C32.211 31.9658 32.5966 31.582 32.5966 31.1101V25.0215C32.5966 23.0488 32.7672 22.2242 34.4836 22.0128C34.626 21.9959 34.7788 21.9949 34.8176 21.9949C35.3228 21.9767 35.6896 21.6242 35.6896 21.1567Z" class="Xero__LogoPath-sc-1jj6kwd-2 cNYYwL" fill="#FFFFFF"></path><path d="M13.0526 26.1305L17.5041 21.6836C17.6669 21.5245 17.7567 21.3106 17.7567 21.0815C17.7567 20.6084 17.3684 20.2235 16.8914 20.2235C16.6605 20.2235 16.4426 20.3146 16.2784 20.4796L11.8266 24.9041L7.35704 20.4714C7.19408 20.3116 6.97812 20.2235 6.74871 20.2235C6.27397 20.2235 5.88782 20.6084 5.88782 21.0815C5.88782 21.3111 5.97997 21.529 6.14727 21.6947L10.602 26.1269L6.15399 30.5648C5.98236 30.7279 5.88782 30.9469 5.88782 31.1811C5.88782 31.654 6.27397 32.0389 6.74871 32.0389C6.97463 32.0389 7.19059 31.9513 7.35738 31.7914L11.8222 27.3492L16.2677 31.7706C16.4382 31.945 16.6596 32.0411 16.8914 32.0411C17.3684 32.0411 17.7567 31.6553 17.7567 31.1811C17.7567 30.9547 17.667 30.7401 17.5044 30.5765L13.0526 26.1305Z" class="Xero__LogoPath-sc-1jj6kwd-2 cNYYwL" fill="#FFFFFF"></path><path d="M41.222 30.216C38.9223 30.216 37.0516 28.3547 37.0516 26.0667C37.0516 23.7762 38.9223 21.9126 41.222 21.9126C43.5192 21.9126 45.388 23.7762 45.388 26.0667C45.388 28.3547 43.5192 30.216 41.222 30.216ZM41.223 20.1443C37.9429 20.1443 35.2742 22.801 35.2742 26.0664C35.2742 29.331 37.9429 31.987 41.223 31.987C44.5016 31.987 47.1689 29.331 47.1689 26.0664C47.1689 22.801 44.5016 20.1443 41.223 20.1443Z" class="Xero__LogoPath-sc-1jj6kwd-2 cNYYwL" fill="#FFFFFF"></path></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,22 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
import authScope from '../common/auth-scope';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
response_type: 'code',
client_id: $.auth.data.clientId as string,
scope: authScope.join(' '),
redirect_uri: redirectUri,
});
const url = `https://login.xero.com/identity/connect/authorize?${searchParams.toString()}`;
await $.auth.set({
url,
});
}

View File

@@ -0,0 +1,48 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import refreshToken from './refresh-token';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/xero/connections/add',
placeholder: null,
description:
'When asked to input a redirect URL in Xero, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const isStillVerified = async ($: IGlobalVariable) => {
const currentUser = await getCurrentUser($);
return !!currentUser.tenantName;
};
export default isStillVerified;

View File

@@ -0,0 +1,39 @@
import { URLSearchParams } from 'node:url';
import { IGlobalVariable } from '@automatisch/types';
import authScope from '../common/auth-scope';
const refreshToken = async ($: IGlobalVariable) => {
const headers = {
Authorization: `Basic ${Buffer.from(
$.auth.data.clientId + ':' + $.auth.data.clientSecret
).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded',
};
const params = new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: $.auth.data.refreshToken as string,
});
const { data } = await $.http.post(
'https://identity.xero.com/connect/token',
params.toString(),
{
headers,
additionalProperties: {
skipAddingAuthHeader: true,
},
}
);
await $.auth.set({
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
idToken: data.id_token,
scope: authScope.join(' '),
tokenType: data.token_type,
});
};
export default refreshToken;

View File

@@ -0,0 +1,53 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
import { URLSearchParams } from 'url';
const verifyCredentials = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const headers = {
Authorization: `Basic ${Buffer.from(
$.auth.data.clientId + ':' + $.auth.data.clientSecret
).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded',
};
const params = new URLSearchParams({
grant_type: 'authorization_code',
code: $.auth.data.code as string,
redirect_uri: redirectUri,
});
const { data } = await $.http.post(
'https://identity.xero.com/connect/token',
params.toString(),
{
headers,
}
);
await $.auth.set({
accessToken: data.access_token,
tokenType: data.token_type,
idToken: data.id_token,
});
const currentUser = await getCurrentUser($);
const screenName = [currentUser.tenantName, currentUser.tenantType]
.filter(Boolean)
.join(' @ ');
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
scope: $.auth.data.scope,
expiresIn: data.expires_in,
refreshToken: data.refresh_token,
tenantId: currentUser.tenantId,
screenName,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,18 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if (requestConfig.additionalProperties?.skipAddingAuthHeader)
return requestConfig;
if ($.auth.data?.accessToken) {
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
}
if ($.auth.data?.tenantId) {
requestConfig.headers['Xero-tenant-id'] = $.auth.data.tenantId as string;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,10 @@
const authScope: string[] = [
'offline_access',
'openid',
'profile',
'email',
'accounting.transactions',
'accounting.settings',
];
export default authScope;

View File

@@ -0,0 +1,8 @@
import { IGlobalVariable } from '@automatisch/types';
const getCurrentUser = async ($: IGlobalVariable) => {
const { data: currentUser } = await $.http.get('/connections');
return currentUser[0];
};
export default getCurrentUser;

View File

@@ -0,0 +1,3 @@
import listOrganizations from './list-organizations';
export default [listOrganizations];

View File

@@ -0,0 +1,27 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List organizations',
key: 'listOrganizations',
async run($: IGlobalVariable) {
const organizations: {
data: IJSONObject[];
} = {
data: [],
};
const { data } = await $.http.get('/api.xro/2.0/Organisation');
if (data.Organisations?.length) {
for (const organization of data.Organisations) {
organizations.data.push({
value: organization.OrganisationID,
name: organization.Name,
});
}
}
return organizations;
},
};

View File

View File

@@ -0,0 +1,20 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import triggers from './triggers';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Xero',
key: 'xero',
baseUrl: 'https://go.xero.com',
apiBaseUrl: 'https://api.xero.com',
iconUrl: '{BASE_URL}/apps/xero/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/xero/connection',
primaryColor: '13B5EA',
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
triggers,
dynamicData,
});

View File

@@ -0,0 +1,4 @@
import newBankTransactions from './new-bank-transactions';
import newPayments from './new-payments';
export default [newBankTransactions, newPayments];

View File

@@ -0,0 +1,60 @@
import defineTrigger from '../../../../helpers/define-trigger';
export default defineTrigger({
name: 'New bank transactions',
key: 'newBankTransactions',
pollInterval: 15,
description: 'Triggers when a new bank transaction occurs.',
arguments: [
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
],
async run($) {
const params = {
page: 1,
order: 'Date DESC',
};
let nextPage = false;
do {
const { data } = await $.http.get('/api.xro/2.0/BankTransactions', {
params,
});
params.page = params.page + 1;
if (data.BankTransactions?.length) {
for (const bankTransaction of data.BankTransactions) {
$.pushTriggerItem({
raw: bankTransaction,
meta: {
internalId: bankTransaction.BankTransactionID,
},
});
}
}
if (data.BankTransactions?.length === 100) {
nextPage = true;
} else {
nextPage = false;
}
} while (nextPage);
},
});

View File

@@ -0,0 +1,109 @@
import defineTrigger from '../../../../helpers/define-trigger';
type Params = {
page: number;
order: string;
where?: string;
};
export default defineTrigger({
name: 'New payments',
key: 'newPayments',
pollInterval: 15,
description: 'Triggers when a new payment is received.',
arguments: [
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'Payment Type',
key: 'paymentType',
type: 'dropdown' as const,
required: false,
description: '',
variables: true,
value: '',
options: [
{ label: 'Accounts Receivable', value: 'ACCRECPAYMENT' },
{ label: 'Accounts Payable', value: 'ACCPAYPAYMENT' },
{
label: 'Accounts Receivable Credit (Refund)',
value: 'ARCREDITPAYMENT',
},
{
label: 'Accounts Payable Credit (Refund)',
value: 'APCREDITPAYMENT',
},
{
label: 'Accounts Receivable Overpayment (Refund)',
value: 'AROVERPAYMENTPAYMENT',
},
{
label: 'Accounts Receivable Prepayment (Refund)',
value: 'ARPREPAYMENTPAYMENT',
},
{
label: 'Accounts Payable Prepayment (Refund)',
value: 'APPREPAYMENTPAYMENT',
},
{
label: 'Accounts Payable Overpayment (Refund)',
value: 'APOVERPAYMENTPAYMENT',
},
],
},
],
async run($) {
const paymentType = $.step.parameters.paymentType;
const params: Params = {
page: 1,
order: 'Date DESC',
};
if (paymentType) {
params.where = `PaymentType="${paymentType}"`;
}
let nextPage = false;
do {
const { data } = await $.http.get('/api.xro/2.0/Payments', {
params,
});
params.page = params.page + 1;
if (data.Payments?.length) {
for (const payment of data.Payments) {
$.pushTriggerItem({
raw: payment,
meta: {
internalId: payment.PaymentID,
},
});
}
}
if (data.Payments?.length === 100) {
nextPage = true;
} else {
nextPage = false;
}
} while (nextPage);
},
});

View File

@@ -0,0 +1,301 @@
export const fields = [
{
label: 'Subject',
key: 'subject',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Assignee',
key: 'assigneeId',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'Note: An error occurs if the assignee is not in the default group (or the specific group chosen below).',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.showUserRole',
value: 'true',
},
{
name: 'parameters.includeAdmins',
value: 'true',
},
],
},
},
{
label: 'Collaborators',
key: 'collaborators',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Collaborator',
key: 'collaborator',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.includeAdmins',
value: 'true',
},
],
},
},
],
},
{
label: 'Collaborator Emails',
key: 'collaboratorEmails',
type: 'dynamic' as const,
required: false,
description:
'You have the option to include individuals who are not Zendesk users as Collaborators by adding their email addresses here.',
fields: [
{
label: 'Collaborator Email',
key: 'collaboratorEmail',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
],
},
{
label: 'Group',
key: 'groupId',
type: 'dropdown' as const,
required: false,
variables: true,
description: 'Allocate this ticket to a specific group.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listGroups',
},
],
},
},
{
label: 'Requester Name',
key: 'requesterName',
type: 'string' as const,
required: false,
variables: true,
description:
'To specify the Requester, you need to fill in the Requester Name in this field and provide the Requestor Email in the next field.',
},
{
label: 'Requester Email',
key: 'requesterEmail',
type: 'string' as const,
required: false,
variables: true,
description:
'To specify the Requester, you need to fill in the Requester Email in this field and provide the Requestor Name in the previous field.',
},
{
label: 'First Comment/Description Format',
key: 'format',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Plain Text', value: 'Plain Text' },
{ label: 'HTML', value: 'HTML' },
],
},
{
label: 'First Comment/Description',
key: 'comment',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Should the first comment be public?',
key: 'publicOrNot',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Yes', value: 'yes' },
{ label: 'No', value: 'no' },
],
},
{
label: 'Tags',
key: 'tags',
type: 'string' as const,
required: false,
variables: true,
description: 'A comma separated list of tags.',
},
{
label: 'Status',
key: 'status',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'New', value: 'new' },
{ label: 'Open', value: 'open' },
{ label: 'Pending', value: 'pending' },
{ label: 'Hold', value: 'hold' },
{ label: 'Solved', value: 'solved' },
{ label: 'Closed', value: 'closed' },
],
},
{
label: 'Type',
key: 'type',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Problem', value: 'problem' },
{ label: 'Incident', value: 'incident' },
{ label: 'Question', value: 'question' },
{ label: 'Task', value: 'task' },
],
},
{
label: 'Due At',
key: 'dueAt',
type: 'string' as const,
required: false,
variables: true,
description: 'Limited to tickets typed as "task".',
},
{
label: 'Priority',
key: 'priority',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Urgent', value: 'urgent' },
{ label: 'High', value: 'high' },
{ label: 'Normal', value: 'normal' },
{ label: 'Low', value: 'low' },
],
},
{
label: 'Submitter',
key: 'submitterId',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.includeAdmins',
value: 'false',
},
],
},
},
{
label: 'Ticket Form',
key: 'ticketForm',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'When chosen, this will configure the form displayed for this ticket. Note: This field is solely relevant for Zendesk enterprise accounts.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listTicketForms',
},
],
},
},
{
label: 'Sharing Agreements',
key: 'sharingAgreements',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Sharing Agreement',
key: 'sharingAgreement',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSharingAgreements',
},
],
},
},
],
},
{
label: 'Brand',
key: 'brandId',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'This applies exclusively to Zendesk customers subscribed to plans that include multi-brand support.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBrands',
},
],
},
},
];

View File

@@ -0,0 +1,101 @@
import { IJSONArray, IJSONObject } from '@automatisch/types';
import defineAction from '../../../../helpers/define-action';
import { fields } from './fields';
import isEmpty from 'lodash/isEmpty';
type Payload = {
ticket: IJSONObject;
};
export default defineAction({
name: 'Create ticket',
key: 'createTicket',
description: 'Creates a new ticket',
arguments: fields,
async run($) {
const {
subject,
assigneeId,
groupId,
requesterName,
requesterEmail,
format,
comment,
publicOrNot,
status,
type,
dueAt,
priority,
submitterId,
ticketForm,
brandId,
} = $.step.parameters;
const collaborators = $.step.parameters.collaborators as IJSONArray;
const collaboratorIds = collaborators?.map(
(collaborator: IJSONObject) => collaborator.collaborator
);
const collaboratorEmails = $.step.parameters
.collaboratorEmails as IJSONArray;
const formattedCollaboratorEmails = collaboratorEmails?.map(
(collaboratorEmail: IJSONObject) => collaboratorEmail.collaboratorEmail
);
const formattedCollaborators = [
...collaboratorIds,
...formattedCollaboratorEmails,
];
const sharingAgreements = $.step.parameters.sharingAgreements as IJSONArray;
const sharingAgreementIds = sharingAgreements
?.filter(isEmpty)
.map((sharingAgreement: IJSONObject) =>
Number(sharingAgreement.sharingAgreement)
);
const tags = $.step.parameters.tags as string;
const formattedTags = tags.split(',');
const payload: Payload = {
ticket: {
subject,
assignee_id: assigneeId,
collaborators: formattedCollaborators,
group_id: groupId,
is_public: publicOrNot,
tags: formattedTags,
status,
type,
due_at: dueAt,
priority,
submitter_id: submitterId,
ticket_form_id: ticketForm,
sharing_agreement_ids: sharingAgreementIds,
brand_id: brandId,
},
};
if (requesterName && requesterEmail) {
payload.ticket.requester = {
name: requesterName,
email: requesterEmail,
};
}
if (format === 'HTML') {
payload.ticket.comment = {
html_body: comment,
};
} else {
payload.ticket.comment = {
body: comment,
};
}
const response = await $.http.post('/api/v2/tickets', payload);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,102 @@
export const fields = [
{
label: 'Name',
key: 'name',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Email',
key: 'email',
type: 'string' as const,
required: true,
variables: true,
description:
'It is essential to be distinctive. Zendesk prohibits the existence of identical users sharing the same email address.',
},
{
label: 'Details',
key: 'details',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
{
label: 'Notes',
key: 'notes',
type: 'string' as const,
required: false,
variables: true,
description:
'Within this field, you have the capability to save any remarks or comments you may have concerning the user.',
},
{
label: 'Phone',
key: 'phone',
type: 'string' as const,
required: false,
variables: true,
description:
"The user's contact number should be entered in the following format: +1 (555) 123-4567.",
},
{
label: 'Tags',
key: 'tags',
type: 'string' as const,
required: false,
variables: true,
description: 'A comma separated list of tags.',
},
{
label: 'Role',
key: 'role',
type: 'string' as const,
required: false,
variables: true,
description:
"It can take on one of the designated roles: 'end-user', 'agent', or 'admin'. If a different value is set or none is specified, the default is 'end-user.'",
},
{
label: 'Organization',
key: 'organizationId',
type: 'dropdown' as const,
required: false,
variables: true,
description: 'Assign this user to a specific organization.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listOrganizations',
},
],
},
},
{
label: 'External Id',
key: 'externalId',
type: 'string' as const,
required: false,
variables: true,
description:
'An exclusive external identifier; you can utilize this to link organizations with an external record.',
},
{
label: 'Verified',
key: 'verified',
type: 'dropdown' as const,
required: false,
description:
"Specify if you can verify that the user's assertion of their identity is accurate.",
variables: true,
options: [
{ label: 'True', value: 'true' },
{ label: 'False', value: 'false' },
],
},
];

View File

@@ -0,0 +1,53 @@
import { IJSONObject } from '@automatisch/types';
import defineAction from '../../../../helpers/define-action';
import { fields } from './fields';
type Payload = {
user: IJSONObject;
};
export default defineAction({
name: 'Create user',
key: 'createUser',
description: 'Creates a new user.',
arguments: fields,
async run($) {
const {
name,
email,
details,
notes,
phone,
role,
organizationId,
externalId,
verified,
} = $.step.parameters;
const tags = $.step.parameters.tags as string;
const formattedTags = tags.split(',');
const payload: Payload = {
user: {
name,
email,
details,
notes,
phone,
organization_id: organizationId,
external_id: externalId,
verified: verified || 'false',
tags: formattedTags,
},
};
if (role) {
payload.user.role = role;
}
const response = await $.http.post('/api/v2/users', payload);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,35 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Delete ticket',
key: 'deleteTicket',
description: 'Deletes an existing ticket.',
arguments: [
{
label: 'Ticket',
key: 'ticketId',
type: 'dropdown' as const,
required: true,
variables: true,
description: 'Select the ticket you want to delete.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listFirstPageOfTickets',
},
],
},
},
],
async run($) {
const ticketId = $.step.parameters.ticketId;
const response = await $.http.delete(`/api/v2/tickets/${ticketId}`);
$.setActionItem({ raw: { data: response.data } });
},
});

View File

@@ -0,0 +1,43 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Delete user',
key: 'deleteUser',
description: 'Deletes an existing user.',
arguments: [
{
label: 'User',
key: 'userId',
type: 'dropdown' as const,
required: true,
variables: true,
description: 'Select the user you want to modify.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.showUserRole',
value: 'true',
},
{
name: 'parameters.includeAllUsers',
value: 'true',
},
],
},
},
],
async run($) {
const userId = $.step.parameters.userId;
const response = await $.http.delete(`/api/v2/users/${userId}`);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,32 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Find ticket',
key: 'findTicket',
description: 'Finds an existing ticket.',
arguments: [
{
label: 'Query',
key: 'query',
type: 'string' as const,
required: true,
variables: true,
description:
'Write a search string that specifies the way we will search for the ticket in Zendesk.',
},
],
async run($) {
const query = $.step.parameters.query;
const params = {
query: `type:ticket ${query}`,
sort_by: 'created_at',
sort_order: 'desc',
};
const response = await $.http.get('/api/v2/search', { params });
$.setActionItem({ raw: response.data.results[0] });
},
});

View File

@@ -0,0 +1,15 @@
import createTicket from './create-ticket';
import createUser from './create-user';
import deleteTicket from './delete-ticket';
import deleteUser from './delete-user';
import findTicket from './find-ticket';
import updateTicket from './update-ticket';
export default [
createTicket,
createUser,
deleteTicket,
deleteUser,
findTicket,
updateTicket,
];

View File

@@ -0,0 +1,167 @@
export const fields = [
{
label: 'Ticket',
key: 'ticketId',
type: 'dropdown' as const,
required: true,
variables: true,
description: 'Select the ticket you want to change.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listFirstPageOfTickets',
},
],
},
},
{
label: 'Subject',
key: 'subject',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
{
label: 'Assignee',
key: 'assigneeId',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'Note: An error occurs if the assignee is not in the default group (or the specific group chosen below).',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.showUserRole',
value: 'true',
},
{
name: 'parameters.includeAdmins',
value: 'true',
},
],
},
},
{
label: 'Group',
key: 'groupId',
type: 'dropdown' as const,
required: false,
variables: true,
description: 'Allocate this ticket to a specific group.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listGroups',
},
],
},
},
{
label: 'New Status',
key: 'status',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'New', value: 'new' },
{ label: 'Open', value: 'open' },
{ label: 'Pending', value: 'pending' },
{ label: 'Hold', value: 'hold' },
{ label: 'Solved', value: 'solved' },
{ label: 'Closed', value: 'closed' },
],
},
{
label: 'New comment to add to the ticket',
key: 'comment',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
{
label: 'Should the first comment be public?',
key: 'publicOrNot',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Yes', value: 'yes' },
{ label: 'No', value: 'no' },
],
},
{
label: 'Tags',
key: 'tags',
type: 'string' as const,
required: false,
variables: true,
description: 'A comma separated list of tags.',
},
{
label: 'Type',
key: 'type',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Problem', value: 'problem' },
{ label: 'Incident', value: 'incident' },
{ label: 'Question', value: 'question' },
{ label: 'Task', value: 'task' },
],
},
{
label: 'Priority',
key: 'priority',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Urgent', value: 'urgent' },
{ label: 'High', value: 'high' },
{ label: 'Normal', value: 'normal' },
{ label: 'Low', value: 'low' },
],
},
{
label: 'Submitter',
key: 'submitterId',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.includeAdmins',
value: 'false',
},
],
},
},
];

View File

@@ -0,0 +1,57 @@
import defineAction from '../../../../helpers/define-action';
import { fields } from './fields';
import isEmpty from 'lodash/isEmpty';
import omitBy from 'lodash/omitBy';
export default defineAction({
name: 'Update ticket',
key: 'updateTicket',
description: 'Modify the status of an existing ticket or append comments.',
arguments: fields,
async run($) {
const {
ticketId,
subject,
assigneeId,
groupId,
status,
comment,
publicOrNot,
type,
priority,
submitterId,
} = $.step.parameters;
const tags = $.step.parameters.tags as string;
const formattedTags = tags.split(',');
const payload = {
subject,
assignee_id: assigneeId,
group_id: groupId,
status,
comment: {
body: comment,
public: publicOrNot,
},
tags: formattedTags,
type,
priority,
submitter_id: submitterId,
};
const fieldsToRemoveIfEmpty = ['group_id', 'status', 'type', 'priority'];
const filteredPayload = omitBy(
payload,
(value, key) => fieldsToRemoveIfEmpty.includes(key) && isEmpty(value)
);
const response = await $.http.put(`/api/v2/tickets/${ticketId}`, {
ticket: filteredPayload,
});
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="363" height="259" fill="#03363d"><path d="M173.82 40.5v112.86H80.34L173.82 40.5zm0-40.5a46.74 46.74 0 1 1-93.48 0h93.48zm15.4 153.37a46.74 46.74 0 0 1 93.48 0h-93.48zm0-40.5V0h93.5l-93.5 112.86zm52.28 137.06a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H227c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06h25.68zM0 249.4l28.3-28.76H.67v-9.02h40.76v9.2l-28.3 28.75h28.7v9.03H0v-9.2zm73.6.52a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H59.12c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06H84.8zm72.23 4.03c0-15 11.23-24.44 23.6-24.44a20.34 20.34 0 0 1 15.67 7.05v-27.72h10v68.6h-10V252a20.1 20.1 0 0 1-15.76 7.42c-12 0-23.5-9.5-23.5-24.43m39.82-.1a14.92 14.92 0 1 0-14.91 15.32c8.6 0 14.9-6.86 14.9-15.32m73.48 13.6l9.06-4.7a13.44 13.44 0 0 0 12.08 6.86c5.66 0 8.6-2.9 8.6-6.2 0-3.76-5.47-4.6-11.42-5.83-8-1.7-16.33-4.33-16.33-14 0-7.43 7.07-14.3 18.2-14.2 8.77 0 15.3 3.48 19 9.1l-8.4 4.6a12.19 12.19 0 0 0-10.57-5.36c-5.38 0-8.12 2.63-8.12 5.64 0 3.38 4.34 4.32 11.14 5.83 7.74 1.7 16.5 4.23 16.5 14 0 6.48-5.66 15.22-19.06 15.13-9.8 0-16.7-3.95-20.67-10.9m66.9-10.87l-7.93 8.65v12.2h-10v-68.6h10v44.93l21.23-23.3h12.18l-18.4 20.1 18.88 26.88h-11.32l-14.63-20.86zM126.8 210.53c-11.9 0-21.85 7.7-21.85 20.5v27.45h10.2V232.3c0-7.7 4.43-12.32 12-12.32s11.33 4.6 11.33 12.32v26.18h10.14v-27.45c0-12.78-10-20.5-21.85-20.5"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,23 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
import authScope from '../common/auth-scope';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
response_type: 'code',
redirect_uri: redirectUri,
client_id: $.auth.data.clientId as string,
scope: authScope.join(' '),
});
await $.auth.set({
url: `${
$.auth.data.instanceUrl
}/oauth/authorizations/new?${searchParams.toString()}`,
});
}

View File

@@ -0,0 +1,55 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/zendesk/connections/add',
placeholder: null,
description: '',
clickToCopy: true,
},
{
key: 'instanceUrl',
label: 'Zendesk Subdomain Url',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: 'https://{{subdomain}}.zendesk.com',
clickToCopy: false,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const isStillVerified = async ($: IGlobalVariable) => {
await getCurrentUser($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,56 @@
import { IGlobalVariable, IJSONValue, IField } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
import scopes from '../common/auth-scope';
const verifyCredentials = async ($: IGlobalVariable) => {
await getAccessToken($);
const user = await getCurrentUser($);
const subdomain = extractSubdomain($.auth.data.instanceUrl);
const name = user.name as string;
const screenName = [name, subdomain].filter(Boolean).join(' @ ');
await $.auth.set({
screenName,
apiToken: $.auth.data.apiToken,
instanceUrl: $.auth.data.instanceUrl,
email: $.auth.data.email,
});
};
const getAccessToken = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const response = await $.http.post(`/oauth/tokens`, {
redirect_uri: redirectUri,
code: $.auth.data.code,
grant_type: 'authorization_code',
scope: scopes.join(' '),
client_id: $.auth.data.clientId as string,
client_secret: $.auth.data.clientSecret as string,
});
const data = response.data;
$.auth.data.accessToken = data.access_token;
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
accessToken: data.access_token,
tokenType: data.token_type,
});
};
function extractSubdomain(url: IJSONValue) {
const match = (url as string).match(/https:\/\/(.*?)\.zendesk\.com/);
if (match && match[1]) {
return match[1];
}
return null;
}
export default verifyCredentials;

Some files were not shown because too many files have changed in this diff Show More