UNPKG

28.6 kBMarkdownView Raw
1# LSK.js – bots-plugin-portal
2
3> @lskjs/bots-plugin-portal – LSK.js plugin for @lskjs/bots module for telegram, slack, discord, whatsapp, twitter, instagram and vk bots creation
4
5[![LSK logo](https://badgen.net/badge/icon/MADE%20BY%20LSK?icon=zeit\&label\&color=red\&labelColor=red)](https://github.com/lskjs)
6[![NPM version](https://badgen.net/npm/v/@lskjs/bots-plugin-portal)](https://www.npmjs.com/package/@lskjs/bots-plugin-portal)
7[![NPM downloads](https://badgen.net/npm/dt/@lskjs/bots-plugin-portal)](https://www.npmjs.com/package/@lskjs/bots-plugin-portal)
8[![NPM Dependency count](https://badgen.net/bundlephobia/dependency-count/@lskjs/bots-plugin-portal)](https://bundlephobia.com/result?p=@lskjs/bots-plugin-portal)
9[![Have TypeScript types](https://badgen.net/npm/types/@lskjs/bots-plugin-portal)](https://www.npmjs.com/package/@lskjs/bots-plugin-portal)
10[![Have tree shaking](https://badgen.net/bundlephobia/tree-shaking/@lskjs/bots-plugin-portal)](https://bundlephobia.com/result?p=@lskjs/bots-plugin-portal)
11[![NPM Package size](https://badgen.net/bundlephobia/minzip/@lskjs/bots-plugin-portal)](https://bundlephobia.com/result?p=@lskjs/bots-plugin-portal)
12[![Package size](https://badgen.net//github/license/lskjs/lskjs)](https://github.com/lskjs/lskjs/blob/master/LICENSE)
13[![Ask us in Telegram](https://img.shields.io/badge/Ask%20us%20in-Telegram-brightblue.svg)](https://t.me/lskjschat)
14
15<!-- template file="scripts/templates/preview.md" start -->
16
17<!-- template end -->
18
19***
20
21<!-- # 📒 Table of contents -->
22
23# Table of contents
24
25* [⌨️ Install](#️-install)
26
27* [Bots Plugin Portal](#bots-plugin-portal)
28
29 * [Правила (rules)](#правила-rules)
30
31 * [`cron`](#cron)
32 * [`criteria`](#criteria)
33 * [`action`](#action)
34
35 * [Параметры, используемые при настройке критериев:](#параметры-используемые-при-настройке-критериев)
36
37 * [Список действий, которые реализованы в плагине:](#список-действий-которые-реализованы-в-плагине)
38
39 * [createMessage](#createmessage)
40 * [messageAppend](#messageappend)
41 * [messageTrim](#messagetrim)
42 * [messageEditExtra](#messageeditextra)
43 * [messageAddExtra](#messageaddextra)
44 * [sendMessage](#sendmessage)
45 * [reply](#reply)
46 * [repost](#repost)
47 * [copyMessage](#copymessage)
48 * [remove](#remove)
49 * [findMessage](#findmessage)
50 * [pinChatMessage](#pinchatmessage)
51 * [messageSplit](#messagesplit)
52 * [messagesJoin](#messagesjoin)
53 * [checkInterview](#checkinterview)
54 * [replyInterview](#replyinterview)
55
56* [📖 License](#-license)
57
58* [👥 Contributors](#-contributors)
59
60* [👏 Contributing](#-contributing)
61
62* [📮 Any questions? Always welcome :)](#-any-questions-always-welcome-)
63
64# ⌨️ Install
65
66```sh
67# yarn
68yarn i @lskjs/bots-plugin-portal bluebird lodash
69
70# npm
71npm i @lskjs/bots-plugin-portal bluebird lodash
72```
73
74***
75
76# Bots Plugin Portal
77
78**Bots Plugin Portal** (*@lskjs/bots-plugin-portal*) - плагин, позволяющий настраивать реакции бота на различные триггеры.
79
80Конфиг плагина находится по пути `bots.plugins.portal` и имеет вид:
81
82```js
83bots: {
84 plugins: {
85 portal: {
86 group: true,
87 rules: [
88 //...
89 ],
90 },
91 },
92},
93```
94
95**Params:**
96
97| Field | Type | Desription |
98| ------ | :------: | ------ |
99| group | Boolean | Группировать ли медиа-файлы |
100| rules | Array of Objects | Массив правил, которым следует бот при обработке входящих сообщений или при срабатывании крона |
101
102**Group values:**
103
104| Field | Value | Description |
105| ------ | :------: | ------ |
106| group | true | Группирует медиа-файлы в одно сообщение и помещает их в `ctx.group` |
107| group | false | Медиа-файлы приходят на сервер разными сообщениями |
108
109 /Reference/: При отправке боту нескольких медиа-файлов одним сообщением, на сервер медиа-файлы приходят разными сообщениями. Т.е. при отправке 5 изображений одним сообщением, на сервер приходит не одно сообщение, а 5.
110
111***
112
113## Правила (rules)
114
115Правила позволяют устанавливать триггеры на действия пользователей или на срабатывание крона. Представляют из себя объекты и могут быть вложены друг в друга.
116
117**Правила состоят из 3-х частей:**
118| Rule Part | Type | Required | Description |
119| ------ | :------: | :------: | ------ |
120| cron | Array of String\String | - | Позволяет устанавливать расписания срабатываний экшона |
121|criteria| Object | - | Критерии срабатывания экшона при обработке действий пользователя |
122| action | Object | + | Экшоны. Описания действий, которые выполняет бот |
123
124### `cron`
125
126Параметр позволяет устанавливать время срабатывания действия бота. Например, если необходимо установить отправку сообщений в чат каждую минуту, то конфиг будет выглядеть `cron: '* * * * *'`.
127
128Официальный пакет крона: [node-cron - npm](https://www.npmjs.com/package/node-cron)
129
130### `criteria`
131
132Параметр позволяет устанавливать критерии на триггеры срабатывания бота. Например, если необходимо, чтобы бот реагировал только на сообщения в определенном чате, то критерий будут выглядеть `chatId: 12345678`.
133
134Если criteria отсутствует, то правила могут срабатывать только по крону. Если же `criteria: {}`, то считается, что критерий отбора нет и правила будут срабатывать на любые взаимодействия с ботом.
135
136### `action`
137
138Параметр задает действия бота. Здесь настраивается, будет бот искать сообщения, оправлять или удалять их и тд.
139
140Действия могут быть вложенными и составлять цепочки при помощи полей `then` и `else`. При удачном выполнении действия-родителя бот будет выполнять действия из поля `then`. При неудачном - `else`. Аналогия с if/else, где if - then, а else - else. Например, при проверке checkMessage, если сообщение существует, то бот пройдет по ветке `then`, в противном случае по `else`.
141
142Кроме вложенности, action/then могут быть массивами и содержать параллельные действия.
143
144**Examples:**
145
146```js
147{
148 cron: '* * * * *',
149 criteria: {
150 chatId: 12345678,
151 },
152 action: {
153 type: 'checkMessage',
154 chatId: -123114346456,
155 userId: 12345678,
156 then: {
157 type: 'sendMessage',
158 text: 'some text by true checkMessage',
159 to: 12345678,
160 },
161 else: {
162 type: 'sendMessage',
163 text: 'some text by false checkMessage',
164 to: 12345678,
165 },
166 },
167},
168```
169
170```js
171{
172 cron: ['*/20 * * * *', '*/15 * * * * *'],
173 action: [
174 {
175 type: 'sendMessage',
176 text: 'some text 1',
177 to: 12345678,
178 },
179 {
180 type: 'sendMessage',
181 text: 'some text 2',
182 to: -87654321123,
183 },
184 ],
185}
186```
187
188```js
189{
190 criteria: {
191 chatId: 12345678,
192 },
193 action: {
194 type: 'reply',
195 text: 'some text by reply',
196 },
197},
198```
199
200## Параметры, используемые при настройке критериев:
201
202| Criteria Field | Type | Values | Description |
203| ------ | :------: | ------ | ------ |
204| userId | Array of Number\Array of String\Number\String | | ID пользователя, который взаимодействует с ботом |
205| chatId | Array of Number\Array of String\Number\String | | ID чата, в котором происходит взаимодействие с ботом |
206| chatType | String | private\group\supergroup\channel | Тип чата, в котором происходит взаимодействие |
207| messageType | String | | Тип сообщения. Различают множество типов сообщений |
208| messageText | String\RegExp | | Текст сообщения. Например, можно сделать критерий на команду запуска бота `messageText: /start` |
209| nextRoute | String\RegExp | | Следующий роут. Используется только при заполнении формы. Необходимо устанавливать данный критерий для блокировки каких-либо действий во время заполнения формы в целях избежать спама и лишних триггеров. |
210
211**Chat types:**
212
2131. `private` - личные диалог с ботом
2142. `group` - общий чат
2153. `supergroup` - общий чат
2164. `channel` - канал
217
218**Message types:**
219
2201. `mediaGroup` - группа медиа-файлов
2212. `audio` - приложен аудио-файл
2223. `document` - приложен документ
2234. `animation` - гифка
2245. `photo` - приложено изображение
2256. `sticker` - стикер
2267. `video` - приложено видео
2278. `video_note` - видео-кружочек
2289. `voice` - голосовое сообщение
22910. `contact` - контакт
23011. `dice` - игральная кость
23112. `game` - игра
23213. `poll` - голосование
23314. `quiz` - викторина
23415. `location` - геоданные
23516. `venue` - место
23617. `text` - текстовое сообщение
237
238## Список действий, которые реализованы в плагине:
239
2401. [createMessage](#createMessage) - создание сообщений
2412. [messageAppend](#messageAppend) - добавление текста в конец
2423. [messageTrim](#messageTrim) - фильтр текста
2434. [messageEditExtra](#messageEditExtra) - редактирование кнопок
2445. [messageAddExtra](#messageAddExtra) - добавление кнопок
2456. [sendMessage](#sendMessage) - отправка сообщений
2467. [reply](#reply) - реплай
2478. [repost](#repost) - репост
2489. [copyMessage](#copyMessage) - копирование сообщений
24910. [remove](#remove) - удаление сообщений
25011. [findMessage](#findMessage) - поиск сообщения
25112. [pinChatMessage](#pinChatMessage) - закрепление сообщения
25213. [messageSplit](#messageSplit) - разделение сообщения на элементы
25314. [messagesJoin](#messagesJoin) - группировка сообщений в одно
25415. [checkInterview](#checkInterview) - проверка заполнения формы
25516. [replyInterview](#replyInterview) - отправка формы
256
257### createMessage
258
259**createMessage** - действие бота, необходимое для создания сообщения и последующего редактирования перед отправкой. После создания сообщения, к нему можно добавить/изменить кнопки, отредактировать его текст и тд.
260
261При срабатывании createMessage от крона, создается пустое сообщение, которое можно заполнять различным контентом.<br/>
262При срабатывании createMessage от действия пользователя, сообщение создается на основе входящих данных. Т.е. в текст созданного сообщения помещается текст, написанный пользователем. С кнопками и файлами происходят аналогичные действия.
263
264**Params:**
265
266| Field | Type | Description |
267| ------ | :------: | ------ |
268| text | String | текст сообщения |
269| to | Array of Number\Array of String\Number\String | ID чата, в который будет отправлено сообщение |
270
271**Example:**
272
273```js
274{
275 criteria: {},
276 action: {
277 type: 'createMessage',
278 text: "It's field from message text",
279 to: 12345678,
280 },
281},
282```
283
284### messageAppend
285
286**messageAppend** - действие бота, позволяющее добавить текст в конец сообщения.
287
288**Params:**
289
290| Field | Type | Description |
291| ------ | :------: | ------ |
292| text | String | Добавочный текст |
293
294**Example:**
295
296```js
297{
298 criteria: {
299 messageText: /\/trim/,
300 },
301 action: {
302 type: 'messageAppend',
303 text: 'by @download4bot',
304 then: {
305 type: 'repost',
306 to: 12345678,
307 },
308 },
309},
310
311```
312
313### messageTrim
314
315**messageTrim** - действие бота, позволяющее отфильтровать текст сообщения.
316
317**Params:**
318
319| Field | Type | Description |
320| ------ | :------: | ------ |
321| hashtags | Boolean |Удаляет все хештеги из текста сообщения |
322| links | Boolean | Удаляет все ссылки из текста сообщения |
323| regExp | RegExp | Позволяет удалять любые пользовательские шаблоны из текста сообщения |
324
325**Example:**
326
327```js
328{
329 criteria: {
330 messageText: /\/trim/,
331 },
332 action: {
333 type: 'messageTrim',
334 hashtags: 1,
335 links: 1,
336 regExp: /1?123\n?/,
337 then: {
338 type: 'repost',
339 to: 12345678,
340 },
341 },
342},
343
344```
345
346### messageEditExtra
347
348**messageEditExtra** - действие бота, позволяющее отредактировать клавиатуру сообщения.
349
350**Params:**
351
352| Field | Type | Description |
353| ------ | :------: |------ |
354| extra | Array | Содержит редактируемые кнопки клавиатуры |
355
356**Extra params for type *LIKE***:
357
358| Field | Type | Required | Description |
359| ------ | :------: | :------: | ------ |
360| type | String | + | Кнопки лайк/дисслайк к сообщению |
361| buttons | Array | - | Содержит редактируемые кнопки клавиатуры |
362| buttons.disslike | Object | - | Настройка кнопки дисслайка |
363| buttons.like | Object | - | Настройка кнопки дисслайка |
364
365**Extra params for type *ANSWER***:
366
367| Field | Type | Required | Description |
368| ------ | :------: | :------: | ------ |
369| type | String | + | Кнопка для установления соединения с другим пользователем через бота (чат) |
370| text | String | - | Настройка текста кнопки |
371
372**Extra params for type *SENDER***:
373
374| Field | Type | Required | Description |
375| ------ | :------: | :------: | ------ |
376| type | String | + | Кнопка для перехода к отправителю сообщения |
377| text | String | - | Настройка текста кнопки |
378
379**Example:**
380
381```js
382{
383 criteria: {}
384 action: {
385 type: 'createMessage',
386 text: 'Title text',
387 then: {
388 type: 'messageEditExtra',
389 extra: [
390 {
391 type: 'like',
392 buttons: {
393 disslike: {
394 title: 'test 💔', // default: '💔'
395 value: 10, // default: 0
396 },
397 like: {
398 title: 'test ❤️', // default: '❤️'
399 value: 0, // default: 0
400 },
401 },
402 },
403 {
404 type: 'answer',
405 text: 'Answer @{ {username} }', // default: '@username'
406 },
407 {
408 type: 'sender',
409 text: 'Sender: @{ {username} }', // default: '@username'
410 },
411 ],
412 then: {
413 type: 'sendMessage',
414 to: 12345678,
415 },
416 },
417 },
418},
419```
420
421### messageAddExtra
422
423**messageAddExtra** - действие бота, позволяющее добавить новую клавиатуру сообщения. Если клавиатура уже существует, то messageAddExtra заменит текущую на новую.
424
425**Params:**
426| Field | Type | Description |
427| ------ | :------: | ------ |
428| extra | Array | Содержит редактируемые кнопки клавиатуры |
429
430**Extra params for type *LIKE***:
431
432| Field | Type | Required | Description |
433| ------ | :------: | :------: | ------ |
434| type | String | + | Кнопки лайк/дисслайк к сообщению |
435| buttons | Array | - | Содержит редактируемые кнопки клавиатуры |
436| buttons.disslike | Object | - | Настройка кнопки дисслайка |
437| buttons.like | Object | - | Настройка кнопки дисслайка |
438
439**Extra params for type *ANSWER***:
440
441| Field | Type | Required | Description |
442| ------ | :------: | :------: | ------ |
443| type | String | + | Кнопка для установления соединения с другим пользователем через бота (чат) |
444| text | String | - | Настройка текста кнопки |
445
446**Extra params for type *SENDER***:
447
448| Field | Type | Required | Description |
449| ------ | :------: | :------: | ------ |
450| type | String | + | Кнопка для перехода к отправителю сообщения |
451| text | String | - | Настройка текста кнопки |
452
453**Example:**
454
455```js
456{
457 criteria: {}
458 action: {
459 type: 'createMessage',
460 text: 'Title text',
461 then: {
462 type: 'messageAddExtra',
463 extra: [
464 {
465 type: 'like',
466 buttons: {
467 disslike: {
468 title: 'New disslike 💔', // default: '💔'
469 value: 10, // default: 0
470 },
471 like: {
472 title: 'New like ❤️', // default: '❤️'
473 value: 0, // default: 0
474 },
475 },
476 },
477 {
478 type: 'answer',
479 text: 'Answer @{ {username} }', // default: '@username'
480 },
481 {
482 type: 'sender',
483 text: 'Sender: @{ {username} }', // default: '@username'
484 },
485 ],
486 then: {
487 type: 'sendMessage',
488 to: 12345678,
489 },
490 },
491 },
492},
493```
494
495### sendMessage
496
497**sendMessage** - действие бота, при котором бот отправляет сообщение заданному пользователю. Для настройки сообщения используется цепочка действий: `createMessage` -> `messageAddExtra` -> `messageAppend` -> `messageTrim` -> `sendMessage`.
498
499**Params:**
500
501| Field | Type | Required | Description |
502| ------ | :------: | :------: | ------ |
503| text | String | - | Текст сообщения |
504| to | Array of String/Array of Number/String/Number | - | ID получателя(-ей) |
505
506**Example:**
507
508```js
509{
510 criteria: {},
511 action: {
512 type: 'createMessage',
513 then: {
514 type: 'sendMessage',
515 text: 'Text by sendMessage',
516 to: 12345678,
517 },
518 },
519},
520```
521
522### reply
523
524**reply** - действие бота, при котором бот отвечает на сообщение пользователя.
525
526**Params:**
527
528| Field | Type | Required | Description |
529| ------ | :------: | :------: | ------ |
530| text | String | - | Текст сообщения |
531
532**Example:**
533
534```js
535{
536 criteria: {
537 messageText: 'ping',
538 },
539 action: {
540 type: 'reply',
541 text: 'pong',
542 },
543},
544```
545
546### repost
547
548**repost** - действие бота, при котором бот пересылает активное сообщение в заданных чат.
549
550**Params:**
551
552| Field | Type | Required | Description |
553| ------ | :------: | :------: | ------ |
554| to | Array of String/Array of Number/String/Number | - | ID получателя(-ей) |
555
556**Example:**
557
558```js
559{
560 criteria: {
561 chatId: -136512436512436,
562 },
563 action: {
564 type: 'repost',
565 to: 12345678,
566 },
567},
568```
569
570### copyMessage
571
572**sendMessage** - действие бота, при котором бот копирует активное сообщение и пересылает его в заданных чат.
573
574**Params:**
575
576| Field | Type | Required | Description |
577| ------ | :------: | :------: | ------ |
578| to | Array of String/Array of Number/String/Number | - | ID получателя(-ей) |
579
580**Example:**
581
582```js
583{
584 criteria: {
585 chatId: -136512436512436,
586 },
587 action: {
588 type: 'copyMessage',
589 to: 12345678,
590 },
591},
592```
593
594### remove
595
596**remove** - действие бота, при котором бот удаляет активное сообщение. Активным сообщением является полученное сообщение, либо найденное при помощи findMessage.
597
598**Example:**
599
600```js
601{
602 criteria: {
603 messageText: 'попит',
604 },
605 action: {
606 type: 'remove',
607 },
608},
609```
610
611### findMessage
612
613**findMessage** - действие бота, позволяющее найти сообщение в чате по заданным параметрам. Поиск осуществляется в модели `BotsTelegramMessageModel`.
614
615**Params:**
616
617| Field | Type | Required | Description |
618| ------ | :------: | :------: | ------ |
619| random | Boolean | - | Если нашлось несколько сообщений, выбирать ли случайное. По умолчанию: последнее полученное ботом сообщение. |
620| criteria | Object | - | Критерии поиска сообщения в базе |
621| criteria.userId | Array of Number\Array of String\Number\String | - | ID пользователя |
622| criteria.chatId | Array of Number\Array of String\Number\String | - | ID чата |
623| criteria.chatType | String | - | Тип чата |
624| criteria.messageType | String | - | Тип сообщения |
625| criteria.messageDate | Number | - | Дата сообщения в ms |
626| criteria.messageText | String\RegExp | - | Текст сообщения |
627| criteria.messageId | String\RegExp | - | ID сообщения |
628
629**Example:**
630
631```js
632{
633 criteria: {
634 messageText: '/checkMessage',
635 },
636 action: {
637 type: 'findMessage',
638 criteria: {
639 chatId: -1232343354655,
640 messageText: '*message.text',
641 },
642 then: {
643 type: 'repost',
644 to: 12345678,
645 },
646 },
647},
648```
649
650### pinChatMessage
651
652**pinChatMessage** - действие бота, при котором бот закрепляет активное сообщение. Активным сообщением является полученное сообщение, либо сообщение после `findMessage`/`createMessage`.
653
654**Example:**
655
656```js
657{
658 criteria: {
659 messageText: /^#закрепить.*/,
660 },
661 action: {
662 type: 'pinChatMessage',
663 },
664},
665```
666
667### messageSplit
668
669**messageSplit** - действие бота, позволяющее разделять входящее сообщение на элементы для последующей работы. Например, если пользователь пишет боту сообщение, содержащее 5 изображений и подпись, то бот разделит сообщение на 6 элементов.
670
671**Example:**
672
673```js
674{
675 criteria: {},
676 action: {
677 type: 'messageSplit',
678 then: {
679 type: 'sendMessage',
680 to: 12345678,
681 },
682 },
683},
684```
685
686### messagesJoin
687
688**messagesJoin** - действие бота, позволяющее объединять несколько сообщений, переданных боту (например, пересылая сообщения). Если среди сообщений несколько подписей, то они объединятся в единый текст.
689
690**Example:**
691
692```js
693{
694 criteria: {},
695 action: {
696 type: 'messagesJoin',
697 then: {
698 type: 'sendMessage',
699 to: 12345678,
700 },
701 },
702},
703```
704
705### checkInterview
706
707**checkInterview** - действие бота для проверки, заполнил ли пользователь необходимые формы.
708
709**Params:**
710
711| Field | Type | Description |
712| ------ | :------: | ------ |
713| type | String | Тип действия бота. Запускает проверку на заполненность формы |
714| forms | Array of String/String | Список с названиями форм, которые следует заполнить для прохождения проверки |
715
716**Example:**
717
718```js
719{
720 criteria: {
721 messageText: /^(?!\/registration).*$/,
722 nextRoute: /^(?!\/interview).*$/,
723 },
724 action: {
725 type: 'checkInterview',
726 forms: ['intro'],
727 then: [
728 type: 'reply',
729 text: 'Успех!',
730 ],
731 else: {
732 type: 'reply',
733 text: 'Необходима регистрация! /registration'
734 },
735 },
736},
737```
738
739### replyInterview
740
741**replyInterview** - действие бота для отправки формы пользователю.
742
743**Params:**
744
745| Field | Type | Value | Description |
746| ------ | :------: | ------ | ------ |
747| type | String | | Тип действия бота. Отправляет форму пользователю |
748| formName | String | | Название формы, которая будет отправлена пользователю |
749| mode | String | form/dialog | Режим работы формы |
750| preview | Boolean | | Параметр, который указывает, необходимо ли выводить title формы перед вводом данных |
751| autosubmit | Boolean | | Параметр, который указывает, необходимо ли подтверждение данных формы |
752
753**Example:**
754
755```js
756{
757 criteria: {
758 chatType: 'private',
759 messageText: '/registration',
760 },
761 action: {
762 type: 'replyInterview',
763 formName: 'intro',
764 mode: 'form',
765 preview: false,
766 autosubmit: false,
767 then: {
768 type: 'reply',
769 text: 'Успех!',
770 },
771 },
772},
773```
774
775# 📖 License
776
777This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
778
779# 👥 Contributors
780
781<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
782
783<!-- prettier-ignore-start -->
784
785<!-- markdownlint-disable -->
786
787<table>
788 <tr>
789 <td align="center"><a href="https://isuvorov.com"><img src="https://avatars2.githubusercontent.com/u/1056977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Igor Suvorov</b></sub></a><br /><a href="lskjs/lskjs///commits?author=isuvorov" title="Code">💻</a> <a href="#design-isuvorov" title="Design">🎨</a> <a href="#ideas-isuvorov" title="Ideas, Planning, & Feedback">🤔</a></td>
790 </tr>
791</table>
792<!-- markdownlint-restore -->
793<!-- prettier-ignore-end -->
794<!-- ALL-CONTRIBUTORS-LIST:END -->
795
796# 👏 Contributing
797
7981. Fork it (<https://github.com/yourname/yourproject/fork>)
7992. Create your feature branch (`git checkout -b features/fooBar`)
8003. Commit your changes (`git commit -am 'feat(image): Add some fooBar'`)
8014. Push to the branch (`git push origin feature/fooBar`)
8025. Create a new Pull Request
803
804# 📮 Any questions? Always welcome :)
805
806* [Email](mailto:hi@isuvorov.com)
807* [LSK.news – Telegram channel](https://t.me/lskjs)
808* [Спроси нас в телеграме ;)](https://t.me/lskjschat)