Skip to content

Commit

Permalink
feat: tts and sync
Browse files Browse the repository at this point in the history
  • Loading branch information
TheEVolk committed Jan 31, 2023
1 parent e39b2ca commit 51d4c97
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 25 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# yandex-alice-client
![Downloads](https://img.shields.io/npm/dm/yandex-alice-client.svg)
Клиент для отправки запросов Яндекс Алисе и получения от неё ответов.

## Как использовать
Expand Down
2 changes: 1 addition & 1 deletion examples/audio.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import YandexAliceClient from '../lib/index.mjs';
const client = new YandexAliceClient();
await client.connect();

const response = await client.sendText('hello world', true);
const response = await client.sendText('hello world', { isTTS: true });
console.log(response);

await writeFile('response.opus', response.audio);
Expand Down
2 changes: 1 addition & 1 deletion examples/readline.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ const client = new YandexAliceClient();
await client.connect();

rl.on('line', async (line) => {
const { response } = await client.sendText(line.toString(), true);
const { response } = await client.sendText(line.toString());
console.log('[A]', response.card.text);
});
30 changes: 30 additions & 0 deletions examples/skill.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
import YandexAliceClient from '../lib/index.mjs';
import { v4 } from 'uuid';

const rl = readline.createInterface({ input, output });

const client = new YandexAliceClient();
await client.connect();

console.log(
'sync',
await client.synchronizeState({
auth_token: await rl.question('Введите auth_token (ya.ru > Сеть > WS)'),
uuid: v4(),
lang: 'ru-RU',
voice: 'levitan'
}).catch(() => {})
);

await client.sendText('запусти навык занимательные истории')
.then(v => console.log('[A]', v.response.card.text));

await client.sendText('да')
.then(v => console.log('[A]', v.response.card.text));

rl.on('line', async (line) => {
const { response } = await client.sendText(line.toString());
console.log('[A]', response.card.text);
});
10 changes: 10 additions & 0 deletions examples/tts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { writeFile } from 'node:fs/promises';
import YandexAliceClient from '../lib/index.mjs';

const client = new YandexAliceClient();
await client.connect();

const audio = await client.tts('Привет, меня зовут Алиса');
await writeFile('response.opus', audio);

await client.close();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"engines": {
"node": ">=12.20.0"
},
"version": "0.2.2",
"version": "0.3.0",
"dependencies": {
"uuid": "^9.0.0",
"ws": "^8.12.0"
Expand Down
64 changes: 48 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { v4 } from 'uuid';
import type { IAliceClientOptions, IAliceSendTextResponse } from './types';
import type { IAliceClientOptions, IAliceSendTextOptions, IAliceSendTextResponse, IAliceTTSOptions } from './types';
import Uniproxy from './uniproxy.js';

export default class YandexAliceClient {
Expand All @@ -13,24 +13,31 @@ export default class YandexAliceClient {
await this.uniproxy.connect(this.options.server);
}

async sendText(text: string, isTTS = false): Promise<IAliceSendTextResponse> {
public async synchronizeState(data) {
this.uniproxy.sendEvent('System', 'SynchronizeState', data);
}

async sendText(text: string, options: Partial<IAliceSendTextOptions> = {}): Promise<IAliceSendTextResponse> {
if (typeof options === 'boolean') {
options = {};
}

options = this.normalizeSendTextOptions(options);

const messageId = this.uniproxy.sendEvent('Vins', 'TextInput', {
request: {
voice_session: !!isTTS,
voice_session: options.isTTS,
event: {
type: 'text_input',
text
}
},
application: this.getApplication(),
header: {
request_id: v4()
}
application: this.getApplication()
});

const response = await this.uniproxy.receiveData(
messageId,
isTTS ? ['VinsResponse', 'audio'] : ['VinsResponse']
options.isTTS ? ['VinsResponse', 'audio'] : ['VinsResponse']
);

return {
Expand All @@ -39,21 +46,46 @@ export default class YandexAliceClient {
};
}

private getApplication() {
async tts(text: string, options: Partial<IAliceTTSOptions> = {}) {
const messageId = this.uniproxy.sendEvent('TTS', 'Generate', {
voice: options.voice || 'shitova.us',
lang: options.voice || 'ru-RU',
format: options.voice || 'audio/opus',
emotion: options.voice || 'neutral',
quality: options.voice || 'UltraHigh',
text
});

const response = await this.uniproxy.receiveData(
messageId,
['audio']
);

return response.audio;
}

public getApplication() {
return {
app_id: "aliced",
app_version: "1.2.3",
os_version: "5.0",
platform: "android",
app_id: 'aliced',
app_version: '1.2.3',
os_version: '5.0',
platform: 'android',
uuid: v4(),
lang: "ru-RU",
lang: 'ru-RU',
client_time: new Date().toDateString(),
timezone: "Europe/Moscow",
timezone: 'Europe/Moscow',
timestamp: Math.floor(Date.now() / 1e3).toString(),
};
}

close() {
public close() {
this.uniproxy.close();
}

private normalizeSendTextOptions(rawOptions: Partial<IAliceSendTextOptions>) {
const options = { ...rawOptions };
options.isTTS = options.isTTS ?? false;

return options;
}
}
12 changes: 12 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ export interface IAliceClientOptions {
server?: string;
}

export interface IAliceSendTextOptions {
isTTS: boolean;
}

export interface IAliceTTSOptions {
voice: string;
lang: string;
format: string;
emotion: string;
quality: string;
}

export interface IAliceActiveRequest {
id: string;
at: Date;
Expand Down
18 changes: 12 additions & 6 deletions src/uniproxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export default class Uniproxy {
}

public receiveData(messageId: string, needs: string[] = []): Promise<IAliceResponse> {
return new Promise((resolve, reject) => {
let timeoutId;
const promise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => reject(`Message ${messageId} timeout.`), 5e3);
this.requests.set(messageId, {
id: messageId,
at: new Date(),
Expand All @@ -73,24 +75,28 @@ export default class Uniproxy {
resolve,
reject
});
})

promise.finally(() => {
this.requests.delete(messageId);
clearTimeout(timeoutId);
});

return promise as Promise<IAliceResponse>;
}

public sendEvent(namespace: string, name: string, payload: any, streamId?) {
public sendEvent(namespace: string, name: string, payload: any, header: any = {}) {
const event = {
header: {
namespace,
name,
messageId: v4(),
...header
// seqNumber
},
payload
} as any;

if (streamId) {
event.header.streamId = streamId;
}

this.ws.send(JSON.stringify({ event }));
return event.header.messageId;
}
Expand Down

0 comments on commit 51d4c97

Please sign in to comment.