Skip to content

Commit

Permalink
feat: add a new module stateless-prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
negezor committed Sep 27, 2021
1 parent a53820c commit a7ba981
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ run().catch(console.log);
## Community
### Useful modules that may be useful to you

* [@vk-io/stateless-prompt](packages/stateless-prompt): Simple implementation of stateless prompt
* [@vk-io/authorization](packages/authorization): Authorization by login & password, and etc...
* [@vk-io/streaming](packages/streaming): Receiving data with Streaming API
* [@vk-io/session](packages/session): Simple implementation of the sessions
Expand Down
57 changes: 57 additions & 0 deletions packages/stateless-prompt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# VK-IO Scenes
<a href="https://www.npmjs.com/package/@vk-io/stateless-prompt"><img src="https://img.shields.io/npm/v/@vk-io/stateless-prompt.svg?style=flat-square" alt="NPM version"></a>
<a href="https://github.com/negezor/vk-io/actions/workflows/tests.yml"><img src="https://img.shields.io/github/workflow/status/negezor/vk-io/VK-IO CI?style=flat-square" alt="Build Status"></a>
<a href="https://www.npmjs.com/package/@vk-io/stateless-prompt"><img src="https://img.shields.io/npm/dt/@vk-io/stateless-prompt.svg?style=flat-square" alt="NPM downloads"></a>
<a href="https://www.codacy.com/app/negezor/vk-io"><img src="https://img.shields.io/codacy/grade/25ee36d46e6e498981a74f8b0653aacc.svg?style=flat-square" alt="Code quality"></a>

VK-IO Stateless Prompt - Simple implementation of middleware-based stateless prompt

![Example](https://user-images.githubusercontent.com/9392723/134985949-e5cf1758-0469-428e-85ed-12229e36e58b.png)

## Installation
> **[Node.js](https://nodejs.org/) 12.0.0 or newer is required**
### Yarn
Recommended
```
yarn add @vk-io/stateless-prompt
```

### NPM
```
npm i @vk-io/stateless-prompt
```

## Example usage
```js
import { VK } from 'vk-io';

import { StatelessPromptManager } from '@vk-io/stateless-prompt';

const vk = new VK({
token: process.env.TOKEN
});

const namePrompt = new StatelessPromptManager({
slug: 'name',
handler: (context, next) => {
if (!context.text) {
return context.send('Please reply your name with text to previous message');
}

return context.send(`Your name is ${context.text}`);
}
});

vk.updates.on('message_new', namePrompt.middlewareIntercept);

vk.updates.on('message_new', (context, next) => {
if (context.text === '/signup') {
return context.send('What\'s your name? Please reply to this message. ' + namePrompt.suffix);
}

return next();
});

vk.updates.start().catch(console.error);
```
42 changes: 42 additions & 0 deletions packages/stateless-prompt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@vk-io/stateless-prompt",
"version": "1.0.0",
"description": "Stateless prompts for the library vk-io",
"license": "MIT",
"author": {
"name": "Vladlen (Negezor)",
"email": "[email protected]"
},
"repository": {
"type": "git",
"url": "git+https://github.com/negezor/vk-io.git",
"directory": "packages/stateless-prompt"
},
"homepage": "https://github.com/negezor/vk-io#readme",
"bugs": "https://github.com/negezor/vk-io/issues",
"keywords": [
"vk",
"io",
"stateless",
"prompt"
],
"files": [
"lib",
"typings"
],
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
".": {
"import": "./lib/index.mjs",
"require": "./lib/index.js"
}
},
"sideEffects": false,
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"vk-io": "^4.0.0"
}
}
9 changes: 9 additions & 0 deletions packages/stateless-prompt/src/identifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createHash } from 'crypto';

export const getDataHash = (data: string): string => (
createHash('shake256', {
outputLength: 2
})
.update(data)
.digest('hex')
);
6 changes: 6 additions & 0 deletions packages/stateless-prompt/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './types';
export * from './stateless-prompt.types';

export { StatelessPromptManager } from './stateless-prompt-manager';

export { getDataHash } from './identifier';
51 changes: 51 additions & 0 deletions packages/stateless-prompt/src/stateless-prompt-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { getDataHash } from './identifier';

import { MessageContext, Middleware, HandlerMiddleware } from './types';
import { IStatelessPromptManagerOptions } from './stateless-prompt.types';

export class StatelessPromptManager {
protected slug: string;

protected uniqueIdentifier: string;

protected handler: HandlerMiddleware;

public constructor({ slug, handler }: IStatelessPromptManagerOptions) {
this.slug = slug;
this.uniqueIdentifier = getDataHash(slug);

this.handler = handler;
}

/**
* The suffix to add to the end of the message.
* Due to this, the bot understands who is being addressed.
*
* ```ts
* context.send('How old are you? Please reply to this message. ' + agePrompt.suffix);
* ```
*/
public get suffix(): string {
return this.uniqueIdentifier;
}

/**
* Returns the middleware for intercept
*
* ```ts
* updates.on('message_new', agePrompt.middlewareIntercept);
* ```
*/
// eslint-disable-next-line class-methods-use-this
public get middlewareIntercept(): Middleware<MessageContext> {
return (context: MessageContext, next: Function): unknown => {
const { replyMessage } = context;

if (!replyMessage?.text?.endsWith(this.uniqueIdentifier)) {
return next();
}

return this.handler(context, next);
};
}
}
27 changes: 27 additions & 0 deletions packages/stateless-prompt/src/stateless-prompt.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { HandlerMiddleware } from './types';

export interface IStatelessPromptManagerOptions {
/**
* The slug should be unique.
* It depends on it that the bot will respond and recognize the request.
*/
slug: string;

/**
* Handles the prompt
*
* ```ts
* const namePrompt = new StatelessPromptManager({
* slug: 'prompt-name',
* handler: (context, next) => {
* if (!context.text) {
* return context.send('Please reply your name with text to previous message');
* }
*
* return context.send(`Your name is ${context.text}`);
* }
* });
* ```
*/
handler: HandlerMiddleware;
}
7 changes: 7 additions & 0 deletions packages/stateless-prompt/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { MessageContext } from 'vk-io';

export type Middleware<T> = (context: T, next: Function) => unknown;

export type HandlerMiddleware = Middleware<MessageContext>;

export { MessageContext };
3 changes: 2 additions & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const MODULES = [
'scenes',
'session',
'streaming',
'authorization'
'authorization',
'stateless-prompt'
];

const coreModules = builtinModules.filter(name => (
Expand Down

0 comments on commit a7ba981

Please sign in to comment.