├── .env.sample ├── .eslintrc.json ├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── workflow.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── img ├── bs.png ├── habr.png ├── logo.png ├── ph.png ├── reddit.png ├── spark.png └── voicy.sketch ├── locales ├── am.yaml ├── ar.yaml ├── az.yaml ├── ch.yaml ├── de.yaml ├── el.yaml ├── en.yaml ├── es.yaml ├── et.yaml ├── fa.yaml ├── fr.yaml ├── hi.yaml ├── id.yaml ├── it.yaml ├── ja.yaml ├── ko.yaml ├── kz.yaml ├── no.yaml ├── pt.yaml ├── ru.yaml ├── sv.yaml ├── tr.yaml ├── ua.yaml ├── ur.yaml └── uz.yaml ├── nixpacks.toml ├── package.json ├── scripts ├── clean-voicy-localizations.js ├── download.js └── upload.js ├── src ├── app.ts ├── commands │ ├── handleAddPromoException.ts │ ├── handleDisableGoogle.ts │ ├── handleDonate.ts │ ├── handleEnableGoogle.ts │ ├── handleEngine.ts │ ├── handleFiles.ts │ ├── handleGeeky.ts │ ├── handleGoogle.ts │ ├── handleHelp.ts │ ├── handleId.ts │ ├── handleL.ts │ ├── handleLanguage.ts │ ├── handleLock.ts │ ├── handlePrivacy.ts │ ├── handleSilent.ts │ ├── handleStart.ts │ ├── handleTimecodes.ts │ ├── handleTranscribe.ts │ ├── handleTranscribeAll.ts │ ├── handleUrl.ts │ ├── handleViewPromoExceptions.ts │ └── handleWitToken.ts ├── engines │ ├── ashmanov.ts │ ├── google.ts │ ├── index.ts │ ├── platinumfund.ts │ └── wit.ts ├── handlers │ ├── checkBanned.ts │ ├── checkGoogleCredentials.ts │ ├── handleAudio.ts │ ├── handleMyChatMember.ts │ ├── handleSetEngine.ts │ └── handleSetLanguage.ts ├── helpers │ ├── Cluster.ts │ ├── addPromoToText.ts │ ├── augmentError.ts │ ├── bot.ts │ ├── cloud.ts │ ├── deleteFile.ts │ ├── engine │ │ ├── Engine.ts │ │ ├── EngineRecognizer.ts │ │ ├── Language.ts │ │ ├── RecognitionConfig.ts │ │ ├── RecognitionResult.ts │ │ └── RecognitionResultPart.ts │ ├── fileUrl.ts │ ├── flac.ts │ ├── getTextFromAudio.ts │ ├── i18n.ts │ ├── incrementMessageCount.ts │ ├── isRuChat.ts │ ├── language │ │ ├── languageKeyboard.ts │ │ ├── sendLanguage.ts │ │ └── setLanguageCodeFromTelegram.ts │ ├── localeCodeForChat.ts │ ├── logAnswerTime.ts │ ├── promoTexts.ts │ ├── report.ts │ ├── sendStart.ts │ ├── startMongo.ts │ ├── startWebhook.ts │ ├── stripe.ts │ └── urlToText.ts ├── middlewares │ ├── adminLock.ts │ ├── attachChat.ts │ ├── checkDocumentType.ts │ ├── checkFilesBanned.ts │ ├── checkSuperAdmin.ts │ ├── configureI18n.ts │ ├── countMessage.ts │ ├── disallowPrivate.ts │ ├── ignoreOldMessageUpdates.ts │ └── recordTimeReceived.ts └── models │ ├── Chat.ts │ ├── Context.ts │ ├── MessageStats.ts │ ├── PromoException.ts │ └── Voice.ts ├── tsconfig.json └── yarn.lock /.env.sample: -------------------------------------------------------------------------------- 1 | MONGO=mongodb://localhost:27017/voicy 2 | TOKEN=123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ 3 | SALT=1234567890 4 | ADMIN_ID=1234567890 5 | WIT_LANGUAGES={"English":"1234567890","Russian":"1234567890"} 6 | ENVIRONMENT=development -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2021": true, 4 | "node": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "parserOptions": { 8 | "ecmaVersion": 12, 9 | "sourceType": "module", 10 | "project": "./tsconfig.json" 11 | }, 12 | "plugins": ["@typescript-eslint", "prettier", "sort-imports-es6-autofix"], 13 | "extends": [ 14 | "eslint:recommended", 15 | "plugin:@typescript-eslint/recommended", 16 | "plugin:prettier/recommended" 17 | ], 18 | 19 | "rules": { 20 | "@typescript-eslint/no-floating-promises": "error", 21 | "require-await": "error", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "prettier/prettier": [ 25 | "error", 26 | { 27 | "trailingComma": "es5", 28 | "tabWidth": 2, 29 | "semi": false, 30 | "singleQuote": true, 31 | "endOfLine": "auto" 32 | } 33 | ], 34 | "sort-imports-es6-autofix/sort-imports-es6": [ 35 | 2, 36 | { 37 | "ignoreCase": false, 38 | "ignoreMemberSort": false, 39 | "memberSyntaxSortOrder": ["none", "all", "multiple", "single"] 40 | } 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: backmeupplz 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | - Explain what this PR changes 2 | - in one or more 3 | - bullet points 4 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Dosu Backend 2 | 3 | on: 4 | pull_request: 5 | types: [synchronize, opened] 6 | 7 | jobs: 8 | compile-code: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install modules 13 | run: yarn --no-progress 14 | - name: Compile code 15 | run: yarn tsc --skipLibCheck 16 | - name: Lint code 17 | run: yarn lint 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | *.log 4 | dist -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v17.0.1 -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true, 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "hnw.vscode-auto-open-markdown-preview", 6 | "naumovs.color-highlight", 7 | "oderwat.indent-rainbow" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": "explicit" 6 | }, 7 | "eslint.enable": true, 8 | "eslint.validate": [ 9 | "javascript", 10 | "javascriptreact", 11 | "typescript", 12 | "typescriptreact" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nikita Kolmogorov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Voicybot](/img/logo.png?raw=true)](https://voicybot.com/) 2 | 3 | # [@voicybot](https://t.me/voicybot) main repository 4 | 5 | This repository contains the code for one of the most popular bots I've ever built for Telegram — [@voicybot](https://t.me/voicybot). It automatically converts all the audio messages to text when added to a group chat. Please, feel free to fork, add features and create pull requests so that everybody (over 12 000 000 people) can experience the features you've built. 6 | 7 | You can also help by translating the bot to other languages or fixing some texts in existing languages by modifying the `locales` folder or at [localize.borodutch.com](https://localize.borodutch.com). 8 | 9 | ## List of repositories 10 | 11 | - [voicy](https://github.com/backmeupplz/voicy) — the main [@voicybot](https://t.me/voicybot) code 12 | - [voicy-payments](https://github.com/backmeupplz/voicy-payments) — payments service that used stripe to process payments for the Google Speech seconds of recognition; currently retired as the stats server for [voicybot.com](https://voicybot.com) 13 | - [voicy-landing](https://github.com/backmeupplz/voicy-landing) — [borodutch.com](https://borodutch.com) landing page 14 | - [voicy-recognition](https://github.com/backmeupplz/voicy-recognition/) — Recognition service for [voicybot.com](https://voicybot.com) 15 | 16 | ## Installation and local launch 17 | 18 | 1. Clone this repo: `git clone https://github.com/backmeupplz/voicy` 19 | 2. Launch a [mongo database](https://www.mongodb.com/) locally 20 | 3. Create `.env` file with the environment variables listed below 21 | 4. Install `ffmpeg` on your machine 22 | 5. Run `yarn` in the root folder 23 | 6. Run `yarn start` 24 | 25 | ## Environment variables in `.env` file 26 | 27 | | Variable | Description | 28 | | --------------- | --------------------------------------------------------------- | 29 | | `MONGO` | URI for the mongo database used | 30 | | `TOKEN` | Telegram bot token | 31 | | `SALT` | Random salt to generate various encrypted stuff | 32 | | `ADMIN_ID` | Chat id of the person who shall receive valuable logs | 33 | | `WIT_LANGUAGES` | A map of language names to Wit.ai tokens | 34 | | `ENVIRONMENT` | App environment, can be `development`, defaults to `production` | 35 | 36 | See examples in `.env.sample` file. 37 | 38 | ## Continuous integration 39 | 40 | Any commit pushed to `main` gets deployed to [@voicybot](https://t.me/voicybot) via [CI Ninja](https://github.com/backmeupplz/ci-ninja). 41 | 42 | ## License 43 | 44 | MIT — use for any purpose. Would be great if you could leave a note about the original developers. Thanks! 45 | 46 | ## As seen on 47 | 48 | [![Habrahabr](/img/habr.png?raw=true)](https://habrahabr.ru/post/316824/) 49 | [![Spark](/img/spark.png?raw=true)](https://spark.ru/startup/voicy/blog/19008/kak-zapustit-proekt-v-odinochku/) 50 | [![Reddit](/img/reddit.png?raw=true)](https://redd.it/5iduzy) 51 | [![Bot Store](/img/bs.png?raw=true)](https://storebot.me/bot/voicybot) 52 | [![Product Hunt](/img/ph.png?raw=true)](https://www.producthunt.com/posts/voicy) 53 | -------------------------------------------------------------------------------- /img/bs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/bs.png -------------------------------------------------------------------------------- /img/habr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/habr.png -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/logo.png -------------------------------------------------------------------------------- /img/ph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/ph.png -------------------------------------------------------------------------------- /img/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/reddit.png -------------------------------------------------------------------------------- /img/spark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/spark.png -------------------------------------------------------------------------------- /img/voicy.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backmeupplz/voicy/09ee1851205f2d694df05ce931b78616da99fe01/img/voicy.sketch -------------------------------------------------------------------------------- /locales/am.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Հիանալի՜ է։ Այս պահից սկսած *Voicy*-ն *կփորձի ճանաչել* այս զրույցի բոլոր աուդիոֆայլերը:" 2 | files_true: "\U0001F4C1 Հիանալի է! Այս պահից սկսած *Voicy*-ն *կփորձի ճանաչել* այս չատում եղած բոլոր աուդիոֆայլերը." 3 | google: 'Google Speech ձայնի ճանաչումը կարգավորելու համար պատասխանի՛ր այս նամակին` ուղարկելով Google Cloud-ի տվյալների ֆայլը (.json)։ Համոզված չե՞ս՝ ինչ է դա, և ինչպե՞ս ձեռք բերել այն։ Դիտի՛ր [մեր ուսուցանող կարճ հոլովակը](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)' 4 | google_error_doc: 'Ներողություն, դու պետք է ուղարկես տվյալների ֆայլը։' 5 | google_error_mime: "Ներողություն, փաստաթղթի mime տեսակը պետք է լինի 'text/plain'։" 6 | google_success: 'Շնորհավորո՜ւմ ենք։*Voicy*-ն ստացավ *${projectId}* Google Cloud-ի տվյալները։ Այժմ Google Speech ձայնի ճանաչման հնարավորությունը հասանելի է քեզ։' 7 | help: "\U0001F60E *Voicy*-ին ցանկացած ձայնային հաղորդագրությունից և աուդիոֆայլից ստացված խոսքը վերածում է տեքստի (.ogg, .flac, .wav, .mp3)։ Դուք կարող եք խոսել *Voicy*-ի հետ ինչպես անձնական չատում, այնպես էլ նրան ավելացնել խմբային չատին։\n\nԵթե դուք ցանկանում եք օգտագործել այս բոտը(ռոբոտ ծրագիրը) անձնական հաղորդագրության մեջ, ստեղծեք անհատական խումբ ինչ-որ մեկի հետ և այդտեղ ավելացրեք*Voicy*-ն։ Եթե ցանկանում եք *Voicy*-ն ավելացնել խմբային չատին, ավելացրեք նրան խմբի պրոֆիլին որպես մասնակից, կամ *Voicy*-ի բոտի պրոֆիլի խմբում։\n\n/help — Ցույց է տալիս այս նամակը \n/engine — Ձեզ հնարավորություն է տալիս ընտրելու ձայնի ճանաչման միջոցը՝ wit.ai, Yandex SpeechKit կամ Google Speech ⚙\n/language — Հնարավորություն է տալիս ընտրելու ձայնի ճանաչման լեզուն \U0001F4E3\n/lock — ընտրել, բացել կամ արգելափակել ոչ ադմիններին՝ օգտագործելով խմբային չատերի հրահանգները \U0001F511\n/files — Ընտրել՝ արդյոք բոտը պետք է փորձի փոփոխել աուդիոֆայլերը կամ պարզապես անտեսի դրանք \U0001F4C1\n/silent — ընտրել անձայն ռեժիմ, երբ ոչ մի նմանատիպ “Սկսվել է ձայնի ճանաչումը” լրացուցիչ հաղորդագրություն չի ուղարկվել \U0001F636\n/google — Իրականացնել google տվյալների կարգավորումը Google Speech-ի համար։ \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | lock_true: "\U0001F511 Գերազա՜նց է։ *Voicy*-ն այսուհետ կպատասխանի միայն այս չատի *ադմինների* կողմից ուղարկված հրահանգ-զանգերին։" 9 | lock_false: "\U0001F511 Գերազա՜նց է։ *Voicy*-ն այսուհետ կպատասխանի միայն այս չատում *յուրաքանչյուրի* կողմից ուղարկված հրահանգ-զանգերին։" 10 | silent_true: "\U0001F636 Հոյակա՜պ է։ *Voicy*-ն այսուհետ կաշխատի *անձայն ռեժիմով*։ Նա այլևս չատին որևէ նամակ չի ուղարկի՝ բացառությամբ ձայնային տառադարձությունների։" 11 | silent_false: "\U0001F60F Հոյակա՜պ է։ *Voicy*-ն այսուհետ կաշխատի *սովորական ռեժիմով*: Նա կուղարկի “Սկսվել է ձայնի ճանաչումը” նամակը անմիջապես ձայնային հաղորդագրություն ստանալուց հետո։" 12 | start: "\U0001F44B Բարև՜։ *Voicy*-ն ձայնի ճանաչման բոտ է, որը ստացված աուդիոֆայլերը և ձայնային նամակները (.ogg, .flac, .wav, .mp3) վերածում է տեքստի։\n\n*Voicy*-ն ապահովում է ձայնի ճանաչման երեք միջոցներ wit.ai, Yandex SpeechKit and Google Speech: Ի սկզբանե այն նախատեսված է wit.ai օգտագործելու համար, բայց դուք կարող եք ցանկացած պահի միացնել Google Speech-ը կամ Yandex SpeechKit-ը /engine բաժնում։ Տեղեկատվության համար այցելեք /help բաժին։" 13 | engine: "\U0001F44B Ընտրե՛ք ձայնի ճանաչման տարբերակը։ Google Speech-ը ավելի ճշգրիտ է, և աուդիոյի տևողությունը ապահովում է ավելի քան 50 վայրկյան, բայց պետք է միացնել ձեր Google Cloud տվյալների միջոցով (մի փոքր հոգնեցուցիչ է)։ Yandex SpeechKit-ը բավականին ճշգրիտ է և անվճար։ Լեզուների ցանկը սահմանափակ է, բայց շատ դեպքերում աուդիոյի տևողությունը 50 վայրկյանից ավել է։ Wit.ai-ն պակաս ճշգրիտ է, բայց անվճար է։ Չի ապահովում 50 վայրկյանից ավելի աուդիոյի տևողություն, բայց լեզուների ցանկը մեծ է։ Ուշադրություն դարձրեք այն հանգամանքին, որ երեքն էլ աշխատում են տարբեր լեզուներով, հետևաբար, ընտրեք այն մեկը, որն առավել համապատասխան է ձեզ։" 14 | callback_error: 'Միայն նա կարող է ընտրել, ով տվել է հրահանգի մեկնարկը' 15 | engine_success: "\U0001F44D Այժմ *Voicy*-ն այս չատում օգտագործում է *${engine}*։ Շնորհակալությո՜ւն։ Չմոռանա՛ք ընտրել /language:" 16 | language: "\U0001F44B Ընտրե՛ք խոսքի ճանաչման լեզուն *${engine}*-ի համար։" 17 | language_without_engine: "\U0001F44B Ընտրե՛ք խոսքի ճանաչման լեզուն։" 18 | language_success: '\U0001F44D Այժմ *Voicy*-ն այս չատում խոսում է *${language}* (${engine})-ով։ Շնորհակալություն։' 19 | error_twenty: "_\U0001F46E Ես չեմ կարողանում ճանաչել 20 մգբ-ից մեծ ծավալով նամակները_" 20 | initiated: "_\U0001F984 Ձայնի ճանաչումը սկսված է..._" 21 | speak_clearly: "_\U0001F46E Խնդրում եմ պարզ խոսեք, ես չկարողացա հասկանալ դա_" 22 | error: "_\U0001F46E Խնդրում եմ պարզ խոսեք, ես չկարողացա հասկանալ դա_" 23 | google_error_creds: "\U0001F62E Տեղադրեք google տվյալները/google հրահանգով կամ փոխեք միջոցները /engine հրահանգով։ Ձեր տվյալները դեռ հաստատված չեն։" 24 | ashmanov_language: Նանոսեմանտիկան աշխատում է միայն ռուսերեն։ Խնդրում եմ փոխեք/language /engine փոխելու համար։ 25 | geeky: | 26 | Բարի գալուստ մութ անկյուն, ես կառաջնորդեմ քեզ։ Վայելի՛ր հետևյալ ֆունկցիաները (կամ ինչպես կցանկանաս): Եթե հարցեր ունես geeky ֆունկցիաների վերաբերյալ, անցիր @borodutch\_support և դիմիր ուղիղ @borodutch-ին։ Ինձ համար կարևոր են քո կարծիքներն ու առաջարկները, հզո՜ր օգտատեր։ 27 | 28 | /enableGoogle — Կիսվի՛ր այս չատում քո Google տվյալներով, առանց դրանք բացահայտելու։ 29 | /disableGoogle — Հեռացրու՛ քո Google տվյալները այս չատից, առանց դրանք բացահայտելու։ 30 | /timecodes — Տառադարձման մեջ փոխարկել ժամային ցուցիչները (երբ *Voicy*-ն նշում է ամեն տառադարձված ձայնային ֆայլի ժամային ցուցիչները ) 31 | /l — տես/language 32 | `/l en` — լեզուն փոխելու համար կարճ հղում է , փորձի՛ր կիրառել այլ ծրագրավորման լեզու 33 | google_enable_personal_not_setup: Ձեր Google Cloud տվյալները կարծես դեռ կարգավորված չեն։ Նախքան Google key-ն այս չատում ակտիվացնելը խնդրում եմ տեղադրել կարգավորումները @voicybot-ում։ 34 | google_enable_success: Հրաշալի՜ է։ Ձեր Google Cloud տվյալները այժմ կօգտագործվեն այս չատում։ 35 | google_disable_personal_not_setup: Ձեր Google Cloud տվյալները կարծես դեռ կարգավորված չեն։ Նախքան Google key-ն այս չատում ակտիվացնելը խնդրում եմ տեղադրել կարգավորումները @voicybot-ում։ 36 | google_disable_success: Հոյակա՜պ է։ Ձեր Google Cloud տվյալները այլևս չեն օգտագործվի այս չատում։ 37 | oops: 'Վա՜յ։ Այս ֆունկցիան դեռ չի աշխատում, բայց համոզված եղիր, որ ես ստացել եմ քո հայտը և գիտեմ, որ այն անհրաժեշտ է քեզ։ Շնորհակալությո՜ւն։' 38 | timecodes_true: Հիանալի՜ է։ Հիմա *Voicy*-ն ճանաչված տեքստերին կավելացնի ժամային ցուցիչներ։ 39 | timecodes_false: Հիանալի՜ է։ Հիմա *Voicy*-ն չի ավելացնի ժամային ցուցիչներ ճանաչված տեքստերին։ 40 | url: 'Եթե դու ցանկանում ես շրջանցել յուրաքանչյուր ֆայլի համար նախատեսված 20մգբ սահմանը Telegram-ում, դու կարող ես օգտագործել voicybot.com-ը։ Հաջողությո՜ւն։' 41 | -------------------------------------------------------------------------------- /locales/ar.yaml: -------------------------------------------------------------------------------- 1 | files_false: "‏\U0001F4C1 رائع! سوف يتجاهل *voicy* كل الملفات الصوتية المرفقة بهذه المحادثة منذ الآن." 2 | files_true: "‏\U0001F4C1 رائع! سوف يحاول *voicy* التعرف على كل الملفات الصوتية المرسلة على هذه المحادثة منذ الآن." 3 | google: 'لتشغيل أداة التعرف الصوتي لجوجل سبيتش، قم بالرد على هذه الرسالة باستخدام ملف التعريف الخاص بجوجل كلاود (.json) . لا تعرف كيف تفعل ذلك؟ اطلع على [دليلنا السريع](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: ‏عذرا، يجب الرد مع إرفاق ملف التعريف . 5 | google_error_mime: ‏عذرا، خاصية MIME للملفات يجب ان تكون على شكل 'text/plain'. 6 | google_success: '‏مبروك! لقد حصل *Voicy* على ملف التعريف لمشروع جوجل كلاود *${projectId}* يكمنك الآن استعمال خدمة التعرف الصوتي لجوجل سبيتش.' 7 | help: "‏\U0001F60E يقوم *Voicy* بتحويل الصوت إلى نص مكتوب انطلاقا من أي رسالة صوتية أو ملف صوتي يتوصل به (.ogg, .flac, .wav, .mp3). باستطاعتك التحدث إلى *Voicy* في دردشة خاصة أو عن طريق إضافته إلى دردشة جماعية‏\n\n‏ إذا أردت استعمال الروبوت في دردشة خاصة، المرجو إنشاء دردشة خاصة مع أي كان، ثم إضافة *Voicy* إليها. إذا أردت إضافة *Voicy* إلى مجموعة دردشة جماعية، المرجو إضافته كمشارك سواء عبر إعدادات المجموعة أو عبر إعدادات الروبوت *Voicy*.‏\n\n‏/help يظهر هاته الرسالة \U0001F631‏\n‏/engine ,لاختيار محرك التعرف الصوتي: wit.ai أو جوجل سبيتش ⚙\n/language لاختيار لغة التعرف الصوتي \U0001F4E3‏\n‏/lock لتثبيت او إزالة تثبيت تمكن المشرفين من استعمال الاوامر في الدردشة الجماعية \U0001F511‏\n‏/files لتحديد إن كان على الروبوت محاولة تحويل الملفات الصوتية إلى رسائل مكتوبة أم تجاهلها \U0001F4C1‏\n‏/silent لتثبيت أو إزالة الوضع الصامت في حالة عدم وجود أي رسائل مثل ‘تم الشروع في عملية التعرف الصوتي’ \U0001F636‏\n‏/google لإعداد ملف التعريف الخاص بجوجل سبيتش \U0001F986\n/geeky — باقي الإعدادات خاصة بالمستعملين المتقدمين (مثل مشاركة ملف التعريف أو تجاوز حد 20 ميغابايت المحدد على تلغرام) \U0001F47B\n/privacy — سياسة الخصوصية\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "‏\U0001F605 عذرا، هذا الاختيار يعمل فقط في الدردشات الجماعية." 9 | lock_true: "‏\U0001F511 رائع! من الآن سيستجيب *Voicy* فقط للأوامر المرسلة من طرف *المشرفين* على هذه الدردشة." 10 | lock_false: "‏\U0001F511 رائع! من الآن سيستجيب *Voicy* فقط للأوامر المرسلة من طرف *جميع المشاركين* في هذه الدردشة." 11 | silent_true: "‏\U0001F636 عظيم! سيعمل *Voicy* الآن من خلال *الوضع الصامت*: لن يتم إرسال أي رسائل للدردشة ما عدا النسخ المكتوبة للرسائل الصوتية" 12 | silent_false: "‏\U0001F60F عظيم! سيعمل *Voicy* الآن من خلال *الوضع العام*: سيقوم بإرسال تنبيه’تم الشروع في التعرف الصوتي’ فور توصله بالرسائل الصوتية." 13 | start: "‏\U0001F44B أهلا بك! *Voicy* هو روبوت للتعرف الصوتي يقوم بتحويل كل الرسائل والملفات الصوتية (.ogg, .flac, .wav, .mp3) إلى رسائل مكتوبة‏\n\n‏ يعمل *Voicy* بمحركين للتعرف الصوتي: wit.ai وجوجل سبيتش. في الوضع الأولي، يعمل الروبوت عبر wit.ai ولكن يمكنك التغيير إلى جوجل سبيتش في أي وقت من خلال/engine. مزيد من المعلومات على/help.\n" 14 | engine: "‏\U0001F44B الرجاء اختيار محرك التعرف الصوتي. جوجل سبيتش هو الأكثر دقة لكن تشغيله يتطلب استعمال ملف التعريف الخاص بحسابك على جوجل كلاود (عملية معقدة بعض الشيء). Wit.ai أقل دقة لكنه متاح بالمجان. يرجى ملاحظة أن كلا المحركين يعملان على لغات مختلفة، لذا عليك اختيار المحرك المناسب لاحتياجاتك." 15 | callback_error: فقط الشخص الذي بدأ إرسال الأوامر بإمكانه متابعة الاختيارات. 16 | engine_success: "‏\U0001F44D *Voicy* يستخدم الآن *${engine}* في هذه الدردشة. شكرا! المرجو عدم نسيان إعداد /language" 17 | language: "‏\U0001F44B الرجاء إختيار لغة التعرف الصوتي ل${engine}" 18 | language_without_engine: "‏\U0001F44B الرجاء اختيار لغة التعرف الصوتي." 19 | language_success: "‏\U0001F44D الآن يتكلم *Voicy* *${language}* (${engine}) في هذه الدردشة. شكرا" 20 | error_twenty: "‏\U0001F46E لا أستطيع التعرف على الرسائل الصوتية التي تتعدى 20 ميغابايت." 21 | initiated: "‏\U0001F984 تم الشروع في عملية التعرف الصوتي." 22 | speak_clearly: "‏_\U0001F46E الرجاء التكلم بوضوح، لم أتمكن من التعرف على ذلك_" 23 | error: "‏_\U0001F46E لم أتمكن من التعرف على ذلك_" 24 | google_error_creds: "‏\U0001F62E الرجاء إعداد ملف التعريف الخاص بجوجل من خلال إعدادات /google أو تغيير المحرك من خلال إعدادات /engine لم يتم إعداد بياناتك بعد." 25 | ashmanov_language: خدمة النانوسيمانتكس تعمل فقط على اللغة الروسية. الرجاء تغيير /engine أولا ثم تغيير /language. 26 | geeky: | 27 | مرحبا بك في الجانب المظلم، سأكون دليلك. الرجاء الاستمتاع بالخاصيات التالية بمسؤولية (أو لا، افعل كما يحلو لك). إذا كان لديك أي سؤال بخصوص الخاصيات المتقدمة — تجاوز @borodutch\_support وأرسل طلباتك مباشرة إلى @borodutch. آراؤكم واقتراحاتكم تهمني يا زملائي المستعملين المتقدمين! 28 | 29 | /enableGoogle — قم بمشاركة ملف تعريف جوجل الخاص بك في هذه الدردشة دون الافصاح عنه 30 | /disableGoogle — قم بحذف ملف تعريف جوجل الخاص بك من هذه الدردشة دون الافصاح عنه 31 | /timecodes — قم بإظهار أو إخفاء العلامات الزمنية (عندما يقوم *Voice* بتحديد العلامات الزمنية لكل جزء من الملف الصوتي المكتوب) 32 | /l — نفسها كما في /language 33 | `/l en` — طريق مختصر لتغيير اللغة، قم بتجريبه باستعمال رموز لغات مختلفة 34 | google_enable_personal_not_setup: يبدو أن ملف تعريف جوجل كلاود الخاص بك ليس معدا بعد. الرجاء القيام بذلك في @voicybot قبل محاولة تشغيل مفتاح جوجل الخاص بك في هذه الدردشة. 35 | google_enable_success: رائع. سيتم استخدام ملف تعريف جوجل كلاود الخاص بك في هذه الدردشة. 36 | google_disable_personal_not_setup: يبدو أن ملف تعريف جوجل كلاود الخاص بك ليس معدا بعد. الرجاء القيام بذلك في @voicybot قبل محاولة إلغاء مفتاح جوجل الخاص بك في هذه الدردشة. 37 | google_disable_success: رائع. لن يتم استعمال ملف تعريف جوجل كلاود الخاص بك في هذه الدردشة. 38 | oops: 'اعذرني، لم أقم بعد بتضمين هاته الخاصية. لكن اطمئن: توصلت بطلبك وأعرف الآن أنك بحاجة إليها. شكرا!' 39 | timecodes_true: جميل! الآن سيقوم *Voicy* بإضافة العلامات الزمنية إلى النص المتعرف عليه. 40 | timecodes_false: جميل! الآن سيقوم *Voicy* بإزالة العلامات الزمنية من النص المتعرف عليه. 41 | url: إذا أردت تجاوز حد 20 ميغابايت المحدد في تلغرام لكل ملف، بإمكانك استعمال voicybot.com مباشرة. بصحتك! 42 | -------------------------------------------------------------------------------- /locales/az.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Möhtəşəm! Bu andan etibarən “Voicy” bu söhbətdəki bütün səs fayllarını “görməməzlikdən gələcək”." 2 | files_true: "\U0001F4C1 Möhtəşəm! Bu andan tibarən “Voicy” bu söhbətdəki bütün audio faylarını “tanımağa çalışacaq”." 3 | google: 'Google Speech səs tanımasını quraşdırmaq üçün bu mesajı Google Cloud etibarnamə fayl(.json)ı ilə cavablayın. Nə olduğu və necə əldə edəcəyinizi bilmirsiniz? Bunu yoxlayın [qısa təlimimiz](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)' 4 | google_error_doc: 'Bağışlayın, etibarnamə sənədi ilə cavablamalısınız.' 5 | google_error_mime: "Bağışlayın, sənədlərin yazılış mimi belə olmalıdır 'text/plain'" 6 | google_success: 'Təbriklər! “Voicy” *${projectId}* Google Speech Layihəsi üçün etibarnamə fayllarını əldə etdi. Artıq Google Speech tanımağından istifadə edə biləcəksiniz.' 7 | help: "\U0001F60E *Voicy* qəbul etdiyi hər cür səsli mesajları və audio faylarda (.ogg, .oflac, .wav, .mp3) olan danışıqları yazılı formata çevirir. “Voicy” ilə şəxsi söhbət otağında danışa, yaxud qrupa əlavə edə bilərsiniz. \n\nƏgər bu botdan şəxsi mesajlarda istifadə etmək istəyirsinizsə, zəhmət olmasa, kimləsə şəxsi qrup söhbəti açın və *Voicy*ni ora əlavə edin. Əgər *Voicy*ni qrup söhbətinə əlavə etmək istəyirsinizsə, zəhmət olmasa, onu qrup üzvü kimi daxil edin, yaxud qrupla *Voicy* bot profilinə yığışın.\n\n/help — Bu mesajı göstərir \U0001F631\n/engine — Gəlin, sizin üçün səs tanıma mühərriki seçək: wit.ai, Yandex SpeechKit yaxud Google Speech. ⚙\n/language — Gəlin, sizin üçün səs tanıması dili seçək \U0001F4E3\n/lock — adminlərin qrup söhbət otaqlarında əmr verməyini kilidləmə və ya açma çubuğu \U0001F511\n/files — Bot audio faylları çevirməyə cəhd eləməlidir və ya görməməzlikdən gəlməlidir çubuğu \U0001F4C1\n/silent — “Səs tanıması başladılmışdır” kimi əlavə mesajlar göndərilmədiyi səssiz rejimə keçmə çubuğu \U0001F636\n/google — Google Speech üçün google etibarnamələrini quraşdırın \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Bağışlayın, bu əmr ancaq qrup söhbətlərində işləyir." 9 | lock_true: "\U0001F511 Əla! Bundan sonra *Voicy* bu söhbətdə ancaq *adminlər* tərəfindən göndərilən əmr çağırışlarına cavab verəcək." 10 | lock_false: "\U0001F511 Əla! Bundan sonra *Voicy* bu söhbətdə ancaq *hərkəs* tərəfindən göndərilən əmr çağırışlarına cavab verəcək." 11 | silent_true: "\U0001F636 Şəhanə! Bundan sonra *Voicy* *səssiz rejim*də işləyəcək: əsl səs yazıköçürmələrindən başqa söhbətə heç bir mesaj göndərməyəcək." 12 | silent_false: "\U0001F60F Şəhanə! Bundan sonra *Voicy* *adi rejim*də işləyəcək : səs qəbul edən kimi “Səs tanıması başladılmışdır” mesajı göndərəcək." 13 | start: "\U0001F44B Xoş gördük! *Voicy* qəbul etdiyi hər cür səsli mesajları və audio faylarda (.ogg, .oflac, .wav, .mp3) olan danışıqları yazılı formata çevirən səs tanınması botudur.\n\n*Voicy* üç səs tanıması mühərrikini dəstəkləyir: wit.ai, Yandex SpeechKit və Google Speech. Ilkin olaraq wit.ai istifad etməyə quraşdırılıb, lakin istdiyiniz vaxt buradan Google Speech yaxud Yandex SpeechKit dəyişdirə bilərsiniz /engine. Daha çox məlumat burada /help\n" 14 | engine: "\U0001F44B Zəhmət olmasa, nitq tanıması mühərrikini seçin. Google Speech daha dəqiqdir və 50 saniyədən artıq səsləri dəstəkləyir, lakin Google Cloud etibarnamələri il quraşdırılmalıdır (bir qədər müşkülpəsəntdir). Yandex SpeechKit kifayət qədər dəqiqdir, pulsuzdur, şəxsidir və çox vaxt 50 saniyədən artıq səsləri dəstəkləyir, lakin dil siyahısı qısadır. Wit.ai çox da dəqiq deyil, pulsuzdur, 50 saniyədən artıq səsləri dəstəkləmir, lakin dil siyahısı uzundur. Zəhmət olmasa, yadda saxlayın ki, hər üçü müxtəlif dilləri dəstəkləyir, odur ki, özünüzə ən uyğun olanı seçin." 15 | callback_error: Ancaq əmri başladan şəxs seçimləri edə bilər 16 | engine_success: "\U0001F44D Artıq *Voicy* *${engine}* bu söhbətdə istifadə edir. Minnətdaram! Quraşdırmağı unutma /language" 17 | language: "\U0001F44B Zəhmət olmasa, ${engine} üçün nitq tanıma dilini seçin" 18 | language_without_engine: "\U0001F44B Zəhmət olmasa, nitq tanıma dilini seçin" 19 | language_success: "\U0001F44D Artıq *Voicy* bu söhbətdə *${language}* (${engine}) danışır. Minnətdaram!" 20 | error_twenty: "_\U0001F46E 20 meqabitdən böyük səsli mesajları tanıya bilirəm_" 21 | initiated: "_\U0001F984 Səs tanıma başladılır..._" 22 | speak_clearly: "_\U0001F46E Zəhmət olmasa, aydın danışın. Bunu tanıya bilmədim_" 23 | error: "_\U0001F46E Bunu tanıya bilmədim_" 24 | google_error_creds: "\U0001F62E Zəhmət olmasa, google etibarnamələrini /google əmri ilə quraşdırın, yaxud /engine əmri ilə mühərriki dəyişin. Sizin etibarnamələriniz hələ quraşdırılmayıb." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/ch.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 太好了!从现在起,*Voicy*将*忽略*这个对话中的所有语音档。" 2 | files_true: "\U0001F4C1 太好了!从现在起,*Voicy*将*识别*这个对话中的所有语音档。" 3 | google: '要设置谷歌语音上的语音识别,请回覆此讯息,并在讯息中上传谷歌云凭据档案(.json格式)。您不知道这是什么吗?还是您不知道如何取得这个档案?来查看[我们的快速教学](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)吧!' 4 | google_error_doc: 抱歉!您的回覆必须是一个凭证档案。 5 | google_error_mime: 抱歉!档案类型必须是'text/plain'。 6 | google_success: '恭喜!*Voicy*已取得*${projectId}*谷歌云端项目的凭证档案。现在,你可以开始使用谷歌语音识别了!' 7 | help: "\U0001F60E *Voicy*会把所有收到的语音讯息及语音档(.ogg、.flac、.wav及.mp3)转换成文字。您可在私人对话中使用*Voicy*,也可以把软体加到群组对话中。\n\n如果您想在私人对话中使用这个软体,请与您的聊天对象设立一个对话群组,然后再把*Voicy*加入群组。如果您想把*Voicy*加入群组对话当中,请把该软体当作群组成员在群组信息中加入群组,或者加到*Voicy*软体档案内的群组中。\n\n/help — 显示此信息\U0001F631\n/engine — 让您选择语音识别系统:wit.ai或谷歌语音 ⚙\n/language — 让您选择语言识别的语言\U0001F4E3\n/lock — 使用群组对话中的指令选项对非管理员进行封锁或解锁\U0001F511\n/files — 切换软体是否应该尝试转换语音档案或忽略它们\U0001F4C1\n/silent —除非出现类似`语音识别已启动`的讯息,否则切换至静音模式\U0001F636\n/google — 设立谷歌语音的云端凭证档案\U0001F986\n/geeky — 其余的额外功能主要由高级用户使用 (例如共享谷歌凭据或忽略20Mb容量限制) \U0001F47B\n/privacy — 隐私政策\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | lock_true: "\U0001F511 太好了!从现在起,*Voicy*只会回应本对话中*管理员*的指令。" 9 | lock_false: "\U0001F511 太好了!从现在起,*Voicy*只会回应本对话中*任何人*的指令。" 10 | silent_true: "\U0001F636 太棒了!从现在起,*Voicy*将以*静音模式*运行:除了语音转录外,它不会向其对话发送任何信息。" 11 | silent_false: "\U0001F60F 太棒了!从现在起,*Voicy*将以*正常模式*运行:它在收到语音讯息后立即发送`语音识别已启动`的信息。" 12 | start: "\U0001F44B 您好! *Voicy*是一个语音识别系统,它可以把所有语音讯息及语音档(包括.ogg、.flac、.wav及.mp3)转换成文字。\n\n*Voicy*支援两个语音识别系统:wit.ai和谷歌语音。系统预设使用wit.ai,但您可以随时在/engine中切换到谷歌语音。更多信息请参阅/help。\n" 13 | engine: "\U0001F44B 请选择语音识别系统。谷歌语音的准确性较好,但它必须使用您的谷歌云凭据进行设置(有点费时)。wit.ai的准确性较差,但它是免费的。请注意,它们两者都支持不同的语言,所以请选择最适合您的那一种。" 14 | callback_error: 只有启动指令的人才能选择选项 15 | engine_success: "\U0001F44D 现在,*Voicy*可在此对话中使用*${engine}*了!感谢您!可别忘了设定 /language。" 16 | language: "\U0001F44B 请为${engine}选择语音识别语言。" 17 | language_without_engine: "\U0001F44B 请选择语音识别的语言。" 18 | language_success: "\U0001F44D 现在,*Voicy*可在此对话中使用*${language}* (${engine})了!感谢您!" 19 | error_twenty: "_\U0001F46E 我无法识别超过20MB的语音讯息_" 20 | initiated: "_\U0001F984 语音识别已启动..._" 21 | speak_clearly: "_\U0001F46E 请您可以说得清楚一点吗?我无法识别_" 22 | error: "_\U0001F46E 我无法辨识_" 23 | google_error_creds: "\U0001F62E 请使用/google指令设置谷歌凭证,或使用/engine指令更改所使用的系统。您的凭据尚未设置。" 24 | ashmanov_language: Nanosemantics只支持俄语。请先将/engine更改为/language。 25 | geeky: | 26 | 欢迎来到黑暗面,我将是您的向导。请负责任地享受以下功能(或者由您自行决定是否负责任地享受其功能)。 如果您对geeky函数有任何疑问 — 请绕过 @borodutch\_support 并直接向@borodutch发送请求。 各位超级用户,我非常重视你们的意见和建议! 27 | 28 | /enableGoogle — 在此对话中共享您的个人谷歌凭据,而无需公开它们 29 | /disableGoogle — 从此对话中删除您的个人谷歌凭据,而无需公开它们 30 | /timecodes — 切换转录中的时间码(当*Voicy*指定每段转录语音的时间码时) 31 | /l — 与/language一样 32 | `/l en` — 更改语言的捷徑,请使用不同的语言代码进行尝试 33 | google_enable_personal_not_setup: 似乎您的个人谷歌云凭据尚未设置。 请先在@voicybot中进行设置然后再尝试在此对话中启用您的谷歌密钥。 34 | google_enable_success: 太好了!您的谷歌云凭据将能够在此对话中使用。 35 | google_disable_personal_not_setup: 似乎您的个人谷歌云凭据尚未设置。请先在@voicybot中进行设置然后再尝试在此对话中停用您的谷歌密钥。 36 | google_disable_success: 太好了!您的谷歌云凭据将不能够在此对话中使用。 37 | oops: 糟糕!我尚未实施此功能。但是请放心:我已经收到了您的请求,我知道您需要此功能。感谢您! 38 | timecodes_true: 很好!现在*Voicy*将会在已识别的文字中添加时间码。 39 | timecodes_false: 很好!现在*Voicy*将不会在已识别的文字中添加时间码。 40 | url: 如果您想要忽略每个文件20Mb的容量限制,可以直接使用voicybot.com。 谢谢! 41 | -------------------------------------------------------------------------------- /locales/de.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Wunderbar! *Voicy* wird ab sofort alle Audio-Dateien in diesem Chat *ignorieren*." 2 | files_true: "\U0001F4C1 Wunderbar! *Voicy* wird ab sofort versuchen, alle Audio-Dateien in diesem Chat zu *erkennen*." 3 | google: 'Antworte auf diese Nachricht mit der JSON-Datei mit den Anmeldeinformationen für die Google Cloud, um die Google Spracherkennung einzurichten. Bist du dir nicht ganz sicher, was das ist und woher du sie bekommen kannst? Schau dir [unsere Kurzanleitung](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8) an.' 4 | google_error_doc: 'Sorry, du solltest mit einem Dokument mit Anmeldeinformationen antworten.' 5 | google_error_mime: "Entschuldigung, der MIME-Typ des Dokuments sollte 'text/plain' sein." 6 | google_success: 'Herzlichen Glückwunsch! *Voicy* hat die Anmeldeinformationen für das *${projectId}* Google-Cloud-Projekt erhalten. Jetzt kannst du die Google-Spracherkennung verwenden.' 7 | help: "\U0001F60E *Voicy* wandelt von jetzt an alle erhaltenen Sprachnachrichten und Audio-Dateien (.ogg, .flac, .wav, .mp3) zu Text um. Du kannst mit *Voicy* entweder im direkten Chat sprechen oder ihn einer Gruppe hinzufügen.\n\nWenn du den Bot in einem privaten Chat verwenden möchtest, erstelle bitte eine private Gruppe mit dieser Person und füge *Voicy* hinzu. Wenn du *Voicy* zu einem Gruppenchat hinzufügen möchtest, kannst du *Voicy* von dem Gruppenprofil oder aus dem Bot-Profil als Mitglied hinzufügen.\n\n/help — zeigt diese Meldung \U0001F631\n/engine — hier kannst du eins der Spracherkennungsprogramme auswählen: wit.ai, Yandex SpeechKit oder Google Speech ⚙\n/language — damit kannst du eine Erkennungssprache auswählen \U0001F4E3\n/lock — umschalten, ob Nicht-Admins in Gruppen-Chats die Befehle verwenden können \U0001F511 \n/files — umschalten, ob der Bot versuchen soll, Audiodateien zu konvertieren, oder diese einfach zu ignorieren \U0001F4C1\n/silent — schalte den Stumm-Modus ein, sodass keine zusätzliche Nachrichten wie „Spracherkennung wird gestartet“ gesendet werden \U0001F636\n/google — Google-Anmeldeinformationen für Google Sprache einrichten \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Tut mir leid, aber dieser Befehl funktioniert nur in Gruppen-Chats." 9 | lock_true: "\U0001F511 Wunderbar! *Voicy* reagiert in diesem Chat nun nur noch auf Befehle, die von einem *Administrator* gesendet werden." 10 | lock_false: "\U0001F511 Wunderbar! *Voicy* reagiert nun auf Befehle von *jedem* in diesem Chat." 11 | silent_true: "\U0001F636 Großartig! *Voicy* funktioniert jetzt im *Stumm-Modus*: Er wird keine Nachrichten in den Chat senden, mit Ausnahme der tatsächlichen Stimm-Transkriptionen." 12 | silent_false: "\U0001F60F Toll! *Voicy* funktioniert jetzt im *normalen Modus*: Er sendet „Spracherkennung wird gestartet“-Nachrichten, direkt nachdem eine Sprachnachricht empfangen wurde." 13 | start: "\U0001F44B Hallo! *Voicy* ist ein Stimmerkennungs-Bot, der alle Sprachnachrichten und Audio-Dateien (.ogg, .flac, .wav,. mp3) zu Text umwandelt.\n\n*Voicy* unterstützt drei Stimm-Erkennungs-Programme: wit.ai, Yandex SpeechKit und die Google-Spracherkennung. Vorerst wird wit.ai verwendet, aber du kannst jederzeit mit /engine zu Google Speech oder Yandex SpeechKit wechseln. Weitere Informationen findet du unter /help." 14 | engine: "\U0001F44B Bitte wählen Sie das Spracherkennungsprogramm. Google Speech ist genauer, muss aber per Google-Cloud Anmeldung eingerichtet werden (das kann etwas mühsam sein). Hier werden Audiodateien, welche länger als 50 Sekunden sind unterstützt. Yandex SpeechKit ist ziemlich genau, kostenlos, privat und unterstützt meistens Audiodateien welche länger als 50 Sekunden sind. Es hat aber nur eine begrenzte Liste von Sprachen. Wit.ai ist weniger genau, dafür aber kostenlos. Es unterstützt nur Audiodateien die maximal 50 Sekunden lang sind, dafür hat es aber mehr Sprachen. Bitte beachte, dass alle drei verschiedene Sprachen unterstützen, wähle also das, welches am besten zu dir passt." 15 | callback_error: 'Nur die Person, die den Befehl gestartet hat, kann Optionen auswählen' 16 | engine_success: "\U0001F44D *Voicy* verwendet jetzt *${engine}* in diesem Chat. Danke! Vergiss nicht, die Sprache mit /language einzustellen." 17 | language: "\U0001F44B Bitte wähle die Sprache für die Spracherkennung für ${engine}." 18 | language_without_engine: "\U0001F44B Bitte wähle die Sprache der Spracherkennung." 19 | language_success: "\U0001F44D *Voicy* spricht jetzt *${language}* (${engine}) in diesem Chat. Danke!" 20 | error_twenty: "_\U0001F46E Ich kann keine Sprachnachrichten erkennen, die größer als 20 Megabytes sind_" 21 | initiated: "_\U0001F984 Spracherkennung wird gestartet..._" 22 | speak_clearly: "_\U0001F46E Bitte sprich deutlicher, das konnte ich leider nicht erkennen_" 23 | error: "_\U0001F46E Das konnte ich nicht erkennen_" 24 | google_error_creds: "\U0001F62E Bitte richte die Google-Anmeldeinformationen mit dem /google Befehl ein oder ändere das Programm mit dem Befehl /engine. Deine Zugangsdaten sind noch nicht eingerichtet." 25 | ashmanov_language: Nanosemantics funktioniert leider nur mit Russisch. Bitte ändere das /engine um die /language zu ändern. 26 | geeky: | 27 | Willkommen zur dunklen Seite. Ich werde dein Führer sein. Bitte nutze die folgenden Funktionen verantwortungsvoll (oder auch nicht, das entscheidest du). Falls du Fragen zu unseren beeindruckenden Funktionen hast wende dich bitte an @borodutch\_support und sende deine Nachricht direkt an @borodutch. Deine Meinung und Vorschläge werden von uns wertgeschätzt! 28 | /enableGoogle — Teile deine persönliche Zugangsdaten mit diesem Chat ohne sie sichtbar zu machen 29 | /disableGoogle — Entferne deine persönlichen Zugangsdaten ohne diese sichtbar zu machen 30 | /timecodes — Schalte Zeitangaben in der Transkription um (wenn *Voicy* die Zeitangaben spezifiziert für jedes einzelne Audio-Transkription) 31 | /l — das gleiche wie /language 32 | `/l en` — Kurzbefehl um die Sprache zu ändern, versuche das mit anderen Sprachcodes 33 | google_enable_personal_not_setup: 'Es scheint, als ob deine Google Einwahldaten noch nicht vollständig eingestellt wurden, tu dies bitte unter @voicybot bevor du versuchst deinen Google Schlüssel in diesen Chat zu entfernen' 34 | google_enable_success: Wundervoll. Deine Google Zugangsdaten werden nun in diesem Chat genutzt. 35 | google_disable_personal_not_setup: 'Es scheint, als ob deine persönlichen Google Cloud Zugangsdaten noch nicht eingestellt sind. Bitte stelle sie unter @voicybot ein bevor du versuchst deine Zugangsdaten aus diesem Chat zu entfernen' 36 | google_disable_success: Wundervoll. Deine Google Zugangsdaten werden in diesem Chat nicht mehr benutzt. 37 | timecodes_true: Schön! Ab jetzt wird *Voicy* Zeitangaben zu deinen erkannten Texten hinzufügen. 38 | timecodes_false: Schön! Ab jetzt wird *Voicy* keine Zeitangaben zu deinen erkannten Texten mehr hinzufügen. 39 | url: Wenn du das Limit von 20Mb auf Telegram vermeiden möchtest besuche uns direkt auf der Seite voicybot.com. Prost! 40 | -------------------------------------------------------------------------------- /locales/el.yaml: -------------------------------------------------------------------------------- 1 | error: "_\U0001F46E Δεν μπορούσα να το αναγνωρίσω _" 2 | -------------------------------------------------------------------------------- /locales/en.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Wonderful! *Voicy* will *ignore* all audio files in this chat since now." 2 | files_true: "\U0001F4C1 Wonderful! *Voicy* will *try to recognize* all audio files in this chat since now." 3 | google: 'Reply to this message with the Google Cloud credentials file (.json) to set up Google Speech voice recognition. Not sure what is this and how to get it? Check out [our quick tutorial](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Sorry, you should reply with a credentials document.' 5 | google_error_mime: "Sorry, document's mime type should be 'text/plain'." 6 | google_success: 'Congratulations! *Voicy* got the credentials file for the *${projectId}* Google Cloud Project. Now you can use Google Speech recognition.' 7 | help: "\U0001F60E *Voicy* converts speech to text from any voice messages and audio files (.ogg, .flac, .wav, .mp3) it receives. You can either talk to *Voicy* in the private chat or add it to a group.\n\nIf you want to use this bot in private messages, please, create a private group with anyone and add *Voicy* there. If you want to add *Voicy* to a group chat, please, add it as a participant on the group profile or to the group in the *Voicy* bot profile.\n\n/help — Shows this message \U0001F631\n/engine — Lets you pick a voice recognition engine: wit.ai or Google Speech ⚙\n/language — Lets you pick a voice recognition language \U0001F4E3\n/lock — Toggles lock or unlock of non-admins using commands in group chats \U0001F511\n/files — Toggles if the bot should attempt to convert audio files or just ignore them \U0001F4C1\n/silent — Toggles silent mode when no extra messages like `Voice recognition is initiated` are sent \U0001F636\n/google — Set up google credentials for Google Speech \U0001F986\n/geeky — The rest of extra functions mostly used by advanced users (like sharing google credentials or bypassing 20Mb Telegram limit) \U0001F47B\n/privacy — Privacy policy\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n/transcribeAll - To toggle transcribing all voice messages in the chat (effective only for group/supergroup chats)\n/transcribe - To transcribe a specific voice message\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Sorry, but this command only works in group chats." 9 | lock_true: "\U0001F511 Great! *Voicy* will now respond only to command calls sent by *admins* in this chat." 10 | lock_false: "\U0001F511 Great! *Voicy* will now respond only to command calls from *anyone* in this chat." 11 | silent_true: "\U0001F636 Magnificent! *Voicy* will now work in *silent mode*: it will not send any messages to the chat except for the actual voice transcriptions." 12 | silent_false: "\U0001F60F Magnificent! *Voicy* will now work in *usual mode*: it will send `Voice recognition is initiated` messages right after it receives voice messages." 13 | start: "\U0001F44B Hello there! *Voicy* is a voice recognition bot that converts all voice messages and audio files (.ogg, .flac, .wav, .mp3) it gets into text.\n\n*Voicy* supports two voice recognition engines: wit.ai and Google Speech. Initially, it's set to use wit.ai but you can switch to Google Speech anytime in /engine. More information in /help.\n" 14 | engine: "\U0001F44B Please, select the engine of speech recognition. Google Speech is more accurate but has to be set up with your Google Cloud credentials (a bit tedious). Wit.ai is less accurate but free. Please, note that both of them support different languages, so pick the one that suits you the best." 15 | callback_error: Only the person who started the command can select options 16 | engine_success: "\U0001F44D Now *Voicy* uses *${engine}* in this chat. Thank you! Don't forget to set /language." 17 | language: "\U0001F44B Please select the language of speech recognition for ${engine}" 18 | language_without_engine: "\U0001F44B Please select the language of speech recognition" 19 | language_success: "\U0001F44D Now *Voicy* speaks *${language}* (${engine}) in this chat. Thank you!" 20 | error_twenty: "_\U0001F46E I can't recognize voice messages larger than 20 megabytes_" 21 | initiated: "_\U0001F984 Voice recognition is initiated..._" 22 | speak_clearly: "_\U0001F46E Please, speak clearly, I couldn't recognize that_" 23 | error: "_\U0001F46E I couldn't recognize that_" 24 | google_error_creds: "\U0001F62E Please, set up google credentials with the /google command or change the engine with the /engine command. Your credentials are not set up yet." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | geeky: | 27 | Welcome to the dark side, I'll be your guide. Please, enjoy the following functions responsibly (or not, you decide). If you have any questions about the geeky functions — bypass the @borodutch\_support and send requests directly to @borodutch. I value your opinion and suggestions, fellow power users! 28 | 29 | /enableGoogle — Share your personal Google credentials with this chat without exposing them 30 | /disableGoogle — Remove your personal Google credentials from this chat without exposing them 31 | /timecodes — Toggle timecodes in the transcriptions (when *Voicy* specifies the timecodes for each piece of transcribed audio) 32 | /url — Bypass the 20Mb Telegram limit 33 | /l — The same as /language 34 | `/l en` — Shortcut to change language, try it out with different language codes 35 | google_enable_personal_not_setup: 'Looks like your personal Google Cloud credentials were not set yet. Please, do so in @voicybot before trying to enable your Google key in this chat.' 36 | google_enable_success: Wonderful. Your Google Cloud credentials will now be used in this chat. 37 | google_disable_personal_not_setup: 'Looks like your personal Google Cloud credentials were not set yet. Please, do so in @voicybot before trying to disable your Google key in this chat.' 38 | google_disable_success: Wonderful. Your Google Cloud credentials will not be used in this chat anymore. 39 | oops: "Ooopsie! I haven't implemented this feature yet. But be rest assured: I've received your request for it and will know that you need this feature. Thank you!" 40 | timecodes_true: Nice! Now *Voicy* will add timecodes to the recognized text. 41 | timecodes_false: Nice! Now *Voicy* will not add timecodes to the recognized text. 42 | url: 'If you want to bypass the Telegram limit of 20Mb per file, you can use voicybot.com directly. Cheers!' 43 | sunsetting: Free Voicy will no longer work. See [this post](https://blog.borodutch.com/im-sunsetting-voicy/) for the details and if you want to buy the bot. If you want to keep using Voicy, you have to make a one-time donation of $6.99 by using /donate. Cheers! 44 | already_paid: You've already donated to Voicy. Thank you! 45 | pay: 'Click the button below to donate $6.99. Or you can send $6.99 worth of tokens to `borodutch.eth` and send @borodutch the transaction. Thank you so much for your support!' 46 | pay_button: Donate $6.99 47 | transcribe_all_true: 'Perfect, Voicy will now *convert all voice messages* it receives in this chat.' 48 | transcribe_all_false: 'Got it, Voicy will now *ignore all voice messages* it receives in this chat. You can send /transcribe to convert a specific voice message.' 49 | reply_to_voice: 'Please, reply to the voice message you want to transcribe.' 50 | -------------------------------------------------------------------------------- /locales/es.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 ¡Maravilloso! *Voicy* *ignorará* todos los archivos de audio en este chat a partir de ahora." 2 | files_true: "\U0001F4C1 ¡Maravilloso! *Voicy* *intentará reconocer* todos los archivos de audio en este chat a partir de ahora." 3 | google: 'Responde a este mensaje con el archivo de credenciales de Google Cloud (.json) para configurar el reconocimiento de voz de Google Speech. ¿No está seguro de lo que es esto o cómo lograrlo? Revise [nuestro tutorial rápido](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Lo siento, debe responder con un archivo de credenciales.' 5 | google_error_mime: "Lo siento, el tipo de archivo (MIME) debe ser 'text/plain'." 6 | google_success: '¡Felicitaciones! *Voicy* obtuvo el archivo de credenciales para el *${projectId}* Proyecto Google Cloud. Ahora usted puede usar el reconocimiento de voz de Google Speech.' 7 | help: "\U0001F60E *Voicy* convierte la voz a texto desde cualquier mensaje de voz o archivo de audio (.ogg, .flac, .wav, .mp3) que reciba. Usted puede tanto hablarle a *Voicy* en un chat privado o agregarlo a un grupo.\n\nSi usted desea usar este bot en mensajes privados, por favor, cree un grupo privado con cualquiera y agregue allí a *Voicy*. Si usted desea agregar a *Voicy* a un chat grupal, por favor, agréguelo como un participante en el perfil del grupo o desde el perfil del bot *Voicy*.\n\n/help — Muestra este mensaje \U0001F631\n/engine — Le permite escoger un motor de reconocimiento de voz: wit.ai, Yandex SpeechKit or Google Speech ⚙\n/language — Le permite escoger el idioma de reconocimiento de voz \U0001F4E3\n/lock — Alterna entre el bloqueo y desbloqueo de los no administradores usando comandos en los chats grupales \U0001F511\n/files — Alterna entre si el bot debe intentar convertir archivos de audio o simplemente ignorarlos \U0001F4C1\n/silent — Cambia al modo silencio cuando se envían mensajes extras como `Reconocimiento de voz iniciado` \U0001F636\n/google — Establezca las credenciales de Google para Google Speech \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Lo siento, pero este comando solo funciona en chat grupales." 9 | lock_true: "\U0001F511 ¡Estupendo! *Voicy* responderá ahora solo a los comandos enviados por *administradores* en este chat." 10 | lock_false: "\U0001F511 ¡Estupendo! *Voicy* responderá ahora a los comandos de *cualquiera* en este chat." 11 | silent_true: "\U0001F636 ¡Magnífico! *Voicy* trabajará ahora en *modo silencio*: no enviará ningún mensaje al chat excepto por las transcripciones de voz." 12 | silent_false: "\U0001F60F ¡Magnífico! *Voicy* trabajará ahora en *modo usual*: enviará mensajes de `Reconocimiento de voz iniciado` justo después de recibir mensajes de voz." 13 | start: "\U0001F44B ¡Hola! *Voicy* es un bot de reconocimiento de voz que convierte todos los mensajes de voz o archivos de audio (.ogg, .flac, .wav, .mp3) que recibe en texto.\n\n*Voicy* admite tres motores de reconocimiento de voz: wit.ai, Yandex SpeechKit y Google Speech. Inicialmente está predeterminado para utilizar wit.ai pero usted puede cambiarlo a Google Speech o Yandex SpeechKit en cualquier momento en /engine. Más información en /help.\n" 14 | engine: "\U0001F44B Por favor, seleccione el motor de reconocimiento de voz. Google Speech es el más preciso y admite audio con una duración mayor de 50 segundos, pero debe ser programado con sus credenciales de Google Cloud (algo tedioso). Yandex SpeechKit es bastante preciso, gratis, privado, y la mayoría del tiempo permite audio mayor de 50 segundos, pero tiene una lista limitada de idiomas. Wit.ai es menos preciso, aunque es gratis, y no admite audio mayor de 50 segundos, pero posee una gran variedad de idiomas. Por favor, tenga en cuenta que los tres admiten diferentes idiomas, así que escoja el que más le convenga." 15 | callback_error: Solo la persona que inició comando puede seleccionar opciones 16 | engine_success: "\U0001F44D Ahora *Voicy* usa *${engine}* en este chat. ¡Gracias! No olvide establecer el /language." 17 | language: "\U0001F44B Por favor, seleccione el idioma de reconocimiento de voz ${engine}" 18 | language_without_engine: "\U0001F44B Por favor seleccione el idioma de reconocimiento de voz" 19 | language_success: "\U0001F44D Ahora *Voicy* habla *${language}* (${engine}) en este chat. ¡Gracias!" 20 | error_twenty: "_\U0001F46E No puedo reconocer mensajes de voz más grandes de 20 megabytes_" 21 | initiated: "_\U0001F984 Reconocimiento de voz iniciado..._" 22 | speak_clearly: "_\U0001F46E Por favor, hable claro, no pude reconocer eso_" 23 | error: "_\U0001F46E No pude reconocer eso_" 24 | google_error_creds: "\U0001F62E Por favor, establezca las credenciales de Google con el comando /google o cambie el motor con el comando /engine. Sus credenciales aún no se han establecido." 25 | ashmanov_language: 'Nanosemantics solo admite el idioma ruso. Por favor, cambie el /engine primero para cambiar el /language.' 26 | geeky: | 27 | Bienvenido al lado oscuro, seré su guía. Por favor, disfrute de las siguientes funciones responsablemente (o no, usted decide). Si tiene alguna pregunta sobre las funciones geeky — ignore @borodutch\_support y envíe su consulta directamente a @borodutch. ¡Valoro sus opiniones y sugerencias, colegas usuarios expertos! 28 | 29 | /enableGoogle — Comparta sus credenciales de Google en este chat sin revelarlas 30 | /disableGoogle — Elimine sus credenciales personales de Google de este chat sin revelarlas 31 | /timecodes — Alterna códigos de tiempo en las transcripciones (cuando *Voicy* especifica los códigos de tiempo para cada fragmento de audio transcrito) 32 | /l — lo mismo que /language 33 | `/l en` — Un atajo para cambiar el idioma, pruébelo con diferentes códigos de idioma 34 | google_enable_personal_not_setup: 'Parece que sus credenciales personales de Google Cloud todavía no han sido establecidas. Por favor, hágalo en @voicybot antes de intentar habilitar su llave de Google en este chat.' 35 | google_enable_success: Estupendo. Sus credenciales de Google Cloud ahora serán usadas en este chat. 36 | google_disable_personal_not_setup: 'Parece que sus credenciales personales de Google cloud todavía no han sido establecidas. Por favor, hágalo en @voicybot antes de intentar habilitar su llave de Google en este chat.' 37 | google_disable_success: Estupendo. Sus credenciales de Google Cloud no se usarán más en este chat. 38 | oops: '¡Vaya! Todavía no he implementado esa función. Pero no se preocupe: he recibido su solicitud para ello y sabré que necesita esa función. ¡Gracias!' 39 | timecodes_true: ¡Bien! Ahora *Voicy* añadirá códigos de tiempo al texto reconocido. 40 | timecodes_false: ¡Bien! Ahora *Voicy* no añadirá códigos de tiempo al texto reconocido. 41 | url: 'Si quiere evitar el límite de Telegram de 20Mb por archivo, puede usar directamente voicybot.com. ¡Gracias!' 42 | -------------------------------------------------------------------------------- /locales/et.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 ድንቅ ! *Voicy* ከአሁን ጀምሮ በዚህ ምልልስ ውስጥ ያሉ ፋይሎችን*ችላ* ይላቸዋል፡፡ " 2 | files_true: "\U0001F4C1 ድንቅ ! *Voicy* ከአሁን ጀምሮ በዚህ ምልልስ ውስጥ ያሉ ፋይሎችን*እውቅና ለመስጠት ይሞክራል*፡፡" 3 | google: 'የ Google Speech voice recognition (ጉግልን የንግግር ድምጽ መለያን) ለማስጀመር ለዚህ መልዕክት የ Google Cloud credentials file (ጉግል ክላውድ መረጃ ፋይሎችን) በመጠቀም ይመልሱ፡፡ይህ ምንእንደሆነ እና እንዴት እንደሚያደርጉት እርግጠኛ አይደሉም?[ፈጣን የማጥኛ ድረ-ገጻችንን] (https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8) ይመልከቱ፡፡' 4 | google_error_doc: ይቅርታ፡ከመረጃዎችዎ ጋር ሊመልሱ ይገባል፡፡ 5 | google_error_mime: ይቅርታ፡የመረጃዎችዎ's mime አይነት 'text/plain' ሊሆን ይገባዋል፡፡ 6 | google_success: 'እንኳን ደስ አለዎት!*Voicy*ለ*${projectId}* Google Cloud Project (ጉግል ክላውድ ፕሮጀክት)የሚሆኑ መረጃዎችን አግኝቷል፡፡አሁን Google Speech recognition(የጉግል ንግግር መለያ)አገልግሎትን መጠቀም ይችላሉ፡፡' 7 | help: "\U0001F60E *Voicy*ከእያንዳንዱ የድምጽ መልዕክት ወይም ፋይል (.ogg, .flac, .wav, .mp3) የተቀበላቸውን እያንዳንዱን ንግግር ወደ ጽሁፍ ይቀይራል*Voicy*ን በመጠቀም በግል ወይም በቡድን ምልልስ ሊያደርጉ ይችላሉ.\n\nIf ይህንን ቡት በግል መልዕክት መጠቀም ከፈለጉ እባክዎ የግል የሆነ ቡድን በመመስረት*Voicy*ን ይጨምሩበት*Voicy*ን በቡድን ምልልስ ውስጥ መጨመር ከፈለጉ በቡድኑ ፕሮፋይል ላይ እንደተሳታፊ ይጨምሩት ወይም በቡድኑ ፕሮፋይል*Voicy* bot profile ላይ ይጨምሩት\n\n/help — የሚለውን መልዕክት ያሳያል \U0001F631\n/engine — የድምጽ መለያውን ቋንቋ እንዲመርጡ ያደርግዎታል \U0001F4E3\n/lock — በቡድን ምልልስ ወቅት ትእዛዛትን በመጠቀም መክፈት እና መዝጋት ያስችልዎታል \U0001F511\n/files — ቡቱ የድምጽ ፋይሎችን ለመቀየር በሚሞክርበት ጊዜ ቁልፋን በመቀያየር ወይም ችላ በማለት ይረዳችኃል \U0001F4C1\n/silent — ለምሳሌ እንደ `Voice recognition is initiated`(የድምጽ መለየት እንዲጀምር ተደርጓል) የሚሉ አይነት የውጪ መልዕክቶች ሲመጡ ቁልፉን ድምጽ አልባ ያደርገዋል \U0001F636\n/google — ለጉግል የንግግር አገልግሎት መረጃዎችን ያዘጋጃል \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 ይቅርታ፡ይህ ትዕዛዝ የሚሰራው በቡድን ምልልስ ብቻ ነው፡፡" 9 | lock_true: "\U0001F511 ድንቅ! *Voicy* አሁን ምላሽ የሚሰጠው በዚህ ምልልስ ውስጥ ላሉ*admins* የትዕዛዝ ጥሪዎች ብቻ ነው፡፡" 10 | lock_false: "\U0001F511 ድንቅ ! *Voicy* አሁን ምላሽ የሚሰጠው በዚህ ምልልስ ውስጥ ላሉ *ለማንኛቸውም *ነው፡፡" 11 | silent_true: "\U0001F636 ግሩም! *Voicy* አሁን በ*silent mode*ይሰራል፡የድምጹን ቀጥተኛ ትርጓሜ ካልሆነ መልዕክት አያስተላልፍም፡፡" 12 | silent_false: "\U0001F60F ግሩም! *Voicy* አሁን በ*usual mode*ይሰራል፡የድምጽ መልዕክቶቹን እንደተቀበለ ወዲያውኑ `Voice recognition is initiated` (የድምጽ መለየት አገልግሎት ተጀምሯል) የሚል መልዕክት ያስተላልፋል፡፡" 13 | start: "\U0001F44B ሄሎ! *Voicy*የድምጽ መለያ ቡት ሲሆን ሁሉንም የድምጽ መልዕክቶችና የድምጽ ፋይሎች (.ogg, .flac, .wav, .mp3) እንዲሁም በጽሁፍ የተቀበላቸውን ወደ ጽሁፍ ይቀይራል፡፡\n\n*Voicy* ሦስት የድምጽ መለያ ኢንጅኖችን ይደግፋል፡ wit.ai: Yandex SpeechKit እና Google Speech ናቸው፡፡በመጀመሪያ wit.ai እንዲጠቀም የተስተካከለ ቢሆንም ነገር ግን Google Speech ወይም Yandex SpeechKit በማንኛውም ጊዜ እንዲጠቀም አድርገው መቀየር ይችላሉ /ኢንጂን፡፡የበለጠ መረጃ በ ሄልፕ / ያገኛሉ፡፡\n" 14 | engine: "\U0001F44B እባክዎ፡የድምጽ መለያ ኢንጂኑን ይምረጡ፡፡ Google Speech (ጉግል የንግግር መለያ) ከ50 ሰከንድ በላይ ለሆኑ የድምጽ ቅጂዎች ይበልጥ የሚያገለግል ሲሆን ነገር ግን Google Cloud credentials (የጉግከል ክላውድ መረጃዎችዎን) በመጠቀም ሊያስጀምሩት ይገባል(በጣም አድካሚ ነው)፡፡ Yandex SpeechKit ደግሞ ነጻ፣ትክክለኛ፣የግል እና በአብዛኛው ጊዜ ከ50 ሰከንድ በላይ የሆኑ ድምጾችን የሚቀበል ሲሆን ነገር ግን የሚጠቀማቸው ቋንቋዎች ዝርዝር አለው፡፡ Wit.ai ትክለኛነቱ ዝቅ ያለ፣ነጻ እናከ50 ሰከንድ በላይ እርዝመት ያላቸውን ድምጾች የማያቀበል ነገርግን የብዙ ቋንቋዎች ዝርዝር ያለው ነው፡፡እባክዎ፡ሦስቱም የተለያዩ ቋንቋዎችን ይቀበላሉ ስለዚህ ለእርስዎ የሚስማማዎትን መርጠው ይጠቀሙ፡፡" 15 | callback_error: ትዕዛዙን የጀመረው ሰው ብቻ ነው አማራጮችን ሊመርጥ የሚችለው 16 | engine_success: "\U0001F44D አሁን *Voicy*በዚህ ምልልስ ውስጥ *${engine}*ን ይጠቀማል፡፡እናመሰግናለን!ቋንቋ መምረጥ እንዳለብዎት /እንዳይዘነጉ፡፡" 17 | language: "\U0001F44B እባክዎ ለ${engine} የንግግሩን መለያ ቋንቋ አይነት ይምረጡ" 18 | language_without_engine: "\U0001F44B እባክዎ የንግግሩን መለያ ቋንቋ አይነት ይምረጡ" 19 | language_success: "\U0001F44D አሁን *Voicy* በዚህ ምልልስ ውስጥ *${language}* ይናገራል፡፡እናመሰግናለን!" 20 | error_twenty: "_\U0001F46E እኔ 20 ሜጋባይት በላይ የሆኑ መልዕክቶችን መገንዘብ አልችልም_" 21 | initiated: "_\U0001F984 የንግግር መለያ እንዲሰራ ተደርጓል…_" 22 | speak_clearly: "_\U0001F46E እባክዎ፡ልረዳው ስላልቻልኩ በግልጽ ይናገሩ_" 23 | error: "_\U0001F46E ይህንንላውቀው አልቻልኩም_" 24 | google_error_creds: "_\U0001F62E እባክዎ፡የ Google credentials (የጉግል መረጃዎችን) /google command or change the engine (የጉግል ትዕዛዛት ወይም ኢንጂኑን ቀይር)/ከኢንጂን ትዕዛዝ ጋር ያድርጉት፡፡መረጃዎችም እስካሁን አልተያያዙም፡፡_" 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/fa.yaml: -------------------------------------------------------------------------------- 1 | files_false: "‏\U0001F4C1 عالیه! وُیسی تمام فایل های صوتی در این چت از الآن رو نادیده میگیره." 2 | files_true: "‏\U0001F4C1 عالیه! وُیسی تلاش میکنه تمام فایل های صوتی در این چت از الآن رو شناسایی کنه." 3 | google: '‏به این پیام با فایل اعتبارنامه Google Cloud (.json) پاسخ بده تا تشخیص صدای Google Speech رو تنظیم کنید. مطمئن نیستید که این چی هست و چجوری میشه گرفتش؟ ]فیلم آموزشی سریع ما روببینید https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8.' 4 | google_error_doc: ‏با عرض پوزش، شما باید با یک سند اعتبارنامه پاسخ دهید. 5 | google_error_mime: ‏با عرض پوزش، نوع mime سند باید 'text/plain' باشد. 6 | google_success: '‏تبریک میگم! وُیسی فایل اعتبار نامه برای پروژه ${projectId} Google Cloud را دریافت کرد. اکنون شما قادر به استفاده از تشخیص صدای گوگل هستید.' 7 | help: "‏وُیسی هر گفتار از پیام و فایل های صوتی (.ogg، .flac، .wav، .mp3) را که دریافت میکند به متن تبدیل می کند. شما می توانید با وُیسی در چت خصوصی صحبت کنید و یا آن را به یک گروه اضافه کنید.\n\n‏اگر می خواهید از این ربات در پیام های خصوصی استفاده کنید، لطفا یک گروه خصوصی با هرکسی بسازید و وُیسی را آنجا اضافه کنید. اگر می خواهید وُیسی را به یک چت گروهی اضافه کنید، لطفا آن را به عنوان مشارکت کننده در پروفایل گروه یا به گروه در پروفایل ربات وُیسی اضافه کنید.\n\n‏/help — این پیام را نشان می دهد\n‏/engine — اجازه می دهد تا یک موتور تشخیص صدا را انتخاب کنید: wit.ai، Yandex SpeechKit یا Google Speech\n‏/language — به شما اجازه می دهد تا یک زبان تشخیص صدا را انتخاب کنید \U0001F4E3\n‏/lock — بین قفل کردن یا بازکردن غیر مدیران با استفاده از دستورات در گروه های چت تغییر وضعیت میدهد \U0001F511\n‏/files — اگر ربات برای تبدیل فایل های صوتی تلاش کند یا فقط آنها را نادیده بگیرد تغییر وضعیت میدهد \U0001F4C1\n‏/silent — هنگامی که هیچ پیام اضافی مانند ‘تشخیص صدا آغاز شده است’ فرستاده میشود به حالت سکوت تغییر وضعیت میدهد \U0001F636\n‏/google — اعتبارنامه های گوگل برای گفتار گوگل را تنظیم کنید\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "‏\U0001F605 متاسفم، اما این دستور فقط در چت گروهی کار می کند." 9 | lock_true: "‏\U0001F511 خیلی خوب! وُیسی اکنون تنها به تماسهای دستوری ارسال شده توسط ادمین ها در این چت واکنش خواهد داد." 10 | lock_false: "‏\U0001F511 خیلی خوب! وُیسی اکنون تنها به تماسهای دستوری ارسال شده توسط هر شخصی در این چت واکنش خواهد داد." 11 | silent_true: "‏\U0001F636 عالی! وُیسی در حال حاضر در حالت سکوت کار می کند: هیچ پیامی به غیر از رونویسی صوتی به چت ارسال نمی کند." 12 | silent_false: "‏\U0001F60F عالی! وُیسی در حال حاضر در حالت معمولی کار می کند: پیام ‘تشخیص صدا آغاز شده است’ را درست پس از دریافت پیام های صوتی ارسال می کند." 13 | start: "‏\U0001F44B درود بر شما! وُیسی یک ربات تشخیص صدا است که تمام پیام ها و فایل های صوتی (ogg, .flac, .wav, .mp3.) را به متن تبدیل می کند.\n\n‏وُیسی از سه موتور تشخیص صدا پشتیبانی می کند: wit.ai ، Yandex SpeechKit و گفتار گوگل. ابتدا تنظیم شده است که از wit.ai استفاده کند اما میتوانید در هر زمان آن را در /engine به Google Speech یا Yandex SpeechKit تغییر دهید. اطلاعات بیشتر در /help.\n" 14 | engine: "‏\U0001F44B لطفا موتور تشخیص گفتار را انتخاب کنید. گفتار گوگل دقیق تر است و از صوت بیش از 50 ثانیه پشتیبانی می کند، اما باید با اعتبار Google Cloud شما تنظیم شود (کمی خسته کننده است). Yandex SpeechKit بسیار دقیق، رایگان، خصوصی است و زمان صوتی بیش از 50 ثانیه را پشتیبانی می کند، اما لیست زبان های محدودی دارد. Wit.ai کمتر دقیق است، رایگان است، و از صدای بیش از 50 ثانیه پشتیبانی نمیکند، اما دارای زبان های زیادی است. لطفا توجه داشته باشید که هر سه از زبان های مختلف پشتیبانی می کنند، بنابراین یکی را که به بهترین شکل مناسبتان است را انتخاب کنید." 15 | callback_error: ‏فقط فردی که دستور را را آغاز کرده می تواند گزینه ها را انتخاب کند 16 | engine_success: "‏\U0001F44D اکنون وُیسی از ${engine} در این چت استفاده می کند. متشکرم! فراموش نکنید که /language را تنظیم کنید." 17 | language: "‏\U0001F44B لطفا زبان تشخیص گفتار را برای ${engine} انتخاب کنید" 18 | language_without_engine: "‏\U0001F44B لطفا زبان تشخیص گفتار را انتخاب کنید" 19 | error_twenty: "‏_\U0001F46E من نمی توانم پیام های صوتی بزرگتر از 20 مگابایت را تشخیص دهم_" 20 | initiated: "‏_\U0001F984 تشخیص صدا آغاز شده است..._" 21 | speak_clearly: "‏_\U0001F46E لطفا به وضوح صحبت کنید، نمی توانم آن را تشخیص دهم_" 22 | error: "‏_\U0001F46E من نمی توانم آن را تشخیص دهم_" 23 | google_error_creds: "‏\U0001F62E لطفا اعتبارنامه گوگل را با دستور /google تنظیم کنید یا موتور را با فرمان /engine تغییر دهید. اعتبارنامه های شما هنوز تنظیم نشده است.\uFEFF" 24 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 25 | -------------------------------------------------------------------------------- /locales/fr.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Merveilleux ! *Voicy* va *ignorer* tous les fichiers audio de cette discussion à partir de maintenant." 2 | files_true: "\U0001F4C1 Merveilleux ! *Voicy* va *essayer de reconnaître* tous les fichiers audio de cette discussion à partir de maintenant." 3 | google: "Répondez à ce message en utilisant le fichier d'identification Google Cloud (.json) pour configurer la reconnaissance vocale de Google Speech. Vous ne savez pas ce que c'est ou comment l'obtenir ? Consultez [notre tutoriel rapide] (https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)." 4 | google_error_doc: "Désolé, vous devez répondre avec un document d'identification." 5 | google_error_mime: 'Désolé, le type MIME du document doit être "text/plain".' 6 | google_success: "Félicitations ! *Voicy* a obtenu le fichier d'identification du projet Google Cloud *${projectId}*. Vous pouvez désormais utiliser la reconnaissance vocale de Google." 7 | help: "\U0001F60E *Voicy* convertit la voix en texte à partir de tous les messages vocaux et fichiers audio (.ogg, .flac, .wav, .mp3) qu'il reçoit. Vous pouvez parler à *Voicy* soit par message privé, soit en l'ajoutant à un groupe. Si vous voulez utiliser ce bot dans des messages privés, veuillez créer un groupe privé avec quelqu'un et y ajouter *Voicy*.\n\nSi vous voulez ajouter *Voicy* à une discussion de groupe, veuillez l'ajouter en tant que participant en utilisant soit le profil du groupe, soit le profil du bot *Voicy*.\n\n/help — Affiche ce message \U0001F631\n/engine — Vous permet de choisir un moteur de reconnaissance vocale : wit.ai ou Google Speech ⚙\n/language — Vous permet de choisir la langue de reconnaissance vocale \U0001F4E3\n/lock — Vous permet de choisir de verrouiller ou déverrouiller des non-administrateurs à l'aide de commandes dans les discussions de groupe \U0001F511\n/files — Vous permet de choisir si le bot doit tenter de convertir des fichiers audio ou les ignorer \U0001F4C1\n/silent — Active le mode silencieux lorsqu'aucun message supplémentaire comme `La reconnaissance vocale est lancée` n'est envoyé \U0001F636\n/google — Vous permet de configurer les identifiants Google pour Google Speech \U0001F986\n/geeky — Le reste des fonctions supplémentaires utilisées principalement par des utilisateurs avancés (comme le partage des identifiants Google ou le dépassement de la limite de 20 Mo sur Telegram) \U0001F47B\n/privacy — Politique de confidentialité\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Désolé, cette commande ne fonctionne que dans les discussions de groupe." 9 | lock_true: "\U0001F511 Parfait ! *Voicy* ne répondra désormais qu'aux appels de commande envoyés par les *admins* de cette discussion." 10 | lock_false: "\U0001F511 Parfait ! *Voicy* ne répondra désormais qu'aux appels de commande de *tous* les participants à cette discussion." 11 | silent_true: "\U0001F636 Magnifique ! *Voicy* fonctionnera désormais en mode *silencieux* : il n'enverra plus de messages à la discussion, à l'exception des transcriptions vocales." 12 | silent_false: "\U0001F60F Magnifique ! *Voicy* va maintenant fonctionner en *mode normal* : il enverra le message `La reconnaissance vocale est initiée` juste après avoir reçu des messages vocaux." 13 | start: "\U0001F44B Bien le bonjour ! *Voicy* est un bot de reconnaissance vocale qui convertit en texte tous les messages vocaux et les fichiers audio (.ogg, .flac, .wav, .mp3) qu'il reçoit. *Voicy* utilise deux moteurs de reconnaissance vocale : wit.ai et Google Speech. Par défaut, il est configuré pour utiliser wit.ai mais vous pouvez passer à Google Speech à tout moment dans /engine. Plus d'informations sur /help." 14 | engine: "\U0001F44B Veuillez sélectionner le moteur de reconnaissance vocale. Google Speech est plus précis mais doit être configuré avec vos identifiants Google Cloud (un peu fastidieux). Wit.ai est moins précis mais gratuit. Veuillez noter que les deux moteurs supportent différentes langues, alors choisissez celui qui vous convient le mieux." 15 | callback_error: Seule la personne qui a lancé la commande peut sélectionner des options 16 | engine_success: "\U0001F44D Désormais, *Voicy* utilise *${engine}* dans cette discussion. Merci ! N'oubliez pas de régler /language." 17 | language: "\U0001F44B Veuillez sélectionner la langue de reconnaissance vocale pour ${engine}" 18 | language_without_engine: "\U0001F44B Veuillez sélectionner la langue de reconnaissance vocale" 19 | language_success: "\U0001F44D Désormais, *Voicy* parle *${language}* (${engine}) dans cette discussion.Merci!" 20 | error_twenty: "_\U0001F46E Je ne peux pas reconnaître les messages vocaux de plus de 20 megabytes_" 21 | initiated: "_\U0001F984 La reconnaissance vocale est lancée..._" 22 | speak_clearly: "_\U0001F46E S'il vous plaît, parlez distinctement, je n'ai pas pu reconnaître ça_" 23 | error: "_\U0001F46E Je n'ai pas pu reconnaître ça_" 24 | google_error_creds: "\U0001F62E Veuillez configurer les identifiants Google avec la commande /google ou changer le moteur avec la commande /engine. Vos identifiants ne sont pas encore configurés." 25 | ashmanov_language: La nanosémantique ne soutient que la langue russe. Veuillez d'abord changer /engine pour changer /language. 26 | geeky: | 27 | Bienvenue dans le côté obscur, je serai votre guide. S'il vous plaît, profitez des fonctions suivantes de manière responsable (ou pas, à vous de décider). Si vous avez des questions sur les fonctions geeky — contournez @borodutch\_support et envoyez vos requêtes directement à @borodutch. Votre opinion et vos suggestions sont importantes pour moi, chers collègues utilisateurs de force ! 28 | 29 | /enableGoogle — /enableGoogle — Partagez vos identifiants Google personnels avec cette discussion sans les exposer 30 | /disableGoogle — Supprimez vos identifiants Google personnels de cette discussion sans les exposer 31 | /timecodes — Affichez ou cachez les timecodes dans les transcriptions (lorsque *Voicy* spécifie les timecodes pour chaque morceau audio transcrit) 32 | /l — même que /language 33 | `/l en` — raccourci pour changer la langue, essayez-le avec des codes de langue différents 34 | google_enable_personal_not_setup: Il semble que vos identifiants personnels Google Cloud ne sont pas encore configurés. Veuillez le faire dans @voicybot avant d'essayer d'activer votre clé Google dans cette discussion. 35 | google_enable_success: Merveilleux. Vos identifiants Google Cloud seront désormais utilisés dans cette discussion. 36 | google_disable_personal_not_setup: Il semble que vos identifiants personnels Google Cloud ne sont pas encore configurés. Veuillez le faire dans @voicybot avant d'essayer de désactiver votre clé Google dans cette discussion 37 | google_disable_success: Génial. Vos identifiants Google Cloud ne seront plus utilisés dans cette discussion. 38 | oops: "Mince ! Je n'ai pas encore implémenté cette fonctionnalité. Mais rassurez-vous : J'ai reçu votre demande et je sais que vous avez besoin de cette fonction. Je vous remercie !" 39 | timecodes_true: 'Joli ! Maintenant, *Voicy* ajoutera des codes temporels au texte reconnu.' 40 | timecodes_false: Joli ! Maintenant *Voicy* n'ajoutera plus de codes temporels au texte reconnu. 41 | url: 'Si vous voulez contourner la limite de 20 Mo par fichier sur Telegram, vous pouvez utiliser directement voicybot.com. A la vôtre !' 42 | -------------------------------------------------------------------------------- /locales/hi.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 शानदार! *Voicy* इस चैट में अब आगे से सारी ऑडियो फाइल्स को *नज़रअन्दाज़* कर देगा।" 2 | files_true: "\U0001F4C1 शानदार! *Voicy* इस चैट में अब आगे से सारी ऑडियो फाइल्स को *रिकॉग्नाइज करने की कोशिश* करेगा।" 3 | google: 'Google Speech के वॉयस रेकग्निशन को सेट करने के लिए Google Cloud क्रेडेंशियल फाइल (.json) के साथ इस मैसेज का रिप्लाई करें। समझ में नहीं आ रहा है कि यह क्या है और इसे कैसे पाया जा सकता है? यह देखें [हमारा त्वरित ट्यूटोरियल](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)।' 4 | google_error_doc: 'क्षमा करें, आपको किसी क्रेडेंशियल डॉक्यूमेंट के साथ रिप्लाई करना चाहिए।' 5 | google_error_mime: "क्षमा करें, डॉक्यूमेंट का माइम टाइप 'text/plain' होना चाहिए।" 6 | google_success: 'बधाई हो! *Voicy* को *${projectId}* Google Cloud प्रोजेक्ट के लिए क्रेडेंशियल्स फ़ाइल मिल गई। अब आप Google Speech रेकग्निशन का उपयोग कर सकते हैं।' 7 | help: "\U0001F60E *Voicy* सभी वॉयस मैसेज और ऑडियो फ़ाइलों (.ogg, .flac, .wav, .mp3) से प्राप्त होने वाली आवाज को टेक्स्ट में परिवर्तित करता है। आप निजी बातचीत में भी *Voicy* से बात कर सकते हैं या इसे किसी ग्रुप में भी जोड़ सकते हैं।\n\nअगर आप निजी संदेशों में इस बॉट का उपयोग करना चाहते हैं, तो कृपया, किसी के साथ एक निजी ग्रुप बनायें और वहाँ *Voicy* को जोड़ें। यदि आप किसी ग्रुप चैट में *Voicy* को जोड़ना चाहते हैं, तो कृपया इसे ग्रुप प्रोफ़ाइल पर या *Voicy* बॉट प्रोफाइल में ग्रुप में एक प्रतिभागी के रूप में जोड़ें।\n\n/help — इस मैसेज को दिखाता है \U0001F631\n/engine — आपको कोई वॉयस रेकग्निशन इंजन चुनने देता है: wit.ai, या Google Speech ⚙\n/language — आपको एक वॉयस रेकग्निशन भाषा चुनने देता है \U0001F4E3\n/lock — ग्रुप चैट में कमांड का उपयोग करके नॉन-एडमिन के लॉक या अनलॉक को टॉगल करता है \U0001F511\n/files — टॉगल करता है कि बॉट को ऑडियो फ़ाइलों को कन्वर्ट करने का प्रयास करना चाहिए या केवल उन्हें अनदेखा करना चाहिए \U0001F4C1\n/silent — साइलेंट मोड में टॉगल करता है जब 'वॉयस रेकग्निशन शुरू की गयी है' जैसे कोई अतिरिक्त मैसेज नहीं भेजे जाते हैं \U0001F636\n/google — Google Speech के लिए Google क्रेडेंशियल सेट करता है \U0001F986\n/geeky — बाकी अतिरिक्त फंक्शनस जो ज्यादातर एडवांस्ड यूजर्स द्वारा उपयोग किए जाते हैं (जैसे कि Google क्रेडेंशियल साझा करना या 20Mb टेलीग्राम लीमिट को दरकिनार करना) \U0001F47B\n/privacy — गोपनीयता नीति \n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 क्षमा करें, लेकिन यह कमांड केवल ग्रुप चैट में ही काम करती है।" 9 | lock_true: "\U0001F511 बहुत बढ़िया! *Voicy* अब इस चैट में *एडमिनस* द्वारा भेजे गए कमांड कॉल का ही जवाब देगा।" 10 | lock_false: "\U0001F511 बहुत बढ़िया! *Voicy* अब इस चैट में *किसी के भी* कमांड कॉल का जवाब देगा।" 11 | silent_true: "\U0001F636 अद्भुत! *Voicy* अब *साइलेंट मोड* में काम करेगा: यह वास्तविक वॉयस ट्रांसक्रिप्शनस को छोड़कर चैट में कोई मैसेज नहीं भेजेगा।" 12 | silent_false: "\U0001F60F अद्भुत! *Voicy* अब *सामान्य मोड* में काम करेगा: यह वॉयस मैसेज प्राप्त करने के तुरंत बाद 'वॉयस रेकग्निशन शुरू की गयी है' मैसेज भेज देगा।" 13 | start: "\U0001F44B नमस्ते! *Voicy* एक वॉयस रेकग्निशन बॉट है जो सभी वॉयस मैसेज और ऑडियो फ़ाइलों (.ogg, .flac, .wav, .mp3) को टेक्स्ट में परिवर्तित करता है।\n\n*Voicy* दो वॉयस रेकग्निशन इंजन्स को सपोर्ट करता है: wit.ai और Google Speech। सबसे पहले यह wit.ai का उपयोग करने के लिए सेट किया गया है लेकिन आप किसी भी समय में /engine Google Speech में बदल सकते हैं। अधिक जानकारी /help में है।\n" 14 | engine: "\U0001F44B कृपया, स्पीच रेकग्निशन के इंजन का चयन करें। Google Speech अधिक सटीक है लेकिन इसे आपके Google Cloud क्रेडेंशियल्स के साथ सेट अप करना होगा (थोड़ा कठिन)। Wit.ai कम सटीक है लेकिन, नि: शुल्क है। कृपया, ध्यान दें कि दोनों अलग-अलग भाषाओं का समर्थन करते हैं, इसलिए आपके लिए जो ज्यादा बेहतर है वह चुनें।" 15 | callback_error: केवल वही व्यक्ति विकल्प चुन सकता है जिसने कमांड शुरू किया 16 | engine_success: "\U0001F44D अब *Voicy* इस चैट में *${engine}* का उपयोग करता है। धन्यवाद! /language सेट करना न भूलें।" 17 | language: "\U0001F44B कृपया ${engine} के लिए स्पीच रेकग्निशन की भाषा चुनें" 18 | language_without_engine: "\U0001F44B कृपया स्पीच रेकग्निशन की भाषा चुनें" 19 | language_success: "\U0001F44D अब *Voicy* इस चैट में *${language}* (${engine}) बोलती है। धन्यवाद!" 20 | error_twenty: "_\U0001F46E मैं 20 मेगाबाइट्स_ से बड़े वॉयस मैसेज को रिकॉग्नाइज नहीं कर सकता" 21 | initiated: "_\U0001F984 वॉयस रेकग्निशन शुरू की गयी है..._" 22 | speak_clearly: "_\U0001F46E कृपया, स्पष्ट रूप से बोलें, मैं उसे रिकॉग्नाइज नहीं कर सका_" 23 | error: "_\U0001F46E मैं उसकी पहचान नहीं कर सका_" 24 | google_error_creds: "_\U0001F62E कृपया, /google command के साथ गूगल क्रेडेंशियल्स को सेट करें या /engine कमांड के साथ इंजन को बदल दें। आपके क्रेडेंशियल्स अभी तक सेटअप नहीं हुए हैं।_" 25 | ashmanov_language: 'नैनोसेमिंटिक्स केवल रूसी का समर्थन करता है। कृपया, /langauge बदलने के लिए पहले /engine बदलें। ' 26 | geeky: | 27 | डार्क साइड में आपका स्वागत है, मैं आपका मार्गदर्शक बनूंगा। कृपया, जिम्मेदारी से निम्नलिखित कार्यों का आनंद लें (या नहीं, आप तय करें)। यदि आपके पास geeky फ़ंक्शंस के बारे में कोई प्रश्न हैं — @borodutch\_support को बायपास करें और सीधे @borodutch को अनुरोध भेजें। साथी पावर युजर्स, मैं आपकी राय और सुझावों को महत्व देता हूं! 28 | 29 | /enableGoogle — इस चैट के ज़रिए बिना अपने व्यक्तिगत Google क्रेडेंशियल्स को उजागर किए साझा करें 30 | /disableGoogle — अपनी व्यक्तिगत Google क्रेडेंशियल्स को इस चैट से बाहर निकाले बिना उन्हें उजागर किए 31 | /timecodes — ट्रांस्क्रिप्शन्स में टॉगल टाइमकोड्स (जब *वॉयसी* ट्रांस्क्रािब्ड ऑडियो के प्रत्येक टुकड़े के लिए टाइमकोड निर्दिष्ट करता है) 32 | /l — एक समान जैसी /langauge 33 | `/l en` — भाषा बदलने का शॉर्टकट, इसे विभिन्न भाषा कोडस के साथ आज़माएं 34 | google_enable_personal_not_setup: 'लगता है कि आपके व्यक्तिगत Google क्लाउड क्रेडेंशियल अभी तक सेट नहीं किए गए थे। कृपया, इस चैट में अपनी Google key को इनेब्ल करने का प्रयास करने से पहले @voicybot में वो सेट करें। ' 35 | google_enable_success: अद्भुत। अब आपके Google क्लाउड क्रेडेंशियल्स का उपयोग इस चैट में किया जाएगा। 36 | google_disable_personal_not_setup: 'लगता है कि आपके व्यक्तिगत Google क्लाउड क्रेडेंशियल अभी तक सेट नहीं किए गए थे। कृपया, इस चैट में अपनी Google key को डिसेब्ल करने का प्रयास करने से पहले @voicybot में वो सेट करें। ' 37 | google_disable_success: अद्भुत। आपके Google क्लाउड क्रेडेंशियल्स का उपयोग अब इस चैट में नहीं किया जाएगा। 38 | oops: 'उफ़! मैंने इस सुविधा को अभी तक लागू नहीं किया है। लेकिन निश्चिंत रहें: मुझे इसके लिए आपका अनुरोध मिल गया है और आपको पता चल जाएगा कि आपको इस सुविधा की आवश्यकता है। धन्यवाद!' 39 | timecodes_true: अच्छा! अब *Voicy* रिकॉग्नाइज्ड टेक्स्ट में टाइमकोड जोड़ देगा। 40 | timecodes_false: अच्छा! अब *Voicy* रिकॉग्नाइज्ड टेक्स्ट में टाइमकोड नहीं जोड़ेगा। 41 | url: 'यदि आप प्रति फ़ाइल 20Mb की टेलीग्राम लीसिट को बायपास करना चाहते हैं, तो आप सीधे Voicybot.com का उपयोग कर सकते हैं। चियर्स!' 42 | -------------------------------------------------------------------------------- /locales/id.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Hebat! *Voicy* akan *mengabaikan* semua file audio dalam obrolan ini mulai dari sekarang." 2 | files_true: "\U0001F4C1 Hebat! *Voicy* akan *mencoba mengenali* semua file audio dalam obrolan ini mulai dari sekarang." 3 | google: 'Balas pesan ini dengan fail kredensial Google Cloud berekstensi (.json) untuk menyiapkan fitur pengenalan suara dari Google Speech. Tidak yakin fail apa itu dan bagaimana mendapatkannya? Lihat [tutorial singkat kami](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Maaf, Anda harus membalas pesan ini dengan dokumen kredensial.' 5 | google_error_mime: "Maaf, dokumen tipe mime harus berupa 'text/plain'." 6 | google_success: 'Selamat! *Voicy* telah mendapatkan fail kredensial untuk Proyek Google Cloud *${projectId}*. Sekarang Anda dapat menggunakan fitur pengenalan Suara Google.' 7 | help: "\U0001F60E *Voicy* dapat mengkonversi ucapan menjadi teks dari pesan suara dan fail audio apapun (.ogg, .flac, .wav, .mp3) yang diterimanya. Anda dapat berbicara dengan *Voicy* dalam obrolan pribadi atau menambahkannya ke grup.\n\nJika anda ingin menggunakan bot ini dalam pesan pribadi, buat grup pribadi dengan siapa saja, lalu tambahkan *Voicy*. Jika anda ingin menambahkan *Voicy* ke dalam obrolan grup, silakan tambahkan *Voicy* sebagai peserta di profil grup atau di grup dalam profil bot *Voicy*.\n\n/help — Menampilkan pesan ini \U0001F631\n/engine — Memungkinkan Anda memilih mesin pengenalan suara: wit.ai atau Google Speech ⚙\n/language — Memungkinkan Anda memilih bahasa pengenalan suara \U0001F4E3\n/lock — Untuk mengunci atau membuka kunci nonadmin menggunakan perintah di obrolan grup \U0001F511\n/files — Mengalihkan jika bot mencoba mengkonversi fail audio atau mengabaikannya \U0001F4C1\n/silent — Mengalihkan ke mode senyap saat tidak ada pesan tambahan seperti `Pengenalan suara dimulai` dikirim \U0001F636\n/google — Menyiapkan kredensial Google untuk Google Speech \U0001F986\n/geeky — Fungsi tambahan yang kebanyakan digunakan oleh pengguna tingkat lanjut (seperti berbagi kredensial Google atau mengabaikan batas 20 MB dalam Telegram) \U0001F47B\n/privacy — Kebijakan privasi\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | lock_true: "\U0001F511 Bagus! *Voicy* sekarang hanya akan merespon perintah yang dikirim oleh *admin* dalam obrolan ini." 9 | lock_false: "\U0001F511 Bagus! *Voicy* sekarang hanya akan merespon perintah-perintah yang dikirimkan oleh *semua orang* dalam obrolan ini." 10 | silent_true: "\U0001F636 Sangat Bagus! *Voicy* sekarang akan bekerja pada mode *senyap*: *Voicy* tidak akan mengirimkan pesan ke dalam obrolan kecuali untuk transkripsi suara aktual." 11 | silent_false: "\U0001F60F Sangat Bagus! *Voicy* sekarang akan bekerja pada mode *biasa*: *Voicy* akan mengirimkan pesan `Pengenalan suara dimulai` segera setelah menerima pesan suara." 12 | start: "\U0001F44B Halo! *Voicy* adalah bot pengenalan suara yang mengubah semua pesan suara dan fail audio (.ogg, .flac, .wav, .mp3) menjadi teks.\n\n*Voicy* mendukung dua mesin pengenalan suara: wit.ai dan Google Speech. Awalnya bot ini diatur untuk menggunakan wi.ai, tetapi Anda dapat beralih ke Google Speech kapan saja dalam /engine. Informasi lebih lanjut dapat dilihat dalam /help.\n" 13 | engine: "\U0001F44B Harap pilih mesin pengenalan suara. Google Speech lebih akurat, tetapi harus diatur dengan fail kredensial Google Cloud Anda (sedikit rumit). Wit.ai kurang akurat, tetapi gratis. Harap perhatikan bahwa keduanya mendukung bahasa yang berbeda, jadi pilihlah yang paling cocok untuk Anda" 14 | callback_error: Hanya orang yang memulai perintah yang dapat mengatur opsi 15 | engine_success: "\U0001F44D Sekarang *Voicy* menggunakan *${engine}* dalam obrolan ini. Terima kasih! Jangan lupa untuk mengatur /language." 16 | language: "\U0001F44B Silakan pilih bahasa pengenalan suara di ${engine}" 17 | language_without_engine: "\U0001F44B Silakan pilih bahasa pengenalan suara" 18 | language_success: "\U0001F44D Sekarang *Voicy* berbicara dengan bahasa *${language}* (${engine}) dalam obrolan ini. Terima kasih!" 19 | error_twenty: "_\U0001F46E Saya tidak dapat mengenali pesan suara yang melebihi 20 MB._" 20 | initiated: "_\U0001F984 Pengenalan suara dimulai..._" 21 | speak_clearly: "_\U0001F46E Mohon bicara dengan jelas, Saya tidak dapat memahaminya_" 22 | error: "_\U0001F46E Saya tidak dapat memahaminya_" 23 | google_error_creds: "\U0001F62E Silakan atur kredensial google dengan perintah /google atau ganti mesin dengan perintah /engine. Kredensial Anda belum diatur." 24 | ashmanov_language: 'Nanosemantics hanya mendukung bahasa Rusia. Silakan, ubah /engine terlebih dahulu untuk mengubah /language.' 25 | geeky: | 26 | Selamat datang di sisi gelap, Saya akan memandu Anda. Mohon nikmati fungsionalitas berikut dengan bertanggung jawab (atau tidak, pilihan Anda). Jika anda mempunyai pertanyaan mengenai fungsi geeky — lewati @borodutch\_support dan kirimkan permintaan langsung ke @borodutch. Saya menghargai opini dan sugesti Anda, para pengguna 27 | 28 | /enableGoogle — Bagikan kredensial Google personal Anda dalam obrolan ini tanpa membukanya 29 | /disableGoogle — Hapus kredensial Google Anda dari obrolan ini tanpa membukanya 30 | /timecodes — Alihkan kode waktu dalam transkripsi (ketika *Voicy* menentukan kode waktu untuk setiap bagian audio yang ditranskripsi 31 | /l — alias untuk /language 32 | `/l en` — alias untuk mengganti bahasa, coba dengan kode bahasa yang berbeda 33 | google_enable_personal_not_setup: Sepertinya kredensial Google Cloud Anda belum terpasang. Mohon atur terlebih dahulu di @voicybot sebelum mencoba mengaktifkan kunci Google di obrolan ini. 34 | google_enable_success: Keren. Kredensial Google Cloud Anda sekarang akan digunakan pada obrolan ini. 35 | google_disable_personal_not_setup: Sepertinya kredensial Google Cloud Anda belum terpasang. Mohon atur terlebih dahulu di @voicybot sebelum mencoba menonaktifkan Google di obrolan ini. 36 | google_disable_success: Keren. Kredensial Google Cloud Anda sekarang tidak akan digunakan dalam obrolan ini. 37 | oops: "Ooopps! Saya belum mengimplementasikan fitur ini. Namun, yakinlah bahwa saya telah menerima permintaan Anda dan akan mengetahui bahwa Anda membutuhkan fitur ini. Terima kasih!'" 38 | timecodes_true: Bagus! Sekarang *Voicy* akan menambahkan kode waktu pada teks yang dikenali. 39 | timecodes_false: Bagus! Sekarang *Voicy* tidak akan menambahkan kode waktu pada teks yang dikenali. 40 | url: 'Jika anda ingin melewati batas Telegram sebesar 20Mb tiap fail, Anda dapat menggunakan voicybot.com secara langsung. Selamat mencoba!' 41 | -------------------------------------------------------------------------------- /locales/it.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Meraviglioso! *Voicy* *ignorerà* tutti i file audio in questa chat a partire da ora." 2 | files_true: "\U0001F4C1 Meraviglioso! *Voicy* *tenterà di riconoscere* tutti i file audio in questa chat a partire da ora." 3 | google: 'Rispondi a questo messaggio con il file delle credenziali di Google Cloud (.json) per impostare il riconoscimento vocale di Google Speech. Non sei sicuro di cosa si tratta e di come ottenerlo? Dai un’occhiata [il nostro breve tutorial](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Spiacente, dovresti rispondere con un documento di credenziali.' 5 | google_error_mime: 'Spiacente, il formato tipo del documento ‘s dovrebbe essere ’testopiano’.' 6 | google_success: 'Congratulazioni! *Voicy* ha ricevuto il file di credenziali per il progetto Google Cloud *${projectId}*. Ora puoi utilizzare il riconoscimento vocale Google Speech.' 7 | help: "\U0001F60E *Voicy* converte il parlato in testo di qualsiasi messaggio vocale e file audio (.ogg, .flac, .wav, .mp3) che riceve. Puoi anche parlare a *Voicy* nella chat privata o aggiungerlo in un gruppo. \n\nSe vuoi usare questo programma nei messaggi privati, per cortesia, crea un gruppo privato con chiunque e aggiungi *Voicy*. Se desideri aggiungere *Voicy* in una chat di gruppo, per cortesia, aggiungilo come partecipante sul profilo del gruppo oppure al gruppo nel profilo del programma *Voicy*. \n\n/help — Mostra questo messaggio \U0001F631\n/engine — Ti consente di selezionare un motore di riconoscimento vocale: wit.ai, Yandex SpeechKit o Google Speech ⚙\n/language — Ti consente di selezionare un linguaggio di riconoscimento vocale \U0001F4E3\n/lock — Attiva lo sblocco o il blocco dell’uso di comandi nelle chat di gruppo da parte di utenti non amministratori \U0001F511\n/files — Attiva se il programma debba tentare di convertire file audio o semplicemente ignorarli \U0001F4C1\n/silent — Attiva la modalità silenziosa quando non vengono inviati messaggi extra come “Il riconoscimento vocale è iniziato” \U0001F636\n/google — Configurazione delle credenziali Google per Google Speech \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Spiacente ma questo comando funziona solamente nelle chat di gruppo." 9 | lock_true: "\U0001F511 Ottimo! *Voicy* risponderà ora solo ai comandi inviati dagli *amministratori* in questa chat." 10 | lock_false: "\U0001F511 Ottimo! *Voicy* risponderà ora solo ai comandi inviati da *chiunque* in questa chat." 11 | silent_true: "\U0001F636 Magnifico! *Voicy* funzionerà ora in *modalità silenziosa*: non invierà alcun messaggio eccetto le effettive trascrizioni vocali." 12 | silent_false: "\U0001F60F Magnifico! *Voicy* funzionerà ora in *modalità normale*: invierà i messaggi ‘Il riconoscimento vocale è iniziato’ subito dopo aver ricevuto i messaggi vocali. " 13 | start: "| \U0001F44B Ciao! *Voicy* è un programma di riconoscimento vocale che converte tutti i messaggi vocali e i file audio (.ogg, .flac, .wav, .mp3) che riceve in testo.\n*Voicy* supporta tre motori di riconoscimento vocale: wit.ai, Yandex, SpeechKit e Google Speech. Inizialmente è impostato per utilizzare wit.ai ma puoi passare a Google Speech o Yandex SpeechKit in qualsiasi momento in /engine. Maggiori informazioni in /help." 14 | engine: "\U0001F44B Per favore, seleziona il motore di riconoscimento vocale. Google Speech è maggiormente accurato e supporta audio più lunghi di 50 secondi, ma deve essere impostato con le tue credenziali di Google Cloud (un po’ noioso). Yandex Speechkit è piuttosto accurato, gratuito, privato e la maggior parte delle volte supporta audio più lunghi di 50 secondi, ma ha una lista limitata di linguaggi. Wit.ai è meno accurato, gratuito, e non supporta audio più lunghi di 50 secondi, ma ha abbondanza di linguaggi. Per favore, notare che tutti e tre i motori supportano differenti linguaggi, quindi scegli quello che si adatta meglio." 15 | callback_error: Solo la persona che ha lanciato il comando può selezionare le opzioni 16 | engine_success: "\U0001F44D Ora *Voicy* usa *${engine}* in questa chat. Grazie! Non dimenticare di impostare /language." 17 | language: "\U0001F44B Per favore scegli il linguaggio di riconoscimento vocale per ${engine}" 18 | language_without_engine: "\U0001F44B Per favore scegli il linguaggio di riconoscimento vocale" 19 | language_success: "\U0001F44D Adesso Voicy parla *${language}* (${engine}) in questa chat. Grazie!" 20 | error_twenty: "_\U0001F46E Non posso riconoscere messaggi vocali più grandi di 20 megabytes_" 21 | initiated: "_\U0001F984 Il riconoscimento vocale è iniziato..._" 22 | speak_clearly: "_\U0001F46E Per favore, parla chiaramente, non sono riuscito a riconoscerlo_" 23 | error: "_\U0001F46E Non sono riuscito a riconoscerlo_" 24 | google_error_creds: "\U0001F62E Per favore, imposta le credenziali di Google con il comando /google oppure cambia motore con il comando /engine. Le tue credenziali non sono ancora impostate." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/ja.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 さいこう! *ヴォイシー * は、チャット内の音声ファイルを全部 *無視* するよ。" 2 | files_true: "\U0001F4C1 いいねぇ! *ヴォイシー* は、チャット内の音声ファイルを全部 *理解* するよ。" 3 | google: 'グーグルクラウド専用ファイル (.json) を使って返信をし、グーグルスピーチ音声認識をセットアップしましょう。これが何かわからず、どう取得すればいいかわからない? [簡単チュートリアル](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)をご覧ください。' 4 | google_error_doc: すみません、専用ファイルを用いてください。 5 | google_error_mime: すみません、document's mimeは 'text/plain'であるべきです。 6 | google_success: 'よくできました! *ヴォイシー* は *${projectId}* グーグルクラウドプロジェクトの専用ファイルを認識しました。グーグル音声認識が使えるようになりました。' 7 | help: "\U0001F60E *ヴォイシー* はどんな音声ファイル (.ogg, .flac, .wav, .mp3) やボイスメッセージからも文字をおこすことができます。 *ヴォイシー* と個人ででも、グループチャットでも話せます。\n\nもしこのボットをプライベートチャットで利用したいなら、誰かとプライベートグループを作成し、そこに*ヴォイシー*を追加してあげてください。 *ヴォイシー* をグループチャットに追加たい場合、参加者として追加するか、 *ヴォイシー* のプロフィールにあるグループを選択してください。\n\n/help — これを表示する \U0001F631\n/engine — 音声認識エンジンを選択させてくれる: wit.ai, Yandex SpeechKit か Google Speech ⚙\n/language — 音声認識の言語を選択させてくれる。 \U0001F4E3\n/lock — グループチャットの主催者のコマンドをロック、解除する \U0001F511\n/files — ボットが音声ファイルを変換するかしないか \U0001F4C1\n/silent — `音声認識が始まりました` などの追加メッセージがない限りサイレントモードになる \U0001F636\n/google — グーグルスピーチのためグーグル資格情報を設定する \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 ごめんなさい、このコマンドはグループチャットでしか使えません。" 9 | lock_true: "\U0001F511 了解! *ヴォイシー* は *主催者* のコマンドにしか応じないよ。" 10 | lock_false: "\U0001F511 了解! *ヴォイシー* は *みなさん* のコマンドに応じるよ。" 11 | silent_true: "\U0001F636 いいねぇ! *ヴォイシー* はこれから *サイレントモード*: 音声テキスト以外は送信しないよ。" 12 | silent_false: "\U0001F60F いいねぇ! *ヴォイシー* は *通常モード*:ボイスメッセージを受信たときに `音声認識を開始しました` と言うよ。" 13 | start: "\U0001F44B やっほー! *ヴォイシー* はボイスメッセージや音声ファイル (.ogg, .flac, .wav, .mp3)を文字におこす音声認識ボットです。\n\n*ヴォイシー* は三種類の音声認識エンジンが使えます: wit.ai, Yandex SpeechKit と Google Speech.。もともとは wit.ai を利用していたのですが、 Google Speech や Yandex SpeechKit を /engine として利用することもできます。 詳細はこちらで /help.\n" 14 | engine: "\U0001F44B 音声認識エンジンを選択してください。Google Speech はとても正確で50秒以上の音声認識することができますが、Google Cloud credentials とセットアップする必要があります(少し面倒)。 Yandex SpeechKit はまぁまぁ正確、無料、プライベートであり50秒以上の音声をサポートすることが多いですが、言語が限られています。 Wit.ai はあまり正確ではなく、無料、そして 50秒以上の音声は認識できませんが、たくさんの言語が選べます。三種類とも複数の言語を認識できますので、自分に合ったものを選んでください。" 15 | callback_error: コマンドを始めた方のみ、オプション選択できます 16 | engine_success: "\U0001F44D *ヴォイシー* はチャットで *${engine}* を使うよ。ありがとう!/languageを忘れないで。" 17 | language: "\U0001F44B ${engine}の音声認識言語を選択してください。" 18 | language_without_engine: "\U0001F44B の音声認識言語を選択してください。" 19 | language_success: "\U0001F44D これで *ヴォイシー* は *${language}* (${engine}) を話せるよ。ありがとう!" 20 | error_twenty: "_\U0001F46E ぼく、20MB以上のボイスメッセージはわからないんだ_" 21 | initiated: "_\U0001F984 音声認識をはじめます..._" 22 | speak_clearly: "_\U0001F46E わかりませんでした。もう少しはっきり話してください_" 23 | error: "_\U0001F46E わかりませんでした_" 24 | google_error_creds: "\U0001F62E /google コマンドでグーグルクレデンシャルを設定するか、/engine コマンドでエンジンを変更してください。クレデンシャルのセットアップができていません。" 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/ko.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 멋져요! *Voicy* 가 지금부터 이 채팅 내의 모든 음성 파일을 *무시* 합니다." 2 | files_true: "\U0001F4C1 멋져요! *Voicy* 가 지금부터 이 채팅 내의 모든 음성 파일의 *인식을 시도* 합니다." 3 | google: '구글 스피치 음성 인식을 설정하기 위해 구글 클라우드 크리덴셜 파일 (.json)을 이용하여 이 메세지에 답장하세요. 이것이 무엇인지, 어떻게 하는건지 잘 모르시겠나요? [간단한 사용안내](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8)를 확인해보세요.' 4 | google_error_doc: '죄송합니다, 당신은 크리덴셜 문서로 답장하셔야 합니다.' 5 | google_error_mime: "죄송합니다, 문서의 MIME 종류는 'text/plain' 이어야 합니다." 6 | google_success: '축하합니다! *Voicy*가 *${projectId}* 구글 클라우드 프로젝트를 위한 크리덴셜 파일을 얻었습니다. 이제 구글 스피치 인식을 사용할 수 있습니다.' 7 | help: "\U0001F60E *Voicy*는 어떠한 음성 메세지나 음성 파일(.ogg, .flac, .wav, .mp3)도 텍스트로 변환합니다. 당신은 비공개 채팅에서 *Voicy*에게 이야기하거나 혹은 그룹 채팅에 *Voicy*를 추가할 수 있습니다.\n\n비공개 메시지에서 이 봇을 사용하기를 원하신다면, 아무하고나 함께 비공개 그룹을 생성하고 *Voicy*를 추가하세요. 그룹 채팅에 *Voicy*를 추가하고 싶으시다면, 그룹 프로필의 참여자로 *Voicy*를 추가하시거나 *Voicy*봇 프로필 내의 그룹에 추가하세요.\n\n/help — 이 메세지를 보여줍니다 \U0001F631\n/engine — 음성 인식 엔진을 선택합니다: wit.ai, Yandex SpeechKit 혹은 구글 스피치 ⚙\n/language — 음성 인식 언어를 선택합니다 \U0001F4E3\n/lock — 그룹 채팅 내 비관리자의 명령어 사용 잠금 혹은 해제 토글 \U0001F511\n/files — 봇이 음성 파일의 변환을 시도할지 무시할지 토글 \U0001F4C1\n/silent — ‘음성 인식이 시작되었습니다’와 같은 메세지가 더 이상 없을 경우 무음 모드 버튼 \U0001F636\n/google — 구글 스피치를 위한 구글 크리덴셜 설정 \U0001F986\n/geeky — 기타 나머지 기능들은 대부분 고급 사용자들에 의해 사용됩니다 (구글 크리덴셜 공유나 20Mb 텔레그램 제한을 우회하는 것 등) \U0001F47B\n/privacy — 개인정보 보호 정책\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 죄송합니다, 이 명령어은 그룹 채팅에서만 작동합니다." 9 | lock_true: "\U0001F511 좋아요! *Voicy*는 이제 이 채팅의 *관리자*에 의한 명령어에만 응답합니다." 10 | lock_false: "\U0001F511 좋아요! *Voicy*는 이제 이 채팅의 *모든 사람*의 명령어에 응답합니다." 11 | silent_true: "\U0001F636 훌륭해요! *Voicy*는 이제 *무음 모드*로 작동합니다: 실제 음성 기록을 제외한 어떠한 메세지도 보내지 않습니다." 12 | silent_false: "\U0001F60F 훌륭해요! *Voicy*는 이제 *일반 모드*로 작동합니다: 음성 메세지를 받은 직후에 ‘음성 인식이 시작되었습니다’ 메세지를 보냅니다." 13 | start: "\U0001F44B 안녕하세요! *Voicy*는 모든 음성 메세지와 오디오 파일(.ogg, .flac, .wav, .mp3)을 텍스트로 변환하는 음성 인식 봇입니다.\n*Voicy*는 두 가지의 음성 인식 엔진을 지원합니다: wit.ai와 구글 스피치. *Voicy*는 기본적으로 wit.ai를 사용하도록 설정되어 있지만 어느 때나 /engine에서 구글 스피치로 전환할 수 있습니다. 더 많은 정보는 /help 에 있습니다.\n" 14 | engine: "\U0001F44B 음성 인식 엔진을 선택해 주세요. 구글 스피치는 더 정확하지만 구글 클라우드 크리덴셜을 이용해 설정되어야 합니다(약간 지겹지만요). Wit.ai는 덜 정확하지만 무료입니다. 두 엔진은 서로 다른 언어를 지원하므로 당신에게 가장 적합한 것을 고르세요." 15 | callback_error: 명령어를 시작한 사람만이 옵션을 선택할 수 있습니다 16 | engine_success: "\U0001F44D 이제 *Voicy*는 이 채팅에서 *${engine}*을 사용합니다. 감사합니다! /language를 설정하는 것을 잊지 마세요." 17 | language: "\U0001F44B ${engine}을 위한 음성 인식 언어를 선택하세요" 18 | language_without_engine: "\U0001F44B 음성 인식 언어를 선택하세요" 19 | language_success: "\U0001F44D 이제 *Voicy*가 이 채팅에서 *${language}* (${engine})을 말합니다. 감사합니다!" 20 | error_twenty: "_\U0001F46E 저는 20메가바이트 이상의 음성 메세지는 인식할 수 없습니다_" 21 | initiated: "_\U0001F984 음성 인식이 시작됩니다..._" 22 | speak_clearly: "_\U0001F46E 좀 더 정확하게 말씀해주세요. 인식하지 못 했습니다_" 23 | error: "_\U0001F46E 인식하지 못 했습니다_" 24 | google_error_creds: "\U0001F62E 구글 크리덴셜을 /google 명령어로 설정하거나 엔진을 /engine 명령어로 전환하세요. 당신의 크리덴셜이 아직 설정되지 않았습니다." 25 | ashmanov_language: Nanosemantics는 러시아어만 지원합니다. /language를 전환하기 위해 /engine을 먼저 전환해주세요. 26 | geeky: | 27 | 어두운 면에 오신 것을 환영합니다. 저는 여러분의 길잡이가 될 것입니다. 부디 다음의 기능들을 책임감있게 즐겨주시기 바랍니다(아니면 마음대로 하세요). 괴짜 기능들에 대한 질문이 있으시다면 @borodutch\_support를 건너뛰고 @borodutch로 바로 요청을 보내세요. 저는 파워 유저 여러분들의 의견과 제안을 소중히 여깁니다! 28 | 29 | /enableGoogle — 여러분의 개인 구글 크리덴셜을 노출 없이 이 채팅과 공유합니다 30 | /disableGoogle — 여러분의 개인 구글 크리덴셜을 노출 없이 이 채팅에서 삭제합니다 31 | /timecodes — 음성 기록의 타임코드 토글(*Voicy*가 각각 기록된 오디오의 타임코드를 인식했을 때) 32 | /l — /language와 동일 33 | `/l en` — 언어 변환 단축키, 다른 언어 코드들과 함께 사용해보세요 34 | google_enable_personal_not_setup: 당신의 구글 클라우드 크리덴셜이 아직 설정 되지 않은 것 같습니다. 이 채팅에서 당신의 구글 키를 허용하기 전에 @voicybot에서 설정해주세요. 35 | google_enable_success: 훌륭합니다. 이제 당신의 구글 클라우드 크리덴셜이 이 채팅에서 사용될 겁니다. 36 | google_disable_personal_not_setup: 당신의 개인 구글 클라우드 크리덴셜이 아직 설정되지 않은 것 같습니다. 이 채팅에서 당신의 구글 키를 해제하기 전에 @voicybot에서 설정해주세요. 37 | google_disable_success: 훌륭합니다. 당신의 구글 클라우드 크리덴셜이 이 채팅에서 더 이상 사용되지 않을 겁니다. 38 | oops: 이런! 아직 이 기능을 구현하지 못했습니다. 하지만 확실한건 저는 당신의 요청을 받았고 당신이 이 기능이 필요하다는 것을 알게 되었습니다. 감사합니다! 39 | timecodes_true: 좋아요! 이제 *Voicy*가 인식된 텍스트에 타임코드를 추가할 것입니다. 40 | timecodes_false: 좋아요! 이제 *Voicy*가 인식된 텍스트에 타임코드를 추가하지 않을 것입니다. 41 | url: '만약 당신이 파일 당 20Mb의 텔레그램 제한을 우회하고 싶으시다면, voicybot.com을 바로 이용하시면 됩니다. 화이팅!' 42 | -------------------------------------------------------------------------------- /locales/kz.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Великолепно! *Войси* теперь будет *игнорировать* все аудио файлы в этом чате." 2 | files_true: "\U0001F4C1 Великолепно! *Войси* теперь будет *пробовать перевести в текст* все аудио файлы в этом чате." 3 | google: 'Ответьте на это сообщение с файлом аутентификации для Google Cloud (.json), чтобы настроить распознавание речи от Google Speech. Не уверены, как сделать, чтобы все заработало? Прочитайте [наше короткое руководство](https://medium.com/@nikitakolmogorov/%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-google-speech-%D0%B2-voicybot-9f8268cd58c6).' 4 | google_error_doc: 'Ответьте, пожалуйста, аутентификационным документом.' 5 | google_error_mime: "Пожалуйста, убедитесь, что тип документа — 'text/plain'." 6 | google_success: 'Поздравляем! *Войси* получил аутентификационный документ для *${projectId}* проекта Google Cloud. Теперь вы можете использовать движок Google Speech в этом чате.' 7 | help: "\U0001F60E *Войси* переводит в текст все голосовые сообщения и аудио файлы (.ogg, .flac, .wav, .mp3), которые получает. Вы можете использовать *Войси* в личных сообщениях или добавить его в группу.\n\nЕсли вы хотите использовать *Войси* в приватных сообщениях с другими людьми, то, пожалуйста, создайте приватную группу с собеседником и добавьте туда *Войси*. Если хотите добавить *Войси* в групповой чат, то, пожалуйста, добавьте его, как собеседника в профиле группы, или в профиле *Войси*.\n\n/help — Это сообщение \U0001F631\n/engine — Выбор движка распознавания речи: wit.ai или Google Speech ⚙\n/language — Выбор языка распознавания речи \U0001F4E3\n/lock — Включает и выключает обработку команд, отправленных не админами в групповых чатах \U0001F511\n/files — Включает и выключает обработку аудио файлов \U0001F4C1\n/silent — Включает и выключает тихий режим, когда *Войси* не посылает лишних сообщений типа `Распознавание речи инициировано` \U0001F636\n/google — Установка аутентификационного файла для использования движка Google Speech \U0001F986\n/geeky — Еще больше функций, которыми пользуются только продвнутые пользователи (например, шейринг Гугл ключей и обход ограничения Телеграма в 20мб) \U0001F47B\n/privacy — Политика конфиденциальности\n/witToken — Установка вашего Wit токена в формате `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Простите, эта команда работает только в групповых чатах." 9 | lock_true: "\U0001F511 Отлично! *Войси* теперь будет реагировать только на команды, посланные *админами* в этом чате." 10 | lock_false: "\U0001F511 Отлично! *Войси* теперь будет реагировать на команды, посланные *кем угодно* в этом чате." 11 | silent_true: "\U0001F636 Магически! *Войси* теперь работает в *тихом режиме*: он не будет посылать в чат ничего, кроме распознанного текста." 12 | silent_false: "\U0001F60F Магически! *Войси* теперь работает в *обычном режиме*: он будет посылать сообщения типа `Распознавание голоса инициировано` сразу после получения голосовых сообщений." 13 | start: "\U0001F44B Здравствуйте! *Войси* — это бот, который переводит все голосовые сообщения и аудио файлы (.ogg, .flac, .wav, .mp3), которые получает, в текст.\n\n*Войси* поддерживает три движка распознавания речи: wit.ai, Nanosemantics и Google Speech. Изначально, он использует wit.ai, но вы можете переключиться на Google Speech или Nanosemantics в любое время, используя команду /engine. Больше информации в /help.\n" 14 | engine: "\U0001F44B Пожалуйста, выберите движок распознавания речи. Google Speech более точный, но требует, чтобы вы установили аутентификационный файл Google Cloud (немного сложно). Nanosemantics такой же точный и бесплатный, но поддерживает только русский язык. Wit.ai менее точный, но тоже бесплатный. Стоит отметить, что разные движки поддерживают разные языки, так что выберите тот, который подходит вам наиболее всего." 15 | callback_error: 'Только тот, кто запустил выбор, может выбирать настройки' 16 | engine_success: "\U0001F44D Теперь *Войси* использует *${engine}* в этом чате. Спасибо! Не забудьте установить язык через /language." 17 | language: "\U0001F44B Пожалуйста, выберите язык распознавания речи для ${engine}" 18 | language_without_engine: "\U0001F44B Пожалуйста, выберите язык распознавания речи" 19 | language_success: "\U0001F44D Теперь *Войси* использует *${language}* (${engine}) в этом чате. Спасибо!" 20 | error_twenty: "_\U0001F46E Я не умею распознавать файлы тяжелее 20 мегабайт_" 21 | initiated: "_\U0001F984 Распознавание речи инициировано..._" 22 | speak_clearly: "_\U0001F46E Пожалуйста, говорите четче_" 23 | error: "_\U0001F46E У меня не получилось это распознать_" 24 | google_error_creds: "\U0001F62E Пожалуйста, установите аутентификационный файл для Google Cloud через команду /google или смените движок распознавания речи через /engine. Аутентификационный файл еще не установлен." 25 | ashmanov_language: 'Nanosemantics поддерживает только русский язык. Пожалуйста, смените движок командой /engine перед сменой языка командой /language.' 26 | geeky: | 27 | Добро пожаловать на темную сторону, я буду вашим проводником. Пожалуйста, наслаждайтесь следующими функциями с опаской (или без — вам решать). Если у вас есть какие-либо вопросы про гиковские функции, пишите напрямую @borodutch. Мне очень важны ваши мнения и предложения, дорогие продвинутые юзеры! 28 | 29 | /enableGoogle — Установить свои Google ключи в этом чате без показа оных 30 | /disableGoogle — Убрать свои Google ключи из этого чата без показа оных 31 | /timecodes — Включить или выключить таймкоды в тексте (когда *Войси* указывает таймкоды для распознанного по частям текста) 32 | /url — Обойти ограничения Телеграма в 20Мб 33 | /l — коротка форма /language 34 | `/l en` — быстрый способ сменить язык, попробуйте с разными языковыми кодами 35 | google_enable_personal_not_setup: 'Похоже вы еще не установили персональные ключи для Google Cloud. Пожалуйста, установите их в личке @voicybot перед тем, как использовать ключи в этом чате.' 36 | google_enable_success: Отлично. Ваш ключ от Google Cloud теперь будет использован в этом чате. 37 | google_disable_personal_not_setup: 'Похоже вы еще не установили персональные ключи для Google Cloud. Пожалуйста, установите их в личке @voicybot перед тем, как пытаться отключить ключ в этом чате.' 38 | google_disable_success: Отлично. Ваш Google Cloud ключ больше не будет использоваться этим чатом. 39 | oops: 'Упс! Я еще не реализовал эту фичу. Но я был оповещен о том, что вам она нужна. Спасибо!' 40 | timecodes_true: Клево! Теперь *Войси* будет добавлять таймкоды к распознанному тексту. 41 | timecodes_false: Клево! Теперь *Войси* не будет добавлять таймкоды к распознанному тексту 42 | url: 'Если хотите обойти ограничение в 20Мб на файл, можете использовать voicybot.com напрямую. Удачи!' 43 | -------------------------------------------------------------------------------- /locales/no.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Herlig! *Voicy* skal *ignorere* alle lydfiler i denne chatten fra nå av." 2 | files_true: "\U0001F4C1 Herlig! *Voicy* skal *prøv å gjenkjenne* alle lydfiler i denne chatten fra nå av." 3 | google: 'Svar på denne melding med din Google Cloud sine legitimasjon fil (.json) for å konfigurere Google-stemmegjenkjenning. Ikke sikker på hva er dette, og hvordan får man det? Sjekk ut [vår enkel tutorial](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Beklager, du bør svare med et et legitimasjonsdokument.' 5 | google_error_mime: "Beklager, dokument sine mime type burde være 'tekst/plain'." 6 | google_success: 'Gratulerer! *Voicy* fikk legitimasjonsfilen for *${projectId}* Google Cloud Prosjekt. Nå du kan bruke Google talegjenkjenning.' 7 | help: "\U0001F60E *Voicy* konverterer tale til tekst fra alle talemeldinger og lydfiler (.ogg, .flac, .wav, .mp3) den mottar. Du kan enten prata med *Voicy* i private chat eller tilføj det til en gruppe.\n\nHvis du vil bruke dette bot i private meldinger, vennligst opprett en privat gruppe med noen og legg *Voicy* til der. Hvis du vil legge *Voicy* til gruppechat, vennligst, legg det til som deltaker i gruppeprofilen eller til gruppen i *Voicy* bot profil.\n\n/help — Viser denne meldingen \U0001F631\n/engine — Lar deg velge en talegjenkjenningsmotor: wit.ai, Yandex SpeechKit or Google Speech ⚙\n/language — Lar deg velge et talegjenkjenning språk \U0001F4E3\n/lock — Bytter lås eller opplås for ikke-administratorer ved hjelp av kommandoer i gruppechat. \U0001F511\n/files — bytter hvis botet skulle forsøke å konvertere lydfiler eller bare ignorere dem. \U0001F4C1\n/silent — Bytter stillemodus når ingen ekstra meldinger som `Talegjenkjenning er initiert` sendes \U0001F636\n/google — Oppsett google legitimasjon for Google Speech \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Beklager, men denne kommandoen fungerer kun i gruppechatt." 9 | lock_true: "\U0001F511 Flott! *Voicy* skal nå svare kun på kommandoer som sendes av *admins* i denne chatten." 10 | lock_false: "\U0001F511 Flott! *Voicy* skal nå svare kun på kommandoer som sendes av *noen som helst* i denne chatten." 11 | silent_true: "\U0001F636 Storslått! *Voicy* skal nå fungere i *stille modus*: det skal ikke sende meldinger til chatten bortsett fra faktiske stemme transkripsjonene." 12 | silent_false: "\U0001F60F Storslått! *Voicy* skal nå fungere i *vanlig modus*: det skal sende `Talegjenkjenning er initiert` meldinger rett etter at det mottar talemeldinger." 13 | start: "\U0001F44B Hei der! *Voicy* er en stemmegjenkjennings bot som konverterer alle talemeldinger og lydfiler (.ogg, .flac, .wav, .mp3) det mottar til tekst.\n\n*Voicy* støtter tre stemmegjenkjenning motorer: wit.ai, Yandex SpeechKit and Google Speech. Først det satt til bruke wit.ai men du kan bytte til Google Speech eller Yandex SpeechKit når som helst i /engine. Mer informasjon i /help.\n" 14 | engine: "\U0001F44B Vennligst, velg motoren for talegjenkjenning. Google Speech er mer nøyaktig og støtter lyd lenger enn 50 sekunder, men må settes opp med din Google Cloud legitimasjon (litt kjedelig). Yandex SpeechKit er ganske nøyaktig, gratis, privat og det meste støtter lyd lengre enn 50 sekunder, men har begrenset liste over språk. Wit.ai er mindre nøyaktig, gratis, og støtter ikke lyd lenger enn 50 sekunder, men det har mange språk. Vær oppmerksom på at alle tre støtter forskjellige språk, så velg den som passer deg best." 15 | callback_error: 'Kun den personen som startet kommandoen, kan velge alternativer' 16 | engine_success: "\U0001F44D Nå *Voicy* bruker *${engine}* i denne chatten. Takk! Ikke glem å sette /language." 17 | language: "\U0001F44B Vennligst velg språk for talegjenkjenning for ${engine}" 18 | language_without_engine: "\U0001F44B Vennligst velg språk for talegjenkjenning" 19 | language_success: "\U0001F44D Nå *Voicy* snakker *${language}* (${engine}) i denne chatten. Takk!" 20 | error_twenty: "_\U0001F46E Jeg kan ikke gjenkjenne talemeldinger som er større enn 20 megabytes_" 21 | initiated: "_\U0001F984 Stemmegjenkjenning er initiert..._" 22 | speak_clearly: "_\U0001F46E Vennligst, snakk tydelig, jeg kunne ikke gjenkjenne det_" 23 | error: "_\U0001F46E Jeg kunne ikke't gjenkjenne det_" 24 | google_error_creds: "\U0001F62E Vennligst, oppsett google legitimasjon med /google kommandoen eller endre motoren med /engine kommandoen. Din legitimasjon er ikke satt opp ennå." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/pt.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Fantástico! A partir de agora, *Voicy* vai *ignorar* todos os arquivos de áudio nesta conversa." 2 | files_true: "\U0001F4C1 Fantástico! A partir de agora, *Voicy* vai *tentar reconhecer* todos os arquivos de áudio nesta conversa." 3 | google: 'Responda a esta mensagem com os arquivos das credenciais Google Cloud (.json) para configurar o reconhecimento de voz do Google Speech. Desconhece o arquivo e como obtê-lo? Confira [o nosso tutorial rápido](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Desculpe, mas você deve responder com o documento das credenciais.' 5 | google_error_mime: "Desculpe, o formato do documento deve ser 'texto/simples'." 6 | google_success: 'Parabéns! *Voicy* recebeu o arquivo das credenciais para o *${projectId}* Projeto Google Cloud. Agora você pode usar o reconhecimento Google Speech.' 7 | help: "\U0001F60E *Voicy* converte áudio em texto, a partir de qualquer mensagem ou arquivo de áudio (.ogg, .flac, .wav, .mp3) que receba. Você pode falar para o *Voicy* no chat privado ou num grupo.\n\nSe deseja usar este bot em mensagens privadas, por favor crie um grupo privado com alguém e adicione o *Voicy*. Se deseja adicionar *Voicy* a uma conversa de grupo, por favor adicione-o como um participante no perfil do grupo ou no perfil do bot *Voicy*.\n\n/help — Exibe esta mensagem \U0001F631\n/engine — Permite selecionar um motor de reconhecimento de voz: wit.ai ou Google Speech ⚙\n/language — Permite selecionar o idioma para reconhecimento de voz \U0001F4E3\n/lock — Bloqueia ou desbloqueia comandos nos chats de grupo para não-administradores \U0001F511\n/files — Indique se o bot deve tentar converter arquivos áudio ou ignorá-los \U0001F4C1\n/silent — Ative o modo silencioso quando nenhuma mensagem extra como `Reconhecimento de Voz Iniciado` for enviada \U0001F636\n/google — Configure as credenciais do Google Speech \U0001F986\n/geeky — Restante das funções extras, usado principalmente por usuários avançados (como compartilhar credenciais do Google ou ultrapassar o limite de 20Mb do Telegram) \U0001F47B\n/privacy — Política de privacidade\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | lock_true: "\U0001F511 Boa! Agora, *Voicy* só vai responder a comandos enviados por *administradores* neste chat." 9 | lock_false: "\U0001F511 Boa! Agora, *Voicy* vai responder a comandos de *qualquer pessoa* neste chat." 10 | silent_true: "\U0001F636 Ótimo! *Voicy* vai funcionar no *modo silencioso*: não vai enviar mensagens para o chat, exceto para transcrições de voz." 11 | silent_false: "\U0001F60F Ótimo! *Voicy* vai trabalhar no *modo normal*: vai enviar as mensagens `Reconhecimento de Voz Iniciado` depois de receber mensagens de voz." 12 | start: "\U0001F44B Olá! *Voicy* é um bot de reconhecimento de voz que converte em texto todas as mensagens e arquivos de áudio (.ogg, .flac, .wav, .mp3) que recebe.\n\n*Voicy* suporta dois motores de reconhecimento de voz: wit.ai e Google Speech. O bot é predefinido para usar wit.ai, mas se pode alterar para o Google Speech a qualquer momento, usando /engine. Mais informações em /help.\n" 13 | engine: "\U0001F44B Por favor, selecione o motor de reconhecimento de voz. O Google Speech é mais preciso, mas precisa ser configurado com as suas credenciais do Google Cloud (um pouco entediante). O Wit.ai é menos preciso, porém gratuito. Por favor, note que ambos têm suporte a diferentes idiomas, portanto selecione o que mais se ajuste a você." 14 | callback_error: Somente quem iniciou o comando pode selecionar opções 15 | engine_success: "\U0001F44D Agora *Voicy* usa *${engine}* neste chat. Obrigado! Não se esqueça de definir o /language." 16 | language: "\U0001F44B Por favor, selecione o idioma do reconhecimento de voz para ${engine}" 17 | language_without_engine: "\U0001F44B Por favor, selecione o idioma para o reconhecimento de voz" 18 | language_success: "\U0001F44D Agora *Voicy* fala *${language}* (${engine}) neste chat. Obrigado!" 19 | error_twenty: "_\U0001F46E Não consigo reconhecer mensagens de voz com mais de 20 megabytes_" 20 | initiated: "_\U0001F984 Reconhecimento de voz iniciado..._" 21 | speak_clearly: "_\U0001F46E Por favor, fale com clareza, não consegui reconhecer a mensagem_" 22 | error: "_\U0001F46E Não consegui reconhecer a mensagem_" 23 | google_error_creds: "\U0001F62E Por favor, configure as credenciais Google com o comando /google ou altere o motor com o comando /engine. As suas credenciais ainda não foram configuradas." 24 | ashmanov_language: 'A nanosemântica suporta apenas o Russo. Por favor, altere o /engine para alterar o /language primeiro.' 25 | geeky: | 26 | Bem-vindo ao lado obscuro, eu serei seu guia. Por favor, utilize as seguintes funções com responsabilidade (ou não, se quiser). Se você tiver quaisquer questões sobre as funções geeky — evite o @borodutch\_support e envie solicitações diretamente a @borodutch. Valorizo suas opiniões e sugestões, amigos power users! 27 | 28 | /enableGoogle — Compartilha suas credenciais pessoais do Google com este chat, sem expô-las 29 | /disableGoogle — Remove suas credenciais pessoais do Google deste chat, sem expô-las 30 | /timecodes — Altera os timecodes nas transcrições (quando *Voicy* especificar os timecodes para cada parte do áudio transcrito) 31 | /l — O mesmo que /language 32 | `/l en` — atalho para alterar o idioma, use-o com diferentes códigos de idiomas 33 | google_enable_personal_not_setup: 'Parece que as suas credenciais pessoais do Google Cloud não foram configuradas ainda. Por favor, faça isso em @voicybot antes de tentar habilitar a sua chave do Google neste chat.' 34 | google_enable_success: Muito bom. As suas credenciais do Google Cloud serão agora usadas neste chat. 35 | google_disable_personal_not_setup: 'Parece que as suas credenciais pessoais do Google Cloud não foram configuradas ainda. Por favor, faça isso em @voicybot antes de tentar desabilitar a sua chave do Google neste chat' 36 | google_disable_success: Muito bom. As suas credenciais do Google Cloud não serão mais usadas neste chat. 37 | oops: 'Ooops! Ainda não implementei essa ferramenta. Mas saiba que: Recebi sua sugestão e sei que você precisa dessa ferramenta. Obrigado!' 38 | timecodes_true: Ótimo! Agora *Voicy* adicionará timecodes ao texto reconhecido. 39 | timecodes_false: Ótimo! Agora *Voicy* não adicionará timecodes ao texto reconhecido. 40 | url: 'Se você quiser ultrapassar o limite de 20 Mb por arquivo do Telegram, você pode usar o voicybot.com diretamente. Abraços!' 41 | -------------------------------------------------------------------------------- /locales/sv.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Underbart! *Voicy* kommer *ignorera* alla ljudfiler i den h�r chatten fr�n och med nu." 2 | files_true: "\U0001F4C1 Underbart! *Voicy* kommer *f�rs�ka k�nna igen* alla ljudfiler i den h�r chatten fr�n och med nu." 3 | google: 'Svara p� det h�r meddelandet med autentiseringsuppgifterna f�r Google Cloud (.json) f�r att konfigurera Google taligenk�nning. Inte s�ker p� vad det h�r �r eller hur man skaffar det? Se v�r[snabbguide](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'F�rl�t, du skulle svara med ett dokument f�r autentisering.' 5 | google_error_mime: "F�rl�t, dokumentets filtyp ska vara 'text/plain'." 6 | google_success: 'Grattis! *Voicy* fick autentisieringsuppgifterna f�r *${projectId}* Google Cloud Project. Du kan nu anv�nda Google taligenk�nning.' 7 | help: "\U0001F60E *Voicy* konverterar tal till text fr�n alla typer av r�stmeddelanden och ljudfiler (.ogg, .flac, .wav, .mp3) den tar emot. Du kan antingen prata med *Voicy* i den privata chatten eller l�gga till den i en grupp.\n\nOm du vill anv�nda denna bot i privata meddelanden, var god skapa en privat grupp med alla och l�gg till *Voicy* d�r. Om du vill l�gga till *Voicy* till en gruppchatt, var god, l�gg till den som en deltagare i grupprofilen eller till gruppen i *Voicy* bot profil.\n\n/help — Visar det h�r meddelandet \U0001F631\n/engine — L�ter dig v�lja en leverant�r f�r r�stigenk�nning: wit.ai, Yandex SpeechKit or Google Speech ⚙\n/language — L�ter dig v�lja spr�k f�r r�stigenk�nning \U0001F4E3\n/lock — Reglaget l�ser upp eller l�ser m�jligheten f�r icke- administrat�rer att anv�nda kommandon i gruppchatter \U0001F511\n/files — Reglaget reglerar om boten ska f�rs�ka konvertera ljudfiler eller bara ignorera dem \U0001F4C1\n/silent — V�lj ljudl�s n�r inga extra meddelanden som `R�stigenk�nning �r initierad` �nskas \U0001F636\n/google — St�ll in google autentisieringsuppgifter Google Speech \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Detta kommandot fungerar tyv�rr bara i gruppchatter." 9 | lock_true: "\U0001F511 Bra! *Voicy* kommer nu bara svara p� kommandon skickade av *administrat�rer* i den h�r chatten." 10 | lock_false: "\U0001F511 Bra! *Voicy* kommer nu svara endast p� samtal fr�n *alla* i den h�r cahtten." 11 | silent_true: "\U0001F636 Fantastiskt! *Voicy* kommer nu vara i *tyst l�ge*: den kommer inte s�nda n�gra meddelanden till chatten f�rutom de faktiska r�st transkriptionerna." 12 | silent_false: "\U0001F60F Fantastiskt! *Voicy* kommer nu fungera i *normalt l�ge*: den kommer skicka `R�stigenk�nning �r initierad` meddelanden direkt efter att den tar emot r�stmeddelanden." 13 | start: "\U0001F44B Hej d�r! *Voicy* �r en r�stigenk�nnings bot som konverterar alla r�stmeddelanden och ljudfiler (.ogg, .flac, .wav, .mp3) den f�r till text.\n\n*Voicy* har st�d f�r tre r�stigenk�nningsmotorer: wit.ai, Yandex SpeechKit and Google Speech. Fr�n b�rjan �r den inst�lld p� att anv�nda wit.ai men du kan byta till Google Speech eller Yandex SpeechKit n�r som helst i /engine. Mer information i /help.\n" 14 | engine: "\U0001F44B Var god, v�lj motorn f�r r�stigenk�nning. Google Speech �r mer exakt och st�djer audio l�ngre �n 50 sekunder, men m�ste konfigureras med google cloud autentiseringsuppgifter (lite omst�ndigt). Yandex SpeechKit �r ganska exakt, gratis, privat och st�djer f�r det mesta audio �ver 50 sekunder, men st�djer ett begr�nsat antal spr�k. Wit.ai �r inte lika exakt, gratis, och st�djer inte audio l�ngre �n 50 sekunder, men st�djer m�nga olika spr�k. Var god l�gg m�rke till att alla tre st�djer olika spr�k, s� v�lj den som passar dig b�st." 15 | callback_error: Bara personen som startade kommandot kan v�lja inst�llningar 16 | engine_success: "\U0001F44D *Voicy* anv�nder nu *${engine}* i den h�r chatten. Tack! Gl�m inte att st�lla in /language." 17 | language: "\U0001F44B Var god v�lj spr�k f�r r�stigenk�nning f�r ${engine}" 18 | language_without_engine: "\U0001F44B Var god v�lj spr�k f�r r�stigenk�nningen" 19 | language_success: "\U0001F44D *Voicy* talar nu f�r *${language}* (${engine}) i den h�r chatten. Tack!" 20 | error_twenty: "_\U0001F46E Jag kan inte k�nna igen r�stmeddelanden som �r st�rre �n 20 megabytes_" 21 | initiated: "_\U0001F984 R�stigenk�nning �r initierad..._" 22 | speak_clearly: "_\U0001F46E Var god prata tydligt, jag kunde inte k�nna igen vad du sa_" 23 | error: "_\U0001F46E Jag kunde inte k�nna igen det_" 24 | google_error_creds: "\U0001F62E Var god, st�ll in google autentiseringsuppgifter med /google command eller byt motor med /engine command. Dina autentiseringsuppgifter �r inte inst�llda �n." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /locales/tr.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Harika! *Voicy* bu sohbete şimdiye kadar gönderilmiş olan tüm ses dosyalarını *görmezden gelecek.*" 2 | files_true: "\U0001F4C1 Harika! *Voicy* bu sohbete şimdiye kadar gönderilmiş olan tüm ses dosyalarını *tanımaya çalışacak.*" 3 | google: 'Google Speech ses tanıma özelliğini kurmak için, bu mesajı Google Cloud kimlik bilgileri dosyası (.json) ile yanıtlayın. Bu dosyanın ne olduğu ve bu dosyayı nasıl bulabileceğinizı bilmiyor musunuz? [Öğretici videomuza](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8) sayfasından erişebilirsiniz.' 4 | google_error_doc: 'Üzgünüm, kimlik bilgilerini içeren bir belge ile yanıtlamanız gerekiyor.' 5 | google_error_mime: "Üzgünüm, belgenin mime türü 'text/plain' olmalıdır." 6 | google_success: 'Tebrikler! Voicy, *${projectId}* kimliğine sahip Google Cloud Projesi için kimlik bilgilerini içeren belgeyi aldı. Artık Google Speech ses tanıma özelliğini kullanabilirsiniz.' 7 | help: "\U0001F60E *Voicy*, aldığı tüm sesli mesajlardaki ve ses dosyalarındaki (.ogg, .flac, .wav, .mp3) konuşmaları metne dönüştürebilir. *Voicy* ile özel sohbette konuşabilir ya da bir gruba ekleyebilirsiniz.\n\nBu botu özel mesajlarda kullanmak istiyorsanız, lütfen birisiyle özel bir grup oluşturun ve bu gruba *Voicy*'i ekleyin. Bir grup sohbetine *Voicy*'i eklemek istiyorsanız, lütfen onu grup profiline bir katılımcı olarak ekleyebilir ya da *Voicy* bot profilinden grubu dahil edebilirsiniz.\n\n/help — Bu iletiyi görüntüler \U0001F631\n/engine — Bir ses tanıma motoru seçmenize izin verir: wit.ai ya da Google Speech ⚙\n/language — Bir ses tanıma dili seçmenize izin verir \U0001F4E3\n/lock — Grup sohbetlerinde komut kullanarak yönetici olmayanların kilidini açın veya kapatın \U0001F511\n/files — Botun ses dosyalarını dönüştürmeye mi çalışmalı yoksa onları görmezden mi gelmeli, istediğinizi seçin \U0001F4C1\n/silent — `Ses tanıma etkinleştirildi` gibi ek mesajlar gönderilmediğinde sessiz moda geçer \U0001F636\n/google — Google Speech için Google kimlik bilgilerini ayarlayın \U0001F986\n/geeky — Çoğunlukla ileri düzey kullanıcılar tarafından kullanılan diğer ekstra özellikler (Google kimlik bilgilerini paylaşmak veya 20mb Telegram sınırını atlamak gibi) \U0001F47B\n/privacy — Gizlilik politikası\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Üzgünüm fakat bu komut yalnızca grup sohbetlerinde çalışmaktadır." 9 | lock_true: "\U0001F511 Mükemmel! *Voicy* bu sohbette yalnızca *yöneticiler*in gönderdiği komutlara yanıt verecek." 10 | lock_false: "\U0001F511 Mükemmel! *Voicy* bu sohbetteki *herkesin* gönderdiği komutlara yanıt verecek." 11 | silent_true: "\U0001F636 Muhteşem! *Voicy* artık *sessiz modda* çalışacak: sohbete ses deşifreleri haricinde hiçbir mesaj göndermeyecek." 12 | silent_false: "\U0001F60F Muhteşem! *Voicy* artık *normal modda* çalışacak: sesli mesajlar aldıktan hemen sonra `Ses tanıma etkin` mesajılarını gönderir." 13 | start: "\U0001F44B Hey sen! *Voicy*, tüm sesli mesajları ve ses dosyalarını (.ogg, .flac, .wav, .mp3) metne dönüştüren bir ses tanıma botudur.\n\n*Voicy* iki ses tanıma motorunu destekler: wit.ai ve Google Speech. Wit.ai kullanmaya ayarlıdır fakat /engine komutu ile istediğiniz zaman ses tanıma motorunu Google Speech olarak değiştirebilirsiniz. Daha fazla bilgi için /help.\n" 14 | engine: "\U0001F44B Lütfen, ses tanıma motorunu seçin. Google Speech doğruluğu daha yüksek olan motordur ancak Google Cloud kimlik bilgilerinizle (biraz uğraştırıcı) ayarlanması gerekmektedir. Wit.ai doğruluğu daha az olan motordur fakat ücretsizdir. Lütfen, her ikisinin de farklı dilleri desteklediğini unutmayınız bu yuzden size en uygun olanı seçiniz." 15 | callback_error: Yalnızca komutu veren kişi seçenekleri seçebilir 16 | engine_success: "\U0001F44D Artık *Voicy* bu sohbette *${engine}* kullanacaktır. Teşekkürler! Dili ayarlamayı unutmayınız: /language." 17 | language: "\U0001F44B Lütfen ${engine} için ses tanıma dilini seçin" 18 | language_without_engine: "\U0001F44B Lütfen ses tanıma dilini seçin" 19 | language_success: "\U0001F44D Artık *Voicy* bu sohbette *${language}* (${engine}) konuşuyor. Teşekkürler!" 20 | error_twenty: "_\U0001F46E 20 megabayttan daha büyük sesli mesajları tanıyamıyorum_" 21 | initiated: "_\U0001F984 Ses tanıma etkinleştirildi..._" 22 | speak_clearly: "_\U0001F46E Lütfen, daha anlaşılır biçimde konuşun, bunu anlayamadım_" 23 | error: "_\U0001F46E Bunu anlayamadım_" 24 | google_error_creds: "\U0001F62E Lütfen, /google komutuyla Google kimlik bilgilerini ayarlayın veya motoru /engine komutuyla değiştirin. Kimlik bilgileriniz henüz ayarlanmamış." 25 | ashmanov_language: Nanosemantics yalnızca Rusça'yı desteklemektedir. Lütfen /language değiştirmeden önce /engine değiştirin.' 26 | geeky: | 27 | Karanlık tarafa hoş geldiniz. Rehberiniz benim. Lütfen, aşağıdaki özellikleri sorumlu bir şekilde kullanın (ya da yararlanmayın, karar sizin). Bilgisayar kurdu işlevleriyle alakalı hakkında herhangi bir sorunuz varsa, @borodutch\_support adresini boş verin ve direkt @borodutch. Görüş ve önerilerinizi önemsiyorum, değerli yoldaşlarım! 28 | 29 | /enableGoogle — Kişisel Google kimlik bilgilerinizi başka kimseye göstermeden bu sohbetle paylaşın 30 | /disableGoogle — Kişisel Google kimlik bilgilerinizi başka kimseye göstermeden bu sohbetten kaldırın 31 | /timecodes — Transkriptlerdeki zaman kodlarını değiştirir (*Voicy* deşifre edilen sesin her bir parçasının zaman kodlarını belirlerse) 32 | /l — /language ile aynıdır 33 | `/l en` — dili değiştirmek için kısayol, farklı dil kodlarıyla deneyin 34 | google_enable_personal_not_setup: 'Kişisel Google Cloud kimlik bilgileriniz henüz ayarlanmamış gibi gözüküyor. Lütfen, bu sohbetten Google anahtarınızı etkinleştirmeyi denemeden önce @voicybot botunda bu işlemi tamamlayın.' 35 | google_enable_success: Harika. Google Cloud kimlik bilgileriniz artık bu sohbette kullanılabilir. 36 | google_disable_personal_not_setup: 'Kişisel Google Cloud kimlik bilgileriniz henüz ayarlanmamış gibi gözüküyor. Lütfen, bu sohbetten Google anahtarınızı devre dışı bırakmayı denemeden önce @voicybot bu işlemi tamamlayın.' 37 | google_disable_success: Harika. Artık Google Cloud kimlik bilgileriniz bu sohbette kullanılmayacaktır. 38 | oops: 'Hay aksi! Bu özelliğe henüz sahip değilim. Fakat size söz veriyorum: Bu konudaki talebinizi aldım ve bu özelliğe ihtiyacınız olduğunu hatırlayacağım. Teşekkürler!' 39 | timecodes_true: 'Güzel! Şimdi *Voicy*, tanınan metne zaman kodları ekleyecek.' 40 | timecodes_false: 'Güzel! Şimdi *Voicy*, tanınan metne zaman kodları eklemeyecek.' 41 | url: 'Dosya başına 20 MB olan Telegram limitine takılmak istemiyorsanız, doğrudan voicybot.com adresini kullanabilirsiniz. Teşekkürler!' 42 | -------------------------------------------------------------------------------- /locales/ua.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Чудово! Відтепер *Войсі* буде *ігнорувати* всі аудіофайли в цьому чаті." 2 | files_true: "\U0001F4C1 Чудово! Відтепер *Войсі* буде *намагатися перетворити в текст* усі аудіофайли в цьому чаті." 3 | google: 'Відповіддю на це повідомлення повинен бути файл з обліковими даними Google Cloud (.json), що дозволить застосовувати розпізнавання мовлення Google Speech. Не впевнені, як зробити так, щоб все працювало? Перегляньте [наш короткий довідник](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8).' 4 | google_error_doc: 'Будь ласка, дайте відповідь у вигляді файлу з обліковими даними.' 5 | google_error_mime: "Будь ласка, переконайтеся, що файл відповідає типу 'text/plain'." 6 | google_success: 'Вітаємо! *Войсі* отримав файл з обліковими даними для проекту *${projectId}* в Google Cloud. Тепер ви можете використовувати розпізнавання мовлення Google Speech.' 7 | help: "\U0001F60E *Войсі* перетворює в текст усі отримані голосові повідомлення та аудіофайли (.ogg, .flac, .wav, .mp3). Ви можете використовувати *Войсі* в приватних або групових чатах.\n\nЯкщо ви хочете використовувати цей бот у приватних повідомленнях, створіть приватну групу зі співрозмовником і додайте туди *Войсі*. Якщо ж ви хочете додати *Войсі* до групового чату, тоді додайте його як учасника в профілі групи або в профілі бота *Войсі*.\n\n/help — Демонструє це повідомлення \U0001F631\n/engine — Дозволяє обрати інструмент для розпізнавання мовлення: wit.ai або Google Speech ⚙\n/language — Дозволяє обрати мову для розпізнавання мовлення \U0001F4E3\n/lock — Вмикає і вимикає обробку команд, які надсилаються не адміністраторами групових чатів \U0001F511\n/files — Вмикає і вимикає перетворення аудіофайлів в текст \U0001F4C1\n/silent — Вмикає і вимикає режим тиші, в якому не надсилаються зайві повідомлення на кшталт `Ініційовано розпізнавання мовлення` \U0001F636\n/google — Налаштування облікових даних Google для використання Google Speech \U0001F986\n/geeky — Ще більше функцій для досвідчених користувачів (на кшталт спільного використання облікових даних Google та можливостей для обходу обмежень Telegram на обсяг файлів більше 20 МБ) \U0001F47B\n/privacy — Політика конфіденційності\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nВи хочете підтримати автора бота? Я написав наукову книгу про те, як жити здоровіше та щасливіше! Ви можете купити її на Амазоні — amazon.com/dp/B0CHL7WRYM або на сайті книги — wdlaty.com. Дякую!" 8 | lock_true: "\U0001F511 Чудово! Відтепер *Войсі* реагуватиме лише на команди, які надіслані від *адміністраторів* цього чату." 9 | lock_false: "\U0001F511 Чудово! Відтепер *Войсі* реагуватиме на команди, які надіслані від *усіх* у цьому чаті." 10 | silent_true: "\U0001F636 Чудово! Відтепер *Войсі* працюватиме в *режимі тиші* та не надсилатиме в чат нічого, окрім перетвореного в текст мовлення." 11 | silent_false: "\U0001F60F Чудово! Відтепер *Войсі* працюватиме в *звичайному режимі* та надсилатиме повідомлення `Ініційовано розпізнавання мовлення` відразу після отримання голосових повідомлень." 12 | start: "\U0001F44B Привіт! *Войсі* — це бот, який перетворює в текст усі отримані голосові повідомлення та аудіофайли (.ogg, .flac, .wav, .mp3).\n\n*Войсі* підтримує два інструменти для розпізнавання мовлення: wit.ai та Google Speech. За замовчуванням використовується wit.ai, проте ви завжди можете перейти на Google Speech за допомогою команди /engine. Більше інформації в /help.\n" 13 | engine: "\U0001F44B Будь ласка, оберіть інструмент для розпізнавання мовлення. Google Speech більш точний, однак вимагає налаштування з використанням ваших облікових даних Google Cloud (що не зовсім просто). Wit.ai менш точний, але безкоштовний. Варто зазначити, що обидва інструменти підтримують різні мови, тож обирайте той, який найкраще підходить вам." 14 | callback_error: Лише ініціатор команди може вибирати параметри 15 | engine_success: "\U0001F44D Відтепер у цьому чаті *Войсі* використовуватиме *${engine}*. Дякуємо! Не забудьте обрати мову за допомогою команди /language." 16 | language: "\U0001F44B Будь ласка, оберіть мову для розпізнавання мовлення за допомогою команди ${engine}" 17 | language_without_engine: "\U0001F44B Будь ласка, оберіть мову для розпізнавання мовлення" 18 | language_success: "\U0001F44D Відтепер у цьому чаті *Войсі* використовуватиме *${language}* (${engine}). Дякуємо!" 19 | error_twenty: "_\U0001F46E Я не вмію перетворювати в текст голосові повідомлення обсягом більше 20 мегабайт_" 20 | initiated: "_\U0001F984 Ініційовано розпізнавання мовлення..._" 21 | speak_clearly: "_\U0001F46E Будь ласка, говоріть чіткіше, мені не вдалося це розпізнати_" 22 | error: "_\U0001F46E Мені не вдалося це розпізнати_" 23 | google_error_creds: "\U0001F62E Будь ласка, застосуйте облікові дані Google за допомогою команди /google або змініть інструмент для розпізнавання мовлення за допомогою команди /engine. Облікові дані вами ще не застосовувалися." 24 | ashmanov_language: 'Nanosemantics підтримує лише російську мову. Будь ласка, змініть інструмент за допомогою команди /engine, перш ніж змінювати мову за допомогою команди /language.' 25 | geeky: | 26 | Вітаємо на темній стороні, я буду вашим поводирем. Будь ласка, користуйтеся наступними функціями з обережністю (або без, вирішувати лише вам). Якщо у вас виникнуть якісь запитання щодо функцій для ґіків, звертайтесь напряму до @borodutch. Мені надзвичайно важливі думки і зауваження досвідчених користувачів! 27 | 28 | /enableGoogle — Застосувати свої облікові дані Google в цьому чаті без їх відображення 29 | /disableGoogle — Видалити свої облікові дані Google з цього чату без їх відображення 30 | /timecodes — Увімкнути або вимкнути таймкоди в тексті (коли *Войсі* створює таймкоди для аудіофайлу, перетвореного у текст частинами) 31 | /l — те саме, що й /language 32 | `/l en` — швидкий спосіб змінити мову шляхом введення кодів різних мов 33 | google_enable_personal_not_setup: 'Схоже на те, що особисті облікові дані Google Cloud ще не вводилися. Будь ласка, виконайте це у @voicybot, перш ніж застосовувати свої облікові дані в цьому чаті.' 34 | google_enable_success: Чудово! Відтепер ваші облікові дані Google Cloud використовуватимуться в цьому чаті. 35 | google_disable_personal_not_setup: 'Схоже на те, що особисті облікові дані Google Cloud ще не вводилися. Будь ласка, виконайте це у @voicybot, перш ніж видаляти свої облікові дані з цього чату.' 36 | google_disable_success: Чудово! Ваші облікові дані Google Cloud більше не використовуватимуться в цьому чаті. 37 | oops: 'Упс! Така функція ще не реалізована. Однак я неодмінно отримаю сповіщення про те, що вам вона потрібна. Дякую!' 38 | timecodes_true: Чудово! Відтепер *Войсі* додаватиме таймкоди до розпізнаного тексту. 39 | timecodes_false: Чудово! Відтепер *Войсі* не додаватиме таймкоди до розпізнаного тексту. 40 | url: 'Якщо вам потрібно обійти обмеження Telegram на обсяг файлів більше 20 МБ, ви можете скористатися безпосередньо voicybot.com. Успіхів!' 41 | -------------------------------------------------------------------------------- /locales/ur.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 بہت خوب! * وائسی * اب سے اس چیٹ میں موجود تمام آڈیو فائلوں کو * نظر انداز *کردے گا۔" 2 | files_true: "\U0001F4C1 بہت خوب! *وائسی* اس گفتگو میں اب سے تمام آڈیو فائلوں کو *پہچاننے کی کوشش* کرے گا۔" 3 | google: 'گوگل اسپیچ صوتی شناخت کو ترتیب دینے کے لئے اس پیغام کا جواب گوگل کلاؤڈ کی اسناد فائل (.json) سے دیں۔ علم نہیں کہ یہ کیا ہے اور اسے کیسے حاصل کیا جائے؟ [ہمارے فوری ٹیوٹوریل](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8) چیک کریں۔' 4 | google_error_doc: معذرت، آپ کو اسناد کی دستاویز کے ساتھ جواب دینا ضروری ہے۔ 5 | google_error_mime: معذرت، دستاویز کی mime قسم 'text/plain' ہونی چاہئے۔ 6 | google_success: 'مبارک ہو! *ووائسی* کو *${projectId}* گوگل کلاؤڈ پروجیکٹ کی اسناد کی فائل مل گئی۔ اب آپ گوگل اسپیچ کی شناخت کو استعمال کرنے کے قابل ہیں۔' 7 | help: "\U0001F60E *وائسی*بیان کے صوتی پیغامات اور آڈیو فائلوں (.ogg، .flac، .wav، .mp3) کو متن میں تبدیل کرتا ہے جو اسے موصول ہوتا ہے۔ آپ یا تو نجی چیٹ میں *وائسی* سے بات کرسکتے ہیں یا اسے کسی گروپ میں شامل کرسکتے ہیں۔\nاگر آپ اس بوٹ کو نجی پیغامات میں استعمال کرنا چاہتے ہیں تو، براہ کرم، کسی کے ساتھ نجی گروپ بنائیں اور وہاں *وائسی* کو شامل کریں۔ اگر آپ کسی گروپ چیٹ میں *وائسی* کو شامل کرنا چاہتے ہیں تو، براہ کرم، اسے گروپ پروفائل میں شریک کے طور پر یا *وائسی* بوٹ پروفائل سے گروپ میں شامل کریں۔\n\n/help — اس پیغام کو دکھائے گا۔ \U0001F631\n/engine — آپ کو صوتی شناخت کا انجن چننے دیتا ہے: wit.ai یا گوگل اسپیچ ⚙\n/language — آپ کو آواز کی شناخت کی زبان منتخب کرنے دیتا ہے \U0001F4E3\n/lock — گروپ چیٹس میں غیر منتظمین کے لیے وائسی کے کمانڈس استعمال کرنے کو مقفل یا آزاد میں تبدیل کرتا ہے \U0001F511\n/files — تبدیل کرتا ہے کہ، آیا بوٹ آڈیو فائلوں کو تبدیل کرنے کی کوشش کرے یا انہیں نظر انداز کردے \U0001F4C1\n/silent — خاموش وضع میں تبدیل کرتا ہے۔ مثلاً `آواز کی شناخت شروع کی گئی ہے` جیسے اضافی پیغامات نہیں بھیجے جاتے۔ \U0001F636\n/google — گوگل اسپیچ کے لئے گوگل کی اسناد مرتب کرنے کے لیے \U0001F986\n/geeky — باقی اضافی کمانڈس زیادہ تر اعلی ٰدرجے کے صارفین استعمال کرتے ہیں (جیسے گوگل اسناد کا اشتراک کرنا یا 20 ایم بی ٹیلی گرام حد کو نظرانداز کرنا) \U0001F47B\n/privacy — رازداری کی پالیسی\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 معذرت، یہ کمانڈ صرف گروپ چیٹس میں کام کرتا ہے۔" 9 | lock_true: "\U0001F511 بہتر! *وائسی* اب اس چیٹ میں صرف *منتظمین* کے ذریعہ ارسال کردہ کمانڈس کا جواب دے گا۔" 10 | lock_false: "\U0001F511 بہتر! *وائسی* اب اس چیٹ میں *ہر کسی* کی کمانڈس کا جواب دے گا۔" 11 | silent_true: "\U0001F636 شاندار! *وائسی* اب *خاموش وضع* میں کام کرے گا: یہ چیٹ کو حقیقی آواز کا متن کے علاوہ کوئی پیغام نہیں بھیجے گا۔" 12 | silent_false: "\U0001F60F شاندار! *وائسی* اب *عام وضع میں* کام کرے گا: یہ صوتی پیغامات موصول ہونے کے فوراً بعد ہی `آواز کی شناخت شروع کی گئی ہے` پیغامات بھیجے گا۔" 13 | start: "\U0001F44B تسلیمات! *وائسی* آواز کی شناخت والا ایک بوٹ ہے جو تمام صوتی پیغامات اور آڈیو فائلوں (.ogg, .flac, .wav, .mp3) کو متن میں تبدیل کرتا ہے۔\n*وائسی* آواز کو پہچاننے والی دوانجنوں کی حمایت کرتا ہے: wit.ai اور گوگل اسپیچ۔ ابتدائی طور پر یہ wit.ai استعمال کرتا ہے لیکن آپ کسی بھی وقت /engine کے ذریعہ گوگل اسپیچ میں تبدیل کرسکتے ہیں۔ مزید معلومات /help میں\n" 14 | engine: "\U0001F44B براہ کرم، آواز کی شناخت کے انجن کو منتخب کریں۔ گوگل اسپیچ زیادہ درست ہے، لیکن آپ کو اپنے گوگل کلاؤڈ کی سند (تھوڑا پیچیدہ عمل) کے ساتھ مرتب کرنا ہے۔ wit.ai کم درست ہے، لیکن مفت ہے۔ براہ کرم، نوٹ کریں کہ یہ دونوں مختلف زبانوں کی تائید کرتے ہیں، لہذا ایسی زبان کا انتخاب کریں جو آپ کو بہترین لگے۔" 15 | callback_error: صرف وہی شخص اختیارات منتخب کرسکتا ہے جس نے مجھے کمانڈ بھیجا ہے 16 | engine_success: "\U0001F44D اب *وائسی* اس چیٹ میں *${engine}* کا استعمال کررہا ہے۔ آپ کا شکریہ! /language کمانڈ کے ذریعہ زبان طے کرنا نہ بھولیں۔" 17 | language: "\U0001F44B براہ کرم ${engine} کیلئے آواز کی شناخت کی زبان منتخب کریں" 18 | language_without_engine: "\U0001F44B برائے مہربانی آواز کی شناخت کی زبان منتخب کریں" 19 | language_success: "\U0001F44D اب اس چیٹ میں *وائسی* *${language}* (${engine}) میں بات کرے گا۔ آپ کا شکریہ!" 20 | error_twenty: "_\U0001F46E میں 20 ایم بی سے زیادہ حجم کے صوتی پیغامات کو نہیں پہچان سکتا ہوں_" 21 | initiated: "_\U0001F984 آواز کی شناخت شروع کی گئی ہے..._" 22 | speak_clearly: "_\U0001F46E براہ کرم، صاف طور سے بات کریں، میں اسے پہچان نہیں سکا_" 23 | error: "_\U0001F46E میں اسے پہچان نہیں سکا_" 24 | google_error_creds: "\U0001F62E براہ کرم ، /google کمانڈ کے ساتھ گوگل کی اسناد مرتب کریں یا انجن کو /engine کمانڈ سے تبدیل کریں۔ آپ کی اسناد ابھی ترتیب نہیں دی گئی ہیں۔" 25 | ashmanov_language: ۔Nanosemantics صرف روسی زبان کی حمایت کرتا ہے۔ براہ کرم، /engine کو /language تبدیل کرنے سے پہلے تبدیل کریں 26 | geeky: | 27 | تاریک پہلو میں آپ کا استقبال ہے، میں آپ کا رہبر ہوں گا۔ براہ کرم، ذمہ داری کے ساتھ مندرجہ ذیل افعال سے لطف اٹھائیں (یا نہیں، آپ فیصلہ کریں)۔ اگرگیکی اعمال سے متعلق آپ کے سوالات ہوں تو — @borodutch\_support کو نظر انداز کریں اور براہ راست درخواستیں @borodutch کو بھیجیں۔ میں آپ کی رائے اور مشوروں کی قدر کرتا ہوں، میرے قابل قدر صارفین! 28 | /enableGoogle — اپنی ذاتی گوگل سندوں کو، انہیں ظاہر کیے بغیر اس چیٹ کے ساتھ اشتراک کریں 29 | /disableGoogle — اس چیٹ سے اپنی ذاتی گوگل اسناد کو ظاہر کیے بغیر ہٹائیں 30 | /timecodes — متن میں ٹائم کوڈ تبدیل کرتا ہے (جب *وائسی* نقل شدہ آڈیو کے ہر ٹکڑے کے لئے ٹائم کوڈز کی وضاحت کرتا ہے) 31 | /l — /language کمانڈ ہی کی طرح 32 | `/l en` — زبان کو تبدیل کرنے کے لئے شارٹ کٹ، مختلف زبان کے کوڈز کے ساتھ استعمال کریں 33 | google_enable_personal_not_setup: ایسا لگتا ہے کہ آپ کے ذاتی گوگل کلاؤڈ کی اسناد ابھی ترتیب نہیں دی گئی ہیں۔ براہ کرم، اس چیٹ میں اپنی گوگل کنجی کو فعال کرنے کی کوشش سے پہلے @voicybot میں ایسا کریں۔ 34 | google_enable_success: بہت عمدہ۔ آپ کے گوگل کلاؤڈ کی اسناد اب اس چیٹ میں استعمال ہوں گی۔ 35 | google_disable_personal_not_setup: ایسا لگتا ہے کہ آپ کے ذاتی گوگل کلاؤڈ کی اسناد ابھی ترتیب نہیں دی گئی ہیں۔ براہ کرم ، اس چیٹ میں اپنی گوگل کنجی کو غیر فعال کرنے کی کوشش سے پہلے @voicybot میں ایسا کریں۔ 36 | google_disable_success: بہتر ہے۔ آپ کے گوگل کلاؤڈ کی اسناد اس چیٹ میں اب استعمال نہیں ہوں گی۔ 37 | oops: 'افففف! میں نے ابھی تک اس خصوصیت کو نافذ نہیں کیا ہے۔ لیکن یقین دلاؤں: مجھے اس کے لیے آپ کی درخواست موصول ہوگئی ہے اور پتہ چل جائے گا کہ آپ کو اس خصوصیت کی ضرورت ہے۔ آپ کا شکریہ!' 38 | timecodes_true: بہتر! اب *وائسی* عبارت میں ٹائم کوڈز کا اضافہ کرے گا۔ 39 | timecodes_false: بہتر! اب *وائسی* عبارت میں ٹائم کوڈ شامل نہیں کرے گا۔ 40 | url: اگرآپ 20 ایم بی فی فائل کی ٹیلی گرام حد کو نظرانداز کرنا چاہتے ہیں تو، آپ voicybot.com کو براہ راست استعمال کرسکتے ہیں۔ مزہ لیں! 41 | -------------------------------------------------------------------------------- /locales/uz.yaml: -------------------------------------------------------------------------------- 1 | files_false: "\U0001F4C1 Ajoyib! *Voicy* bu chatdagi barcha audio fayllarni hozirdan boshlab *e�tiborsiz qoldiradi*." 2 | files_true: "\U0001F4C1 Ajoyib! *Voicy* bu chatdagi barcha audio fayllarni hozirdan boshlab *tushunishga harakat qiladi*." 3 | google: 'Google nutqni tushunish xizmatini sozlash uchun bu xabarga Google Cloud xizmatidagi shaxsiy ma�lumotlaringiz fayli (.json) bilan javob qaytaring. Buni qanday amalga oshirishni bilmasangiz, [qo�llanmamiz](https://medium.com/@nikitakolmogorov/setting-up-google-speech-for-voicybot-b806545750f8) bilan tanishing.' 4 | google_error_doc: 'Uzr, shaxsiy ma�lumotlaringiz bor hujjat bilan javob qaytaring.' 5 | google_error_mime: "Uzr, hujjatning mime turi 'text/plain' kabi bo�lishi lozim." 6 | google_success: 'Tabriklaymiz! *Voicy* *${projectId}* Google Cloud loyihasi uchun shaxsiy ma�lumotlar faylini oldi. Endi siz Google ovozni tushunish xizmatidan foydalishingiz mumkin.' 7 | help: "\U0001F60E *Voicy* qabul qilingan har qanday audio fayllar (.ogg, .flac, .wav, .mp3) va ovozli fayllarni matnga aylantiradi. *Voicy*ga maxfiy chatda gapirishingiz yoki uni guruhga qo�shib ham foydalanishingiz mumkin.\n\nBu botdan shaxsiy xabarlar uchun foydalanmoqchi bo�lsangiz, suhbatdoshingiz bilan alohida maxfiy guruh yarating va *Voicy*ni ham shu yerga qo�shing. *Voicy*ni guruh chatiga qo�shmoqchi bo�lsangiz, uni guruh profili orqali ishtirokchi sifatida qo�shing yoki *Voicy* bot profilidagi guruhga qo�shishingiz mumkin.\n\n/help — Bu xabarni ko�rsatadi \U0001F631\n/engine — Ovozni tushunish tizimini tanlang: wit.ai, Yandex SpeechKit yoki Google Speech ⚙\n/language — Ovozni tushunish tilini tanlang \U0001F4E3\n/lock — Administrator bo�lmaganlar uchun guruh chatlarida buyruqlardan foydalanishga ruxsat beradi yoki taqiqlaydi \U0001F511\n/files — Bot audio fayllarga aylantirishga uringanda yoki rad qilganda yonadi/o�chadi \U0001F4C1\n/silent — \"Ovozni tushunish ishga tushdi\" kabi qo�shimcha xabarlar yuborilmasa, sokin rejimga o�tadi \U0001F636\n/google — Google Speech uchun Google shaxsiy ma�lumotlarini sozlash \U0001F986\n/witToken — Setting your own Wit token in the format `/witToken 123456789`\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!" 8 | error_group: "\U0001F605 Uzr, bu buyruq faqatgina guruh chatlarida ishlaydi." 9 | lock_true: "\U0001F511 Zo�r! *Voicy* bu chatda faqat *admins* (administratorlar) yuborgan buyruq chaqiruvlariga javob beradi." 10 | lock_false: "\U0001F511 Zo�r! *Voicy* bu chatda faqat *anyone* (har qanday inson) yuborgan buyruq chaqiruvlariga javob beradi." 11 | silent_true: "\U0001F636 Qoyil! *Voicy* endi *silent mode* (sokin rejim)da ham ishlaydi: u endi chatga amaldagi ovoz transkripsiyasidan boshqa hech qanday xabar yubormaydi." 12 | silent_false: "\U0001F60F Qoyil! *Voicy* endi *usual mode* (odatdagi rejim)da ishlaydi: u audio xabarlarni olganidan keyin `Ovozni tushunish ishga tushdi` xabarini yuboradi." 13 | start: "\U0001F44B Salom! *Voicy* � ovozni tushunish boti bo�lib, barcha ovozli xabar va audio fayllarni (.ogg, .flac, .wav, .mp3) matnga aylantiradi.\n\n*Voicy* uchta ovozni tushunish tizimi bilan ishlaydi: wit.ai, Yandex SpeechKit and Google Speech. U odatda wit.ai tizimidan foydalanadi, lekin Google Speech yoki Yandex SpeechKit tizimiga /engine orqali almashtirishingiz mumkin. Batafasil ma�lumotni /help orqali olish mumkin.\n" 14 | engine: "\U0001F44B Ovozni tushunish tizimini tanlang. Google Speech ancha yaxshi ishlaydi va 50 soniyadan uzun audio fayllarni ham o�qiy oladi, lekin Google Cloud shaxsiy ma�lumotlari bilan sozlash kerak bo�ladi (biroz vaqt oladi). Yandex SpeechKit ham yaxshi ishlaydi, bepul, xavfsiz va odatda 50 soniyadan uzun audio fayllarni ham o�qiy oladi, lekin ba�zi tillardagina ishlaydi. Wit.ai is juda yaxshi ishlamasa-da, bepul, 50 soniyadan uzun audio fayllarni o�qiy olmaydi, lekin juda ko�p tilda ishlaydi. Uchalasi har xil tilda ishlaydi. O�zingizga mosini tanlang!" 15 | callback_error: Buyruqni boshlagan odamgina parametrlarni tanlay oladi 16 | engine_success: "\U0001F44D Endi *Voicy* *${engine}*dan bu chatda foydalana oladi. Rahmat! Tilni /language orqali sozlashni unutmang." 17 | language: "\U0001F44B ${engine} uchun ovozni tushunish tilini tanlang" 18 | language_without_engine: "\U0001F44B Ovozni tushunish tilini tanlang" 19 | language_success: "\U0001F44D Endi *Voicy* bu chatda *${language}* (${engine}) tilida gaplasha oladi. Rahmat!" 20 | error_twenty: "_\U0001F46E Men 20 megabaytdan katta ovozli xabarlarni o�qiy olmayman_" 21 | initiated: "_\U0001F984 Ovozni tanih ishga tushdi..._" 22 | speak_clearly: "_\U0001F46E Iltimos, Aniq va tiniq o�qing, Men tushuna olmayapman_" 23 | error: "_\U0001F46E Men tushuna olmayapman_" 24 | google_error_creds: "\U0001F62E Google shaxsiy ma�lumotlrini /google command yordamida sozlang yoki /engine command yordamida tizimni almashtiring. Shaxsiy ma�lumotlaringiz haligacha sozlanmagan." 25 | ashmanov_language: 'Nanosemantics only supports Russian. Please, change /engine to change /language first.' 26 | -------------------------------------------------------------------------------- /nixpacks.toml: -------------------------------------------------------------------------------- 1 | [phases.setup] 2 | nixPkgs = ["...", "ffmpeg_6-full"] 3 | 4 | [phases.install] 5 | cmds = ["yarn install --frozen-lockfile"] 6 | 7 | [phases.build] 8 | cmds = ["yarn build-ts"] 9 | 10 | [start] 11 | cmd = "yarn distribute" 12 | 13 | [env] 14 | NODE_ENV = "production" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voicy", 3 | "version": "1.0.0", 4 | "description": "Text to speech Telegram bot", 5 | "main": "dist/app.js", 6 | "private": false, 7 | "license": "MIT", 8 | "author": "backmeupplz ", 9 | "scripts": { 10 | "distribute": "node dist/app.js", 11 | "develop": "concurrently -k -i -p \"[{name}]\" -n \"Node,TypeScript\" -c \"yellow.bold,cyan.bold\" \"yarn watch-js\" \"yarn watch-ts\"", 12 | "build-ts": "tsc --skipLibCheck", 13 | "watch-ts": "tsc -w --skipLibCheck", 14 | "watch-js": "nodemon dist/app.js", 15 | "pretty": "prettier --check src", 16 | "lint": "yarn pretty && eslint --max-warnings 0 --ext ts,tsx,json src" 17 | }, 18 | "dependencies": { 19 | "@google-cloud/speech": "^4.9.0", 20 | "@google-cloud/storage": "^5.15.3", 21 | "@grammyjs/i18n": "^0.3.0", 22 | "@grammyjs/runner": "^1.0.2", 23 | "@typegoose/typegoose": "^9.2.0", 24 | "axios": "^0.23.0", 25 | "crypto-js": "^4.1.1", 26 | "download": "^8.0.0", 27 | "express": "^4.18.2", 28 | "fluent-ffmpeg": "^2.1.2", 29 | "form-data": "^4.0.0", 30 | "grammy": "^1.3.4", 31 | "js-yaml": "^4.1.0", 32 | "lodash": "^4.17.21", 33 | "mongoose": "6.0.12", 34 | "mongoose-findorcreate": "^3.0.0", 35 | "stripe": "^10.15.0", 36 | "temp": "^0.9.4", 37 | "uuid": "^8.3.2" 38 | }, 39 | "devDependencies": { 40 | "@types/node": "^16.11.4", 41 | "@typescript-eslint/eslint-plugin": "^5.0.0", 42 | "@typescript-eslint/parser": "^5.0.0", 43 | "concurrently": "^6.3.0", 44 | "dotenv": "^10.0.0", 45 | "eslint": "^7.32.0", 46 | "eslint-config-prettier": "^8.3.0", 47 | "eslint-plugin-prettier": "^4.0.0", 48 | "eslint-plugin-sort-imports-es6-autofix": "^0.6.0", 49 | "module-alias": "^2.2.2", 50 | "nodemon": "^2.0.14", 51 | "prettier": "^2.4.1", 52 | "typescript": "^4.4.4" 53 | }, 54 | "_moduleAliases": { 55 | "@": "dist" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /scripts/clean-voicy-localizations.js: -------------------------------------------------------------------------------- 1 | db.getCollection('localizations').find({tags:'voicybot'}).forEach(l => { 2 | const needsFixing = !!l.variants.find(v => !v.selected) 3 | if (needsFixing) { 4 | l.variants = l.variants.filter(v => v.selected) 5 | db.localizations.save(l) 6 | } 7 | }) -------------------------------------------------------------------------------- /scripts/download.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv') 2 | dotenv.config({ path: `${__dirname}/../.env` }) 3 | const axios = require('axios') 4 | const unflatten = require('flat').unflatten 5 | const fs = require('fs') 6 | const jsyaml = require('js-yaml') 7 | 8 | ;(async function getTranslations() { 9 | console.log('==== Getting localizations') 10 | const translations = ( 11 | await axios.get('https://localizer.borodutch.com/localizations') 12 | ).data.filter((l) => { 13 | return l.tags.indexOf('voicybot') > -1 14 | }) 15 | console.log('==== Got localizations:') 16 | console.log(JSON.stringify(translations, undefined, 2)) 17 | // Get flattened map 18 | const flattenedMap = {} // { key: {en: '', ru: ''}} 19 | translations.forEach((t) => { 20 | const key = t.key 21 | const variants = t.variants.filter((v) => !!v.selected) 22 | flattenedMap[key] = variants.reduce((p, c) => { 23 | p[c.language] = c.text 24 | return p 25 | }, {}) 26 | }) 27 | console.log('==== Decoded response:') 28 | console.log(flattenedMap) 29 | // Reverse the map 30 | const reversedMap = {} 31 | Object.keys(flattenedMap).forEach((k) => { 32 | const internals = flattenedMap[k] 33 | for (const language in internals) { 34 | const text = internals[language] 35 | if (!reversedMap[language]) { 36 | reversedMap[language] = {} 37 | } 38 | reversedMap[language][k] = text 39 | } 40 | }) 41 | const unflattened = unflatten(reversedMap) 42 | console.log('==== Reversed and unflattened map') 43 | console.log(unflattened) 44 | for (const language in unflattened) { 45 | const obj = unflattened[language] 46 | const yaml = jsyaml.safeDump(obj, { 47 | lineWidth: -1, 48 | noCompatMode: true, 49 | }) 50 | fs.writeFileSync(`${__dirname}/../locales/${language}.yaml`, yaml) 51 | } 52 | console.log('==== Saved object to the file') 53 | })() 54 | -------------------------------------------------------------------------------- /scripts/upload.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv') 2 | dotenv.config({ path: `${__dirname}/../.env` }) 3 | const axios = require('axios') 4 | const flatten = require('flat') 5 | const fs = require('fs') 6 | const jsyaml = require('js-yaml') 7 | 8 | const files = fs.readdirSync(`${__dirname}/../locales`) 9 | 10 | const localizations = {} 11 | 12 | for (const fileName of files) { 13 | localizations[fileName.split('.')[0]] = jsyaml.safeLoad( 14 | fs.readFileSync(`${__dirname}/../locales/${fileName}`, 'utf8') 15 | ) 16 | } 17 | 18 | const flattenedLocalizations = {} 19 | Object.keys(localizations).forEach((language) => { 20 | flattenedLocalizations[language] = flatten(localizations[language]) 21 | }) 22 | 23 | const result = {} 24 | 25 | Object.keys(flattenedLocalizations['en']).forEach((key) => { 26 | const keyObject = {} 27 | for (const language in flattenedLocalizations) { 28 | if (flattenedLocalizations[language][key]) { 29 | keyObject[language] = flattenedLocalizations[language][key] 30 | } 31 | } 32 | result[key] = keyObject 33 | }) 34 | ;(async function postLocalizations() { 35 | console.log('==== Posting body:') 36 | console.log(JSON.stringify(result, undefined, 2)) 37 | try { 38 | await axios.post(`https://localizer.borodutch.com/localizations`, { 39 | // await axios.post(`http://localhost:1337/localizations`, { 40 | localizations: result, 41 | password: process.env.PASSWORD, 42 | username: 'borodutch', 43 | tags: ['voicybot'], 44 | }) 45 | console.error(`==== Body posted!`) 46 | } catch (err) { 47 | console.error(`==== Error posting: ${err.message}`) 48 | } 49 | })() 50 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | // Setup @/ aliases for modules 3 | import 'module-alias/register' 4 | // Config dotenv 5 | import * as dotenv from 'dotenv' 6 | dotenv.config({ path: `${__dirname}/../.env` }) 7 | // Dependencies 8 | import { run } from '@grammyjs/runner' 9 | import { webhookApp } from '@/helpers/startWebhook' 10 | import Cluster from '@/helpers/Cluster' 11 | import attachChat from '@/middlewares/attachChat' 12 | import bot from '@/helpers/bot' 13 | import checkAdminLock from '@/middlewares/adminLock' 14 | import checkBanned from '@/handlers/checkBanned' 15 | import checkDocumentType from '@/middlewares/checkDocumentType' 16 | import checkFilesBanned from '@/middlewares/checkFilesBanned' 17 | import checkGoogleCredentials from '@/handlers/checkGoogleCredentials' 18 | import checkSuperAdmin from '@/middlewares/checkSuperAdmin' 19 | import configureI18n from '@/middlewares/configureI18n' 20 | import countMessage from '@/middlewares/countMessage' 21 | import disallowPrivate from '@/middlewares/disallowPrivate' 22 | import engines from '@/engines' 23 | import handleAddPromoException from '@/commands/handleAddPromoException' 24 | import handleAudio from '@/handlers/handleAudio' 25 | import handleDisableGoogle from '@/commands/handleDisableGoogle' 26 | import handleDonate from '@/commands/handleDonate' 27 | import handleEnableGoogle from '@/commands/handleEnableGoogle' 28 | import handleEngine from '@/commands/handleEngine' 29 | import handleFiles from '@/commands/handleFiles' 30 | import handleGeeky from '@/commands/handleGeeky' 31 | import handleGoogle from '@/commands/handleGoogle' 32 | import handleHelp from '@/commands/handleHelp' 33 | import handleId from '@/commands/handleId' 34 | import handleL from '@/commands/handleL' 35 | import handleLanguage from '@/commands/handleLanguage' 36 | import handleLock from '@/commands/handleLock' 37 | import handleMyChatMember from '@/handlers/handleMyChatMember' 38 | import handlePrivacy from '@/commands/handlePrivacy' 39 | import handleSetEngine from '@/handlers/handleSetEngine' 40 | import handleSetLanguage from '@/handlers/handleSetLanguage' 41 | import handleSilent from '@/commands/handleSilent' 42 | import handleStart from '@/commands/handleStart' 43 | import handleTimecodes from '@/commands/handleTimecodes' 44 | import handleTranscribe from './commands/handleTranscribe' 45 | import handleTranscribeAll from './commands/handleTranscribeAll' 46 | import handleUrl from '@/commands/handleUrl' 47 | import handleViewPromoExceptions from './commands/handleViewPromoExceptions' 48 | import handleWitToken from '@/commands/handleWitToken' 49 | import i18n from '@/helpers/i18n' 50 | import ignoreOldMessageUpdates from '@/middlewares/ignoreOldMessageUpdates' 51 | import recordTimeReceived from '@/middlewares/recordTimeReceived' 52 | import startMongo from '@/helpers/startMongo' 53 | 54 | async function runApp() { 55 | console.log('Starting app...') 56 | // Mongo 57 | await startMongo() 58 | console.log('Mongo started') 59 | // Middlewares 60 | bot.use(recordTimeReceived) 61 | bot.use(countMessage) 62 | bot.use(ignoreOldMessageUpdates) 63 | bot.use(attachChat) 64 | bot.use(i18n.middleware()) 65 | bot.use(configureI18n) 66 | bot.use(checkBanned) 67 | // Various events 68 | bot.on('my_chat_member', handleMyChatMember) 69 | bot.on(':document', checkGoogleCredentials) 70 | bot.on([':voice', ':video_note'], handleAudio) 71 | bot.on( 72 | [':audio', ':document'], 73 | checkFilesBanned, 74 | checkDocumentType, 75 | handleAudio 76 | ) 77 | // Commands 78 | bot.command('id', handleId) 79 | bot.command('donate', handleDonate) 80 | bot.command('start', checkAdminLock, handleStart) 81 | bot.command('help', checkAdminLock, handleHelp) 82 | bot.command('lock', disallowPrivate, checkAdminLock, handleLock) 83 | bot.command('files', checkAdminLock, handleFiles) 84 | bot.command('silent', checkAdminLock, handleSilent) 85 | bot.command('geeky', checkAdminLock, handleGeeky) 86 | bot.command('timecodes', checkAdminLock, handleTimecodes) 87 | bot.command('url', checkAdminLock, handleUrl) 88 | bot.command('privacy', checkAdminLock, handlePrivacy) 89 | bot.command('witToken', checkAdminLock, handleWitToken) 90 | bot.command('engine', checkAdminLock, handleEngine) 91 | bot.command('google', checkAdminLock, handleGoogle) 92 | bot.command('enableGoogle', checkAdminLock, handleEnableGoogle) 93 | bot.command('disableGoogle', checkAdminLock, handleDisableGoogle) 94 | bot.command('language', checkAdminLock, handleLanguage) 95 | bot.command('l', checkAdminLock, handleL) 96 | bot.command('addPromoException', checkSuperAdmin, handleAddPromoException) 97 | bot.command('viewPromoExceptions', checkSuperAdmin, handleViewPromoExceptions) 98 | bot.command('transcribeAll', checkAdminLock, handleTranscribeAll) 99 | bot.command('transcribe', checkAdminLock, handleTranscribe) 100 | // Callabcks 101 | bot.callbackQuery(Object.keys(engines), handleSetEngine) 102 | bot.callbackQuery(/li.+/, handleSetLanguage) 103 | // Errors 104 | bot.catch(console.error) 105 | // Start bot 106 | await bot.init() 107 | run(bot) 108 | console.info(`Bot ${bot.botInfo.username} is up and running`) 109 | // Start webhook app 110 | webhookApp.listen(4242, () => console.log('Running on port 4242')) 111 | } 112 | 113 | if (Cluster.isPrimary) { 114 | void runApp() 115 | } 116 | -------------------------------------------------------------------------------- /src/commands/handleAddPromoException.ts: -------------------------------------------------------------------------------- 1 | import { addPromoException } from '@/models/PromoException' 2 | import Context from '@/models/Context' 3 | 4 | export default async function handleAddPromoException(ctx: Context) { 5 | const ids = ctx.message.text.split(' ')[1] 6 | if (!ids) { 7 | return ctx.reply('😱') 8 | } 9 | for (const id of ids.split(',')) { 10 | await addPromoException(+id) 11 | } 12 | return ctx.reply('👍') 13 | } 14 | -------------------------------------------------------------------------------- /src/commands/handleDisableGoogle.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | 3 | export default async function handleDisableGoogle(ctx: Context) { 4 | ctx.dbchat.googleKey = undefined 5 | await ctx.dbchat.save() 6 | await ctx.reply(ctx.i18n.t('google_disable_success'), { 7 | parse_mode: 'Markdown', 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/handleDonate.ts: -------------------------------------------------------------------------------- 1 | import { stripe } from '@/helpers/stripe' 2 | import Context from '@/models/Context' 3 | import logAnswerTime from '@/helpers/logAnswerTime' 4 | 5 | export default async function handleDonate(ctx: Context) { 6 | console.log('/donate called', !!ctx.dbchat.paid) 7 | if (ctx.dbchat.paid) { 8 | await ctx.reply(ctx.i18n.t('already_paid'), { 9 | parse_mode: 'Markdown', 10 | disable_web_page_preview: true, 11 | }) 12 | } else { 13 | try { 14 | console.log('Not paid, sending typing action') 15 | await ctx.api.sendChatAction(ctx.dbchat.id, 'typing') 16 | console.log('Not paid, creating session') 17 | const session = await stripe.checkout.sessions.create({ 18 | line_items: [ 19 | { 20 | price: 'price_1LyeHbKXsMRGkVL4i2xZnaZk', 21 | quantity: 1, 22 | }, 23 | ], 24 | success_url: 'https://t.me/voicybot', 25 | cancel_url: 'https://t.me/voicybot', 26 | client_reference_id: `${ctx.dbchat.id}`, 27 | mode: 'payment', 28 | allow_promotion_codes: true, 29 | automatic_tax: { 30 | enabled: true, 31 | }, 32 | }) 33 | console.log('Not paid, sending message') 34 | await ctx.reply(ctx.i18n.t('pay'), { 35 | parse_mode: 'Markdown', 36 | disable_web_page_preview: true, 37 | reply_markup: { 38 | inline_keyboard: [ 39 | [ 40 | { 41 | text: ctx.i18n.t('pay_button'), 42 | url: session.url, 43 | }, 44 | ], 45 | ], 46 | }, 47 | }) 48 | } catch (error) { 49 | console.log('error sending checkout', error) 50 | } 51 | } 52 | logAnswerTime(ctx, '/donate') 53 | } 54 | -------------------------------------------------------------------------------- /src/commands/handleEnableGoogle.ts: -------------------------------------------------------------------------------- 1 | import { ChatModel } from '@/models/Chat' 2 | import Context from '@/models/Context' 3 | 4 | export default async function handleEnableGoogle(ctx: Context) { 5 | const sender = await ChatModel.findOne({ id: `${ctx.from.id}` }) 6 | if (!sender.googleKey) { 7 | return ctx.reply(ctx.i18n.t('google_enable_personal_not_setup'), { 8 | parse_mode: 'Markdown', 9 | }) 10 | } 11 | ctx.dbchat.googleKey = sender.googleKey 12 | await ctx.dbchat.save() 13 | return ctx.reply(ctx.i18n.t('google_enable_success'), { 14 | parse_mode: 'Markdown', 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/commands/handleEngine.ts: -------------------------------------------------------------------------------- 1 | import { InlineKeyboard } from 'grammy' 2 | import Context from '@/models/Context' 3 | import engines from '@/engines' 4 | import logAnswerTime from '@/helpers/logAnswerTime' 5 | 6 | export default async function handleEngine(ctx: Context) { 7 | const keyboard = new InlineKeyboard() 8 | for (const engine of Object.values(engines)) { 9 | keyboard.add({ 10 | text: engine.name, 11 | callback_data: engine.code, 12 | }) 13 | keyboard.row() 14 | } 15 | await ctx.reply(ctx.i18n.t('engine'), { 16 | reply_markup: keyboard, 17 | parse_mode: 'Markdown', 18 | reply_to_message_id: ctx.msg?.message_id, 19 | }) 20 | logAnswerTime(ctx, '/engine') 21 | } 22 | -------------------------------------------------------------------------------- /src/commands/handleFiles.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleFiles(ctx: Context) { 5 | ctx.dbchat.filesBanned = !ctx.dbchat.filesBanned 6 | await ctx.dbchat.save() 7 | await ctx.reply( 8 | ctx.i18n.t(ctx.dbchat.filesBanned ? 'files_false' : 'files_true'), 9 | { 10 | parse_mode: 'Markdown', 11 | } 12 | ) 13 | logAnswerTime(ctx, '/files') 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/handleGeeky.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleGeeky(ctx: Context) { 5 | await ctx.reply(ctx.i18n.t('geeky'), { 6 | parse_mode: 'Markdown', 7 | }) 8 | logAnswerTime(ctx, '/geeky') 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/handleGoogle.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleGoogle(ctx: Context) { 5 | const sentMessage = await ctx.reply(ctx.i18n.t('google'), { 6 | parse_mode: 'Markdown', 7 | }) 8 | ctx.dbchat.googleSetupMessageId = sentMessage.message_id 9 | await ctx.dbchat.save() 10 | logAnswerTime(ctx, '/google') 11 | } 12 | -------------------------------------------------------------------------------- /src/commands/handleHelp.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleHelp(ctx: Context) { 5 | await ctx.reply(ctx.i18n.t('help'), { 6 | disable_web_page_preview: true, 7 | parse_mode: 'Markdown', 8 | }) 9 | logAnswerTime(ctx, '/help') 10 | } 11 | -------------------------------------------------------------------------------- /src/commands/handleId.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleId(ctx: Context) { 5 | await ctx.reply(`\`${ctx.chat.id}\``, { 6 | parse_mode: 'Markdown', 7 | }) 8 | logAnswerTime(ctx, '/id') 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/handleL.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 3 | import engines from '@/engines' 4 | import localeCodeForChat from '@/helpers/localeCodeForChat' 5 | import sendLanguage from '@/helpers/language/sendLanguage' 6 | 7 | export default async function handleL(ctx: Context) { 8 | if (ctx.message.text.length <= 2) { 9 | return sendLanguage(ctx, true) 10 | } 11 | const language = ctx.message.text.split(' ')[1] 12 | if (!language) { 13 | return sendLanguage(ctx, true) 14 | } 15 | const engineObject: EngineRecognizer = engines[ctx.dbchat.engine] 16 | if (engineObject.languageException) { 17 | return ctx.reply(ctx.i18n.t(engineObject.languageException)) 18 | } 19 | const languageObject = engineObject.languages.find((l) => 20 | l.code.toLowerCase().includes(language.toLowerCase()) 21 | ) 22 | if (!languageObject) { 23 | return sendLanguage(ctx, true) 24 | } 25 | ctx.dbchat.languages[engineObject.code] = languageObject.code 26 | ctx.dbchat.markModified('languages') 27 | await ctx.dbchat.save() 28 | ctx.i18n.locale(localeCodeForChat(ctx.dbchat)) 29 | return ctx.reply('👍') 30 | } 31 | -------------------------------------------------------------------------------- /src/commands/handleLanguage.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 3 | import engines from '@/engines' 4 | import sendLanguage from '@/helpers/language/sendLanguage' 5 | 6 | export default function handleLanguage(ctx: Context) { 7 | const engineObject: EngineRecognizer = engines[ctx.dbchat.engine] 8 | if (engineObject.languageException) { 9 | return ctx.reply(ctx.i18n.t(engineObject.languageException), { 10 | parse_mode: 'Markdown', 11 | }) 12 | } 13 | return sendLanguage(ctx, true) 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/handleLock.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleLock(ctx: Context) { 5 | ctx.dbchat.adminLocked = !ctx.dbchat.adminLocked 6 | await ctx.dbchat.save() 7 | await ctx.reply( 8 | ctx.i18n.t(ctx.dbchat.adminLocked ? 'lock_true' : 'lock_false'), 9 | { 10 | parse_mode: 'Markdown', 11 | } 12 | ) 13 | logAnswerTime(ctx, '/lock') 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/handlePrivacy.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | 3 | export default function handlePrivacy(ctx: Context) { 4 | return ctx.reply('https://privacy.borodutch.com') 5 | } 6 | -------------------------------------------------------------------------------- /src/commands/handleSilent.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleSilent(ctx: Context) { 5 | ctx.dbchat.silent = !ctx.dbchat.silent 6 | await ctx.dbchat.save() 7 | await ctx.reply( 8 | ctx.i18n.t(ctx.dbchat.silent ? 'silent_true' : 'silent_false'), 9 | { 10 | parse_mode: 'Markdown', 11 | } 12 | ) 13 | logAnswerTime(ctx, '/silent') 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/handleStart.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import sendLanguage from '@/helpers/language/sendLanguage' 3 | import sendStart from '@/helpers/sendStart' 4 | import setLanguageCodeFromTelegram from '@/helpers/language/setLanguageCodeFromTelegram' 5 | 6 | export default async function handleStart(ctx: Context) { 7 | if (ctx.from && ctx.from.language_code) { 8 | ctx.dbchat = await setLanguageCodeFromTelegram(ctx) 9 | return sendStart(ctx) 10 | } 11 | return sendLanguage(ctx) 12 | } 13 | -------------------------------------------------------------------------------- /src/commands/handleTimecodes.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleTimecodes(ctx: Context) { 5 | ctx.dbchat.timecodesEnabled = !ctx.dbchat.timecodesEnabled 6 | await ctx.dbchat.save() 7 | await ctx.reply( 8 | ctx.i18n.t( 9 | ctx.dbchat.timecodesEnabled ? 'timecodes_true' : 'timecodes_false' 10 | ), 11 | { 12 | parse_mode: 'Markdown', 13 | } 14 | ) 15 | logAnswerTime(ctx, '/timecodes') 16 | } 17 | -------------------------------------------------------------------------------- /src/commands/handleTranscribe.ts: -------------------------------------------------------------------------------- 1 | import { ChatModel } from '@/models/Chat' 2 | import { sendTranscription } from '@/handlers/handleAudio' 3 | import Context from '@/models/Context' 4 | import fileUrl from '@/helpers/fileUrl' 5 | import report from '@/helpers/report' 6 | 7 | export default async function handleTranscribe(ctx: Context) { 8 | try { 9 | if (!ctx.dbchat.paid) { 10 | console.log('Sending the donate message') 11 | await ctx.reply(ctx.i18n.t('sunsetting'), { 12 | parse_mode: 'Markdown', 13 | reply_to_message_id: ctx.msg.message_id, 14 | disable_web_page_preview: true, 15 | }) 16 | return 17 | } 18 | if (!ctx.dbchat.paid) { 19 | await ChatModel.updateOne( 20 | { id: ctx.dbchat.id }, 21 | { $inc: { freeVoicesUsed: 1 } } 22 | ) 23 | } 24 | 25 | const message = ctx.msg.reply_to_message 26 | if (!message) { 27 | await ctx.reply(ctx.i18n.t('reply_to_voice'), { 28 | reply_to_message_id: ctx.msg.message_id, 29 | }) 30 | return 31 | } 32 | 33 | const voice = 34 | message.voice || message.document || message.audio || message.video_note 35 | 36 | if (!voice) { 37 | await ctx.reply(ctx.i18n.t('reply_to_voice'), { 38 | reply_to_message_id: ctx.msg.message_id, 39 | }) 40 | return 41 | } 42 | 43 | // Check size 44 | if (voice.file_size && voice.file_size >= 19 * 1024 * 1024) { 45 | if (!ctx.dbchat.silent) { 46 | await ctx.reply(ctx.i18n.t('error_twenty'), { 47 | parse_mode: 'Markdown', 48 | reply_to_message_id: message.message_id, 49 | }) 50 | } 51 | return 52 | } 53 | // Get full url to the voice message 54 | const fileData = await ctx.api.getFile(voice.file_id) 55 | const voiceUrl = fileUrl(fileData.file_path) 56 | 57 | // Sets message id to the original voice message's id 58 | ctx.msg.message_id = message.message_id 59 | // Send action or transcription depending on whether chat is silent 60 | await sendTranscription(ctx, voiceUrl, voice.file_id) 61 | } catch (error) { 62 | report(error, { ctx, location: 'handleTranscribe' }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/commands/handleTranscribeAll.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleTranscribeAll(ctx: Context) { 5 | ctx.dbchat.transcribeAllAudio = !ctx.dbchat.transcribeAllAudio 6 | await ctx.dbchat.save() 7 | await ctx.reply( 8 | ctx.i18n.t( 9 | !ctx.dbchat.transcribeAllAudio 10 | ? 'transcribe_all_false' 11 | : 'transcribe_all_true' 12 | ), 13 | { 14 | parse_mode: 'Markdown', 15 | } 16 | ) 17 | logAnswerTime(ctx, 'handleTranscribeAll') 18 | } 19 | -------------------------------------------------------------------------------- /src/commands/handleUrl.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleUrl(ctx: Context) { 5 | await ctx.reply(ctx.i18n.t('url'), { 6 | parse_mode: 'Markdown', 7 | }) 8 | logAnswerTime(ctx, '/url') 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/handleViewPromoExceptions.ts: -------------------------------------------------------------------------------- 1 | import { promoExceptions } from '@/models/PromoException' 2 | import Context from '@/models/Context' 3 | 4 | export default function handleViewPromoExceptions(ctx: Context) { 5 | return ctx.reply(promoExceptions.join(', ')) 6 | } 7 | -------------------------------------------------------------------------------- /src/commands/handleWitToken.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function handleWitToken(ctx: Context) { 5 | const witToken = ctx.message.text.split(' ')[1] 6 | if (!witToken) { 7 | ctx.dbchat.witToken = undefined 8 | } else { 9 | ctx.dbchat.witToken = witToken 10 | } 11 | await ctx.dbchat.save() 12 | await ctx.reply('👍') 13 | logAnswerTime(ctx, '/witToken') 14 | } 15 | -------------------------------------------------------------------------------- /src/engines/ashmanov.ts: -------------------------------------------------------------------------------- 1 | import * as FormData from 'form-data' 2 | import { PBKDF2 } from 'crypto-js' 3 | import { createReadStream, statSync } from 'fs' 4 | import Engine from '@/helpers/engine/Engine' 5 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 6 | import RecognitionConfig from '@/helpers/engine/RecognitionConfig' 7 | import axios, { AxiosResponse } from 'axios' 8 | 9 | const salt = process.env.SALT 10 | function hashString(s: string) { 11 | return PBKDF2(s, salt).toString() 12 | } 13 | 14 | interface AshmanovResponse { 15 | response_code: number 16 | msg?: string 17 | r: { 18 | response: { 19 | text: string 20 | }[] 21 | }[] 22 | } 23 | 24 | async function recognize({ 25 | duration, 26 | flacPath, 27 | userId, 28 | forwardedFrom, 29 | }: RecognitionConfig) { 30 | const formData = new FormData() 31 | formData.append('model_type', 'ASR') 32 | formData.append('filename', '67006370772') 33 | formData.append('audio_blob', createReadStream(flacPath), { 34 | knownLength: statSync(flacPath).size, 35 | }) 36 | formData.append('language', 'ru') 37 | formData.append('decoder_name', 'general') 38 | formData.append('sample_rate', 16000) 39 | if (userId) { 40 | formData.append('user_id', hashString(`${userId}`)) 41 | } 42 | if (forwardedFrom) { 43 | formData.append('forwarded_from', hashString(`${forwardedFrom}`)) 44 | } 45 | 46 | const headers = { 47 | ...formData.getHeaders(), 48 | 'Content-Length': `${formData.getLengthSync()}`, 49 | Authorization: 'Basic DtuMDtuFvKTxNFU1ympiunKVMbhoGOU77cIAv03O', 50 | } 51 | 52 | const { data } = (await axios({ 53 | method: 'POST', 54 | url: 'https://asr.nanosemantics.ai/asr/', 55 | data: formData, 56 | headers, 57 | maxBodyLength: Infinity, 58 | maxContentLength: Infinity, 59 | })) as AxiosResponse 60 | if (data.response_code === 3 && data.msg) { 61 | throw new Error(data.msg) 62 | } 63 | const text = data.r[0].response[0].text 64 | return [{ timeCode: `0-${duration}`, text }] 65 | } 66 | 67 | export const ashmanov: EngineRecognizer = { 68 | code: Engine.ashmanov, 69 | name: 'Nanosemantics', 70 | messageWhenEngineIsSet: 71 | 'Пожалуйста, заметьте, что Nanosemantics — это движок распознавания речи, который никак не аффилирован с Войси. Команда Войси не отвечат за то, насколько сохранны ваши данные при использовании движка Nanosemantics, так что используйте на свои страх и риск. Спасибо!', 72 | languageForTelegramCode: () => 'ru', 73 | defaultLanguageCode: 'ru', 74 | recognize, 75 | languageException: 'ashmanov_language', 76 | languages: [{ code: 'ru', name: 'Русский', i18nCode: 'ru' }], 77 | } 78 | -------------------------------------------------------------------------------- /src/engines/index.ts: -------------------------------------------------------------------------------- 1 | import { ashmanov } from '@/engines/ashmanov' 2 | import { google } from '@/engines/google' 3 | import { platinumfund } from '@/engines/platinumfund' 4 | import { wit } from '@/engines/wit' 5 | 6 | const engines = { 7 | ashmanov, 8 | google, 9 | wit, 10 | platinumfund, 11 | } 12 | 13 | export default engines 14 | -------------------------------------------------------------------------------- /src/engines/platinumfund.ts: -------------------------------------------------------------------------------- 1 | import * as FormData from 'form-data' 2 | import { createReadStream } from 'fs' 3 | import Engine from '@/helpers/engine/Engine' 4 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 5 | import RecognitionConfig from '@/helpers/engine/RecognitionConfig' 6 | import RecognitionResultPart from '@/helpers/engine/RecognitionResultPart' 7 | import axios from 'axios' 8 | 9 | const platinumFundLanguages = { 10 | Vietnamese: 'vn', 11 | French: 'fr', 12 | English: 'en', 13 | Chinese: 'cn', 14 | Catalan: 'ca', 15 | Italian: 'it', 16 | Kazakh: 'kz', 17 | Turkish: 'tr', 18 | Spanish: 'es', 19 | Portuguese: 'pt', 20 | German: 'de', 21 | Ukrainian: 'uk', 22 | Swedish: 'sv', 23 | Farsi: 'fa', 24 | Russian: 'ru', 25 | } 26 | 27 | const defaultLanguageCode = 'English' 28 | 29 | function languageForTelegramCode(telegramCode: string): string { 30 | if (!telegramCode) { 31 | return defaultLanguageCode 32 | } 33 | for (const key of Object.keys(platinumFundLanguages)) { 34 | if (telegramCode.toLowerCase().includes(key.toLowerCase())) { 35 | return key 36 | } 37 | } 38 | return defaultLanguageCode 39 | } 40 | 41 | interface PlatinumFundRecognitionResult { 42 | status: string 43 | result?: { 44 | text: string 45 | } 46 | message?: string 47 | } 48 | 49 | async function recognize({ 50 | chat, 51 | duration, 52 | ogaPath, 53 | }: RecognitionConfig): Promise { 54 | const bodyFormData = new FormData() 55 | bodyFormData.append( 56 | 'lang', 57 | platinumFundLanguages[ 58 | chat.languages[Engine.platinumfund] || defaultLanguageCode 59 | ] 60 | ) 61 | bodyFormData.append('speech', createReadStream(ogaPath)) 62 | 63 | const { data: recognitionResult } = 64 | await axios.request({ 65 | method: 'POST', 66 | url: 'https://vosk.platinum.fund/api/v1/stt', 67 | data: bodyFormData, 68 | headers: { 69 | 'Content-Type': `multipart/form-data; boundary=${bodyFormData.getBoundary()}`, 70 | }, 71 | }) 72 | 73 | if (recognitionResult.status === 'error') { 74 | throw new Error(recognitionResult.message) 75 | } 76 | 77 | return Promise.resolve([ 78 | { 79 | text: recognitionResult.result.text, 80 | timeCode: `0-${duration}`, 81 | }, 82 | ]) 83 | } 84 | 85 | export const platinumfund: EngineRecognizer = { 86 | code: Engine.platinumfund, 87 | name: 'Platinum Fund', 88 | messageWhenEngineIsSet: 89 | 'Platinum Fund team has kindly provided us with their speech to text server API. Keep in mind that Voicy is not affiliated with Platinum Fund in any way. Use this engine at your own risk.', 90 | languages: Object.keys(platinumFundLanguages).map((l) => ({ 91 | code: l, 92 | name: l, 93 | i18nCode: platinumFundLanguages[l], 94 | })), 95 | defaultLanguageCode, 96 | languageForTelegramCode, 97 | recognize, 98 | } 99 | -------------------------------------------------------------------------------- /src/handlers/checkBanned.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | export default function checkBanned(ctx: Context, next: NextFunction) { 5 | if (ctx.dbchat.banned) { 6 | return ctx.reply( 7 | 'You are banned (most likely for fraud). Contact @borodutch if this is a mistake.' 8 | ) 9 | } 10 | return next() 11 | } 12 | -------------------------------------------------------------------------------- /src/handlers/checkGoogleCredentials.ts: -------------------------------------------------------------------------------- 1 | import * as download from 'download' 2 | import { NextFunction } from 'grammy' 3 | import Context from '@/models/Context' 4 | import bot from '@/helpers/bot' 5 | import fileUrl from '@/helpers/fileUrl' 6 | import logAnswerTime from '@/helpers/logAnswerTime' 7 | import report from '@/helpers/report' 8 | 9 | export default async function checkGoogleCredentials( 10 | ctx: Context, 11 | next: NextFunction 12 | ) { 13 | const replyToMessage = ctx.message?.reply_to_message 14 | // Check if reply 15 | if (!replyToMessage) { 16 | return next() 17 | } 18 | // Check if reply is to a bot's message 19 | if (replyToMessage.from?.id !== bot.botInfo.id) { 20 | return next() 21 | } 22 | // Check if reply is to the credentials request message 23 | if (replyToMessage.message_id !== ctx.dbchat.googleSetupMessageId) { 24 | return next() 25 | } 26 | // Check if the document exists 27 | const document = ctx.message?.document 28 | if (!document) { 29 | return next() 30 | } 31 | // Check document type 32 | if ( 33 | !document.file_name || 34 | (!document.file_name.includes('json') && 35 | !document.file_name.includes('txt')) 36 | ) { 37 | return ctx.reply(ctx.i18n.t('google_error_mime'), { 38 | parse_mode: 'Markdown', 39 | }) 40 | } 41 | try { 42 | // Download the file 43 | const fileData = await ctx.api.getFile(document.file_id) 44 | const url = await fileUrl(fileData.file_path) 45 | // Download credentials file 46 | const data = await download(url) 47 | // Save to chat 48 | ctx.dbchat.googleKey = data.toString('utf8') 49 | await ctx.dbchat.save() 50 | // Reply with confirmation 51 | await ctx.reply( 52 | ctx.i18n.t('google_success', { 53 | projectId: JSON.parse(ctx.dbchat.googleKey).project_id, 54 | }), 55 | { 56 | parse_mode: 'Markdown', 57 | } 58 | ) 59 | // Log time 60 | logAnswerTime(ctx, 'credentials check') 61 | } catch (error) { 62 | report(error, { ctx, location: 'setupCheckingCredentials' }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/handlers/handleAudio.ts: -------------------------------------------------------------------------------- 1 | import { Chat, ChatModel } from '@/models/Chat' 2 | import { Message } from '@grammyjs/types' 3 | import { addVoice } from '@/models/Voice' 4 | import { pick } from 'lodash' 5 | import Context from '@/models/Context' 6 | import Engine from '@/helpers/engine/Engine' 7 | import addPromoToText from '@/helpers/addPromoToText' 8 | import fileUrl from '@/helpers/fileUrl' 9 | import report from '@/helpers/report' 10 | import urlToText from '@/helpers/urlToText' 11 | 12 | export default async function handleAudio(ctx: Context) { 13 | try { 14 | if (!ctx.dbchat.paid) { 15 | console.log('Sending the donate message') 16 | await ctx.reply(ctx.i18n.t('sunsetting'), { 17 | parse_mode: 'Markdown', 18 | reply_to_message_id: ctx.msg.message_id, 19 | disable_web_page_preview: true, 20 | }) 21 | return 22 | } 23 | if (!ctx.dbchat.paid) { 24 | await ChatModel.updateOne( 25 | { id: ctx.dbchat.id }, 26 | { $inc: { freeVoicesUsed: 1 } } 27 | ) 28 | } 29 | // In a group or supergroup, only transcribe if transcribeAllAudio is true 30 | const isGroup = ctx.chat.type === 'group' || ctx.chat.type === 'supergroup' 31 | if (!ctx.dbchat.transcribeAllAudio && isGroup) { 32 | console.log('Ignored cause transcribeAllAudio is false') 33 | return 34 | } 35 | const message = ctx.msg 36 | const voice = 37 | message.voice || message.document || message.audio || message.video_note 38 | // Check size 39 | if (voice.file_size && voice.file_size >= 19 * 1024 * 1024) { 40 | if (!ctx.dbchat.silent) { 41 | await sendLargeFileError(ctx) 42 | } 43 | return 44 | } 45 | // Get full url to the voice message 46 | const fileData = await ctx.getFile() 47 | const voiceUrl = fileUrl(fileData.file_path) 48 | // Send action or transcription depending on whether chat is silent 49 | await sendTranscription(ctx, voiceUrl, voice.file_id) 50 | } catch (error) { 51 | report(error, { ctx, location: 'handleMessage' }) 52 | } 53 | } 54 | 55 | function sendLargeFileError(ctx: Context) { 56 | return ctx.reply(ctx.i18n.t('error_twenty'), { 57 | parse_mode: 'Markdown', 58 | reply_to_message_id: ctx.msg.message_id, 59 | }) 60 | } 61 | 62 | async function sendTranscription(ctx: Context, url: string, fileId: string) { 63 | // Send typing action or dummy message 64 | let dummyMessage: Message 65 | if (ctx.dbchat.silent) { 66 | await ctx.replyWithChatAction('typing') 67 | } else { 68 | dummyMessage = await ctx.reply(ctx.i18n.t('initiated'), { 69 | reply_to_message_id: ctx.msg.message_id, 70 | parse_mode: 'Markdown', 71 | }) 72 | } 73 | // Check if ok with google engine 74 | if (ctx.dbchat.engine === 'google' && !ctx.dbchat.googleKey) { 75 | if (dummyMessage) { 76 | await ctx.api.editMessageText( 77 | ctx.dbchat.id, 78 | dummyMessage.message_id, 79 | ctx.i18n.t('google_error_creds'), 80 | { 81 | parse_mode: 'Markdown', 82 | } 83 | ) 84 | } 85 | return 86 | } 87 | try { 88 | // Convert utl to text 89 | const { textWithTimecodes, duration } = await urlToText( 90 | url, 91 | sanitizeChat(ctx.dbchat), 92 | ctx.msg.forward_from?.id || ctx.from?.id, 93 | ctx.msg.forward_sender_name 94 | ) 95 | // Send trancription to user 96 | const text = ctx.dbchat.timecodesEnabled 97 | ? textWithTimecodes 98 | .map((t) => `${t.timeCode}:\n${t.text || ''}`) 99 | .join('\n') 100 | : textWithTimecodes 101 | .map((t) => (t.text || '').trim()) 102 | .filter((v) => !!v) 103 | .join('. ') 104 | const texts = splitText(text) || [''] 105 | const firstText = texts.shift().trim() 106 | if (dummyMessage) { 107 | await ctx.api.editMessageText( 108 | ctx.dbchat.id, 109 | dummyMessage.message_id, 110 | firstText 111 | ? addPromoToText(ctx, firstText) 112 | : ctx.i18n.t('speak_clearly'), 113 | { 114 | parse_mode: 'Markdown', 115 | disable_web_page_preview: true, 116 | } 117 | ) 118 | } else if (firstText) { 119 | await ctx.reply(addPromoToText(ctx, firstText), { 120 | reply_to_message_id: ctx.msg.message_id, 121 | parse_mode: 'Markdown', 122 | disable_web_page_preview: true, 123 | }) 124 | } 125 | if (texts.length) { 126 | for (const element of texts) { 127 | await ctx.reply(element, { 128 | reply_to_message_id: ctx.msg.message_id, 129 | parse_mode: 'Markdown', 130 | disable_web_page_preview: true, 131 | }) 132 | } 133 | } 134 | await addVoice({ 135 | url, 136 | textWithTimecodes, 137 | chat: ctx.dbchat, 138 | duration, 139 | fileId, 140 | }) 141 | } catch (error) { 142 | if (dummyMessage) { 143 | let text = ctx.i18n.t('error') 144 | if (ctx.dbchat.engine === Engine.google) { 145 | text = `${text}\n\n\`\`\`\n${error.message || 'Unknown error'}\n\`\`\`` 146 | } 147 | try { 148 | await ctx.api.editMessageText( 149 | ctx.dbchat.id, 150 | dummyMessage.message_id, 151 | text, 152 | { 153 | parse_mode: 'Markdown', 154 | } 155 | ) 156 | } catch (error) { 157 | report(error, { ctx, location: 'updateMessagewithError' }) 158 | } 159 | } 160 | try { 161 | // Check if it's the wrong wit token and remove it 162 | if (ctx.dbchat.engine === Engine.wit && ctx.dbchat.witToken) { 163 | const errors = [ 164 | 'Invalid character in header content', 165 | 'Bad auth, check token/params', 166 | ] 167 | if (errors.find((e) => error.message?.includes(e))) { 168 | ctx.dbchat.witToken = undefined 169 | await ctx.dbchat.save() 170 | } 171 | } 172 | } catch (error) { 173 | report(error, { ctx, location: 'removeBadWitToken' }) 174 | } 175 | report(error, { ctx, location: 'sendTranscription' }) 176 | } finally { 177 | console.info( 178 | `audio message processed in ${ 179 | (new Date().getTime() - ctx.timeReceived.getTime()) / 1000 180 | }s` 181 | ) 182 | } 183 | } 184 | 185 | function splitText(text: string): string[] { 186 | const chunks = text.match(/[\s\S]{1,4000}/g) 187 | return chunks 188 | } 189 | 190 | function sanitizeChat(chat: Chat): Partial { 191 | return pick(chat, [ 192 | 'id', 193 | 'engine', 194 | 'adminLocked', 195 | 'silent', 196 | 'filesBanned', 197 | 'googleSetupMessageId', 198 | 'googleKey', 199 | 'languages', 200 | 'witToken', 201 | ]) 202 | } 203 | 204 | export { sendTranscription } 205 | -------------------------------------------------------------------------------- /src/handlers/handleMyChatMember.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import sendLanguage from '@/helpers/language/sendLanguage' 3 | 4 | export default function handleMyChatMember(ctx: Context) { 5 | if (ctx.myChatMember.new_chat_member.status !== 'member') { 6 | return 7 | } 8 | return sendLanguage(ctx) 9 | } 10 | -------------------------------------------------------------------------------- /src/handlers/handleSetEngine.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import Engine from '@/helpers/engine/Engine' 3 | import engines from '@/engines' 4 | import logAnswerTime from '@/helpers/logAnswerTime' 5 | 6 | export default async function handleSetEngine(ctx: Context) { 7 | const engineCode = ctx.callbackQuery.data 8 | ctx.dbchat.engine = engineCode as Engine 9 | await ctx.dbchat.save() 10 | const engineObject = engines[engineCode] 11 | await ctx.editMessageText( 12 | ctx.i18n.t('engine_success', { engine: engineObject.name }), 13 | { 14 | parse_mode: 'Markdown', 15 | } 16 | ) 17 | if (engineObject.messageWhenEngineIsSet) { 18 | await ctx.reply(engineObject.messageWhenEngineIsSet, { 19 | parse_mode: 'Markdown', 20 | }) 21 | } 22 | await ctx.answerCallbackQuery() 23 | logAnswerTime(ctx, 'setting engine') 24 | } 25 | -------------------------------------------------------------------------------- /src/handlers/handleSetLanguage.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import Engine from '@/helpers/engine/Engine' 3 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 4 | import engines from '@/engines' 5 | import languageKeyboard from '@/helpers/language/languageKeyboard' 6 | import localeCodeForChat from '@/helpers/localeCodeForChat' 7 | import logAnswerTime from '@/helpers/logAnswerTime' 8 | import report from '@/helpers/report' 9 | import sendStart from '@/helpers/sendStart' 10 | 11 | function languageString(languageCode: string, engine: Engine) { 12 | const engineObject = engines[engine] 13 | const language = engineObject.languages.find((l) => l.code === languageCode) 14 | return language.name 15 | } 16 | 17 | export default async function handleSetLanguage(ctx: Context) { 18 | // Get options 19 | const options = ctx.callbackQuery.data.split('~') 20 | const engine = options[2] as Engine 21 | const engineObject: EngineRecognizer = engines[engine] 22 | const isCommand = +options[1] === 1 23 | // Get language 24 | const language = options[3] 25 | // Check if pagination 26 | if (['<', '>'].includes(language)) { 27 | // Get text 28 | const text = isCommand 29 | ? ctx.i18n.t('language', { engine: engineObject.name }) 30 | : ctx.i18n.t('language_without_engine') 31 | // Get page 32 | const page = +options[4] 33 | // Edit message 34 | try { 35 | await ctx.editMessageText(text, { 36 | reply_markup: languageKeyboard( 37 | engine, 38 | isCommand, 39 | language === '<' ? page - 1 : page + 1 40 | ), 41 | parse_mode: 'Markdown', 42 | }) 43 | } catch (error) { 44 | report(error, { ctx, location: 'handleSetLanguage' }) 45 | } 46 | return 47 | } 48 | // Set language 49 | ctx.dbchat.languages[engine] = language 50 | ctx.dbchat.markModified('languages') 51 | // Save chat 52 | await ctx.dbchat.save() 53 | // Update language 54 | ctx.i18n.locale(localeCodeForChat(ctx.dbchat)) 55 | // Edit message 56 | await ctx.editMessageText( 57 | ctx.i18n.t('language_success', { 58 | language: languageString(language, engine), 59 | engine: engineObject.name, 60 | }), 61 | { 62 | parse_mode: 'Markdown', 63 | } 64 | ) 65 | // Recomend Nanosemantics 66 | if (engine === 'wit' && languageString(language, engine) === 'Russian') { 67 | await ctx.reply( 68 | 'Вы используете движок Wit.ai для распознавания русского языка. Советую вам попробовать Nanosemantics в /engine, он работает лучше с русским языком. Спасибо!' 69 | ) 70 | } 71 | // If it was not a command, send start 72 | if (!isCommand) { 73 | await sendStart(ctx) 74 | } 75 | // Log time 76 | logAnswerTime(ctx, 'setting language') 77 | } 78 | -------------------------------------------------------------------------------- /src/helpers/Cluster.ts: -------------------------------------------------------------------------------- 1 | import cluster = require('cluster') 2 | import { Cluster } from 'cluster' 3 | 4 | const Cluster = cluster as unknown as Cluster 5 | 6 | export default Cluster 7 | -------------------------------------------------------------------------------- /src/helpers/addPromoToText.ts: -------------------------------------------------------------------------------- 1 | // import { promoExceptions } from '@/models/PromoException' 2 | import Context from '@/models/Context' 3 | // import isRuChat from '@/helpers/isRuChat' 4 | // import promoTexts from '@/helpers/promoTexts' 5 | 6 | export default function addPromoToText(ctx: Context, text: string) { 7 | // return promoExceptions.includes(+ctx.dbchat.id) 8 | // ? text 9 | // : `${text}\n${isRuChat(ctx.dbchat) ? promoTexts.ru() : promoTexts.en()}` 10 | return text 11 | } 12 | -------------------------------------------------------------------------------- /src/helpers/augmentError.ts: -------------------------------------------------------------------------------- 1 | export default function augmentError(error: Error, augmentation: string) { 2 | error.message = `${augmentation}, ${error.message}` 3 | return error 4 | } 5 | -------------------------------------------------------------------------------- /src/helpers/bot.ts: -------------------------------------------------------------------------------- 1 | import { Bot } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | const bot = new Bot(process.env.TOKEN) 5 | 6 | export default bot 7 | -------------------------------------------------------------------------------- /src/helpers/cloud.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | import { Storage } from '@google-cloud/storage' 3 | import { basename } from 'path' 4 | 5 | function getStorage(key) { 6 | return new Storage({ 7 | credentials: key, 8 | projectId: key.project_id, 9 | }) 10 | } 11 | 12 | export async function put(filePath: string, chat: Partial) { 13 | const key = JSON.parse(chat.googleKey) 14 | const storage = getStorage(key) 15 | const bucket = storage.bucket(key.project_id) 16 | const exists = await bucket.exists() 17 | if (!exists[0]) { 18 | await bucket.create() 19 | } 20 | const [file] = await bucket.upload(filePath) 21 | return `gs://${key.project_id}/${file.name}` 22 | } 23 | 24 | export async function del(uri: string, chat: Partial) { 25 | const key = JSON.parse(chat.googleKey) 26 | const storage = getStorage(key) 27 | const bucket = storage.bucket(key.project_id) 28 | const file = bucket.file(basename(uri)) 29 | await file.delete() 30 | } 31 | -------------------------------------------------------------------------------- /src/helpers/deleteFile.ts: -------------------------------------------------------------------------------- 1 | import { unlinkSync } from 'fs' 2 | 3 | export default function deleteFile(path: string) { 4 | try { 5 | unlinkSync(path) 6 | } catch (error) { 7 | // do nothing, probably file doesn't exist already 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/helpers/engine/Engine.ts: -------------------------------------------------------------------------------- 1 | enum Engine { 2 | google = 'google', 3 | wit = 'wit', 4 | ashmanov = 'ashmanov', 5 | platinumfund = 'platinumfund', 6 | } 7 | 8 | export default Engine 9 | -------------------------------------------------------------------------------- /src/helpers/engine/EngineRecognizer.ts: -------------------------------------------------------------------------------- 1 | import Engine from '@/helpers/engine/Engine' 2 | import Language from '@/helpers/engine/Language' 3 | import RecognitionConfig from '@/helpers/engine/RecognitionConfig' 4 | import RecognitionResultPart from '@/helpers/engine/RecognitionResultPart' 5 | 6 | export default interface EngineRecognizer { 7 | code: Engine 8 | name: string 9 | messageWhenEngineIsSet?: string 10 | languages: Language[] 11 | recognize: (config: RecognitionConfig) => Promise 12 | languageForTelegramCode: (telegramCode: string) => string 13 | defaultLanguageCode: string 14 | languageException?: string 15 | } 16 | -------------------------------------------------------------------------------- /src/helpers/engine/Language.ts: -------------------------------------------------------------------------------- 1 | export default interface Language { 2 | code: string 3 | name: string 4 | i18nCode: string 5 | } 6 | -------------------------------------------------------------------------------- /src/helpers/engine/RecognitionConfig.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | 3 | interface RecognitionConfig { 4 | flacPath: string 5 | chat: Partial 6 | duration: number 7 | ogaPath: string 8 | userId?: number 9 | forwardedFrom?: string 10 | } 11 | 12 | export default RecognitionConfig 13 | -------------------------------------------------------------------------------- /src/helpers/engine/RecognitionResult.ts: -------------------------------------------------------------------------------- 1 | import RecognitionResultPart from '@/helpers/engine/RecognitionResultPart' 2 | 3 | export default interface RecognitionResult { 4 | textWithTimecodes: RecognitionResultPart[] 5 | duration: number 6 | } 7 | -------------------------------------------------------------------------------- /src/helpers/engine/RecognitionResultPart.ts: -------------------------------------------------------------------------------- 1 | interface RecognitionResultPart { 2 | timeCode: string 3 | text: string 4 | } 5 | 6 | export default RecognitionResultPart 7 | -------------------------------------------------------------------------------- /src/helpers/fileUrl.ts: -------------------------------------------------------------------------------- 1 | import { escape } from 'querystring' 2 | 3 | export default function fileUrl(filePath: string) { 4 | return `https://api.telegram.org/file/bot${process.env.TOKEN}/${escape( 5 | filePath 6 | )}` 7 | } 8 | -------------------------------------------------------------------------------- /src/helpers/flac.ts: -------------------------------------------------------------------------------- 1 | import deleteFile from '@/helpers/deleteFile' 2 | import ffmpeg = require('fluent-ffmpeg') 3 | import * as temp from 'temp' 4 | 5 | export default function flac(filepath: string) { 6 | return new Promise<{ flacPath: string; duration: number }>( 7 | (resolve, reject) => { 8 | ffmpeg.ffprobe(filepath, (err, info) => { 9 | if (err) { 10 | reject(err) 11 | return 12 | } 13 | 14 | const fileSize = info.format.duration 15 | const output = temp.path({ suffix: '.flac' }) 16 | 17 | ffmpeg() 18 | .on('error', (error) => { 19 | deleteFile(output) 20 | reject(error) 21 | }) 22 | .on('end', () => resolve({ flacPath: output, duration: +fileSize })) 23 | .input(filepath) 24 | .setStartTime(0) 25 | .duration(fileSize) 26 | .output(output) 27 | .audioFrequency(16000) 28 | .toFormat('flac') 29 | .run() 30 | }) 31 | } 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /src/helpers/getTextFromAudio.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | import engines from '@/engines' 3 | 4 | export default function getTextFromAudio( 5 | flacPath: string, 6 | chat: Partial, 7 | duration: number, 8 | ogaPath: string, 9 | userId?: number, 10 | forwardedFrom?: string 11 | ) { 12 | const engineObject = engines[chat.engine] 13 | return engineObject.recognize({ 14 | flacPath, 15 | chat, 16 | duration, 17 | ogaPath, 18 | userId, 19 | forwardedFrom, 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /src/helpers/i18n.ts: -------------------------------------------------------------------------------- 1 | import { I18n } from '@grammyjs/i18n' 2 | 3 | const i18n = new I18n({ 4 | defaultLanguageOnMissing: true, 5 | directory: `${__dirname}/../../locales`, 6 | defaultLanguage: 'en', 7 | }) 8 | 9 | export default i18n 10 | -------------------------------------------------------------------------------- /src/helpers/incrementMessageCount.ts: -------------------------------------------------------------------------------- 1 | import { MessageStatsModel } from '@/models/MessageStats' 2 | 3 | const step = 100000 4 | let i = 0 5 | 6 | export default function incrementMessageCount() { 7 | if (i <= step) { 8 | i += 1 9 | } else { 10 | i = 0 11 | void incrementTodaysCount() 12 | } 13 | } 14 | 15 | async function incrementTodaysCount() { 16 | const date = dateToEpoch(new Date()) 17 | const { doc } = await MessageStatsModel.findOrCreate({ date }) 18 | doc.count += step 19 | return doc.save() 20 | } 21 | 22 | function dateToEpoch(date) { 23 | return date.setHours(0, 0, 0, 0) 24 | } 25 | -------------------------------------------------------------------------------- /src/helpers/isRuChat.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | import Engine from '@/helpers/engine/Engine' 3 | 4 | export default function isRuChat(chat: Chat) { 5 | return ( 6 | chat.engine === Engine.ashmanov || 7 | (chat.engine === Engine.wit && chat.languages[Engine.wit] === 'Russian') || 8 | (chat.engine === Engine.google && 9 | chat.languages[Engine.google] === 'ru-RU') || 10 | (chat.engine === Engine.platinumfund && 11 | chat.languages[Engine.platinumfund] === 'Russian') 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/helpers/language/languageKeyboard.ts: -------------------------------------------------------------------------------- 1 | import { InlineKeyboard } from 'grammy' 2 | import Engine from '@/helpers/engine/Engine' 3 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 4 | import Language from '@/helpers/engine/Language' 5 | import engines from '@/engines' 6 | 7 | const pageSize = 10 8 | 9 | function buttonFromItem(item: Language, isCommand: boolean, engine: Engine) { 10 | return { 11 | text: item.name, 12 | callback_data: `li~${isCommand ? 1 : 0}~${engine}~${item.code}`, 13 | } 14 | } 15 | 16 | function pageFromList( 17 | list: Language[], 18 | page: number, 19 | isCommand: boolean, 20 | engine: Engine 21 | ) { 22 | const items = list.slice(page * pageSize, page * pageSize + pageSize) 23 | return items.reduce((p, c, i) => { 24 | p.add(buttonFromItem(c, isCommand, engine)) 25 | if (i % 2 !== 0) { 26 | p.row() 27 | } 28 | return p 29 | }, new InlineKeyboard()) 30 | } 31 | 32 | export default function languageKeyboard( 33 | engine: Engine, 34 | isCommand: boolean, 35 | page = 0 36 | ) { 37 | const engineObject: EngineRecognizer = engines[engine] 38 | const list = engineObject.languages 39 | list.sort((a, b) => (a.name < b.name ? -1 : 1)) 40 | const keyboard = pageFromList(list, page, isCommand, engine) 41 | if (list.length > pageSize) { 42 | if (page > 0) { 43 | keyboard.row() 44 | keyboard.add({ 45 | text: '⬅️', 46 | callback_data: `li~${isCommand ? 1 : 0}~${engine}~<~${page}`, 47 | }) 48 | } 49 | if (page < Object.keys(list).length / 10 - 1) { 50 | if (page <= 0) { 51 | keyboard.row() 52 | } 53 | keyboard.add({ 54 | text: '➡️', 55 | callback_data: `li~${isCommand ? 1 : 0}~${engine}~>~${page}`, 56 | }) 57 | } 58 | } 59 | return keyboard 60 | } 61 | -------------------------------------------------------------------------------- /src/helpers/language/sendLanguage.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 3 | import engines from '@/engines' 4 | import languageKeyboard from '@/helpers/language/languageKeyboard' 5 | import logAnswerTime from '@/helpers/logAnswerTime' 6 | 7 | export default async function sendLanguage(ctx: Context, isCommand?: boolean) { 8 | const engineObject: EngineRecognizer = engines[ctx.dbchat.engine] 9 | const text = isCommand 10 | ? ctx.i18n.t('language', { engine: engineObject.name }) 11 | : ctx.i18n.t('language_without_engine') 12 | await ctx.reply(text, { 13 | reply_markup: languageKeyboard(ctx.dbchat.engine, isCommand), 14 | reply_to_message_id: ctx.msg?.message_id, 15 | }) 16 | logAnswerTime(ctx, '/language') 17 | } 18 | -------------------------------------------------------------------------------- /src/helpers/language/setLanguageCodeFromTelegram.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import Engine from '@/helpers/engine/Engine' 3 | import engines from '@/engines' 4 | import localeCodeForChat from '@/helpers/localeCodeForChat' 5 | 6 | export default function setLanguageCodeFromTelegram(ctx: Context) { 7 | const code = ctx.from.language_code.split('-')[0] 8 | for (const engine of Object.values(engines)) { 9 | ctx.dbchat.languages[engine.code] = engine.languageForTelegramCode(code) 10 | } 11 | ctx.dbchat.markModified('languages') 12 | if (code.toLowerCase().indexOf('ru') > -1) { 13 | ctx.dbchat.engine = Engine.ashmanov 14 | } 15 | ctx.i18n.locale(localeCodeForChat(ctx.dbchat)) 16 | return ctx.dbchat.save() 17 | } 18 | -------------------------------------------------------------------------------- /src/helpers/localeCodeForChat.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | import EngineRecognizer from '@/helpers/engine/EngineRecognizer' 3 | import engines from '@/engines' 4 | 5 | export default function localeCodeForChat(chat: Chat) { 6 | const engineObject: EngineRecognizer = engines[chat.engine] 7 | const language = 8 | chat.languages[chat.engine] || engineObject.defaultLanguageCode 9 | const languageObject = 10 | engineObject.languages.find((l) => l.code === language) || 11 | engineObject.languages.find( 12 | (l) => l.code === engineObject.defaultLanguageCode 13 | ) 14 | return languageObject.i18nCode 15 | } 16 | -------------------------------------------------------------------------------- /src/helpers/logAnswerTime.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | 3 | export default function logAnswerTime(ctx: Context, name: string) { 4 | console.info( 5 | `${name} answered in ${ 6 | (new Date().getTime() - ctx.timeReceived.getTime()) / 1000 7 | }s` 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/helpers/promoTexts.ts: -------------------------------------------------------------------------------- 1 | const promoTexts = { 2 | ru: () => 3 | 'Путин и его свита [убивают мирное население](https://t.me/verkhovnaradaofukraine) на войне в Украине #stopputin', 4 | en: () => 5 | 'Putin and his cronies [kill civilians](https://t.me/verkhovnaradaofukraine) in the war in Ukraine #stopputin', 6 | } 7 | 8 | export default promoTexts 9 | -------------------------------------------------------------------------------- /src/helpers/report.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import bot from '@/helpers/bot' 3 | 4 | const ignoredMessages = [ 5 | 'have no rights to send a message', 6 | 'You have exceeded the limit of 60 requests per minute for your app', 7 | "message can't be deleted", 8 | 'message is not modified', 9 | 'replied message not found', 10 | 'CHAT_WRITE_FORBIDDEN', 11 | 'message to edit not found', 12 | 'Exceeded max audio length of 20 seconds', 13 | 'Response code 404 (Not Found)', 14 | 'Too Many Requests: retry after', 15 | 'You have exceeded the limit of 240 requests', 16 | 'MESSAGE_ID_INVALID', 17 | 'bot was kicked from the supergroup chat', 18 | 'The project to be billed is associated with a delinquent billing account', 19 | 'need administrator rights', 20 | 'You have exceeded the limit of 180 requests', 21 | 'The project to be billed is associated with a closed billing account', 22 | 'wrong file_id or the file is temporarily unavailable', 23 | 'not enough rights to send text messages', 24 | 'bot was blocked by the user', 25 | 'bot was kicked from the group chat', 26 | 'bot is not a member of the supergroup chat', 27 | 'The project to be billed is associated with an absent billing', 28 | 'Bad Request: not Found', 29 | 'account not found', 30 | 'Request Entity Too Large', 31 | 'Does not contain any stream', 32 | 'ffprobe exited with code 1', 33 | 'Invalid duration specification for t', 34 | 'Timeout, please try again later', 35 | '504: Gateway Timeout', 36 | 'does not have storage.buckets.create access', 37 | 'bucket name is needed to use Cloud Storage', 38 | ] 39 | 40 | interface ExtraErrorInfo { 41 | ctx?: Context 42 | location?: string 43 | meta?: string 44 | } 45 | 46 | function constructErrorMessage( 47 | error: Error, 48 | { ctx, location, meta }: ExtraErrorInfo 49 | ) { 50 | const { message } = error 51 | const chatInfo = ctx ? [`Chat ${ctx.chat.id}`] : [] 52 | if (ctx && 'username' in ctx.chat) { 53 | chatInfo.push(`@${ctx.chat.username}`) 54 | } 55 | if (ctx && ctx.dbchat) { 56 | chatInfo.push(ctx.dbchat.engine) 57 | chatInfo.push(ctx.dbchat.languages[ctx.dbchat.engine]) 58 | } 59 | const result = `${ 60 | location ? `${escape(location)}${ctx ? '\n' : ''}` : '' 61 | }${chatInfo.filter((v) => !!v).join(', ')}\n${escape(message)}${ 62 | meta ? `${meta}\n` : '' 63 | }` 64 | return result 65 | } 66 | 67 | export default function report(error: Error, info: ExtraErrorInfo = {}) { 68 | console.log(error, info) 69 | } 70 | 71 | function escape(s: string) { 72 | return s.replace(//g, '>').replace(/&/g, '&') 73 | } 74 | -------------------------------------------------------------------------------- /src/helpers/sendStart.ts: -------------------------------------------------------------------------------- 1 | import Context from '@/models/Context' 2 | import logAnswerTime from '@/helpers/logAnswerTime' 3 | 4 | export default async function sendStart(ctx: Context) { 5 | await ctx.reply(ctx.i18n.t('start'), { 6 | parse_mode: 'Markdown', 7 | }) 8 | logAnswerTime(ctx, '/start') 9 | } 10 | -------------------------------------------------------------------------------- /src/helpers/startMongo.ts: -------------------------------------------------------------------------------- 1 | import { connect } from 'mongoose' 2 | 3 | export default function startMongo() { 4 | return connect(process.env.MONGO) 5 | } 6 | -------------------------------------------------------------------------------- /src/helpers/startWebhook.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express' 2 | import { ChatModel } from '@/models/Chat' 3 | import { stripe } from '@/helpers/stripe' 4 | 5 | export const webhookApp = express() 6 | const endpointSecret = process.env.STRIPE_WEBHOOK_SIGNING_SECRET 7 | 8 | webhookApp.post( 9 | '/', 10 | express.raw({ type: 'application/json' }), 11 | async (request, response) => { 12 | const sig = request.headers['stripe-signature'] 13 | 14 | let event 15 | 16 | try { 17 | event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret) 18 | } catch (err) { 19 | response.status(400).send(`Webhook Error: ${err.message}`) 20 | return 21 | } 22 | 23 | // Handle the event 24 | if (event.type === 'checkout.session.completed') { 25 | const chatId = +event.data.object.client_reference_id 26 | const chat = await ChatModel.findOne({ id: chatId }) 27 | if (chat) { 28 | chat.paid = true 29 | await chat.save() 30 | } 31 | } 32 | // Return a 200 response to acknowledge receipt of the event 33 | response.send() 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /src/helpers/stripe.ts: -------------------------------------------------------------------------------- 1 | import Stripe from 'stripe' 2 | 3 | export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { 4 | apiVersion: '2022-08-01', 5 | }) 6 | -------------------------------------------------------------------------------- /src/helpers/urlToText.ts: -------------------------------------------------------------------------------- 1 | import * as download from 'download' 2 | import * as temp from 'temp' 3 | import { Chat } from '@/models/Chat' 4 | import { Worker } from 'cluster' 5 | import { cpus } from 'os' 6 | import { v4 as uuid } from 'uuid' 7 | import { writeFileSync } from 'fs' 8 | import Cluster from '@/helpers/Cluster' 9 | import RecognitionResult from '@/helpers/engine/RecognitionResult' 10 | import RecognitionResultPart from '@/helpers/engine/RecognitionResultPart' 11 | import augmentError from '@/helpers/augmentError' 12 | import deleteFile from '@/helpers/deleteFile' 13 | import flac from '@/helpers/flac' 14 | import getTextFromAudio from '@/helpers/getTextFromAudio' 15 | 16 | const numCPUs = cpus().length 17 | 18 | interface WorkerMessage { 19 | url: string 20 | promiseId: string 21 | chat: Partial 22 | userId?: number 23 | forwardedFrom?: string 24 | } 25 | 26 | interface PrimaryMessage { 27 | textWithTimecodes?: RecognitionResultPart[] 28 | duration?: number 29 | promiseId: string 30 | errorString?: string 31 | } 32 | 33 | // Generate cluster workers 34 | const workers: Worker[] = [] 35 | if (Cluster.isPrimary) { 36 | console.info(`Primary ${process.pid} is running`) 37 | for (let i = 0; i < numCPUs; i += 1) { 38 | const worker = Cluster.fork() 39 | worker.on('message', primaryReceivesMessage) 40 | workers.push(worker) 41 | } 42 | } else { 43 | console.info(`Worker ${process.pid} started`) 44 | process.on('message', workerReceivesMessage) 45 | } 46 | 47 | // Called only from the primary 48 | const recognitionPromises: { 49 | [index: string]: { 50 | res: (value: RecognitionResult) => void 51 | rej: (error: Error) => void 52 | } 53 | } = {} 54 | let clusterNumber = 0 55 | export default function urlToText( 56 | url: string, 57 | chat: Partial, 58 | userId?: number, 59 | forwardedFrom?: string 60 | ) { 61 | if (clusterNumber >= workers.length) { 62 | clusterNumber = 0 63 | } 64 | const worker = workers[clusterNumber] 65 | clusterNumber += 1 66 | // Create promise and send the message to worker 67 | return new Promise((res, rej) => { 68 | const promiseId = uuid() 69 | recognitionPromises[promiseId] = { res, rej } 70 | const workerMessage: WorkerMessage = { 71 | url, 72 | promiseId, 73 | chat, 74 | userId, 75 | forwardedFrom, 76 | } 77 | worker.send(workerMessage) 78 | }) 79 | } 80 | 81 | async function workerReceivesMessage({ 82 | url, 83 | promiseId, 84 | chat, 85 | userId, 86 | forwardedFrom, 87 | }: WorkerMessage) { 88 | let primaryMessage: PrimaryMessage 89 | try { 90 | const result = await convert(url, chat, userId, forwardedFrom) 91 | primaryMessage = { ...result, promiseId } 92 | } catch (error) { 93 | primaryMessage = { 94 | errorString: error.message, 95 | promiseId, 96 | } 97 | } 98 | process.send(primaryMessage) 99 | } 100 | 101 | function primaryReceivesMessage({ 102 | textWithTimecodes, 103 | duration, 104 | promiseId, 105 | errorString, 106 | }: PrimaryMessage) { 107 | // Get promise functions 108 | const promiseFunctions = recognitionPromises[promiseId] 109 | // Log message received 110 | if (promiseFunctions) { 111 | if (errorString) { 112 | promiseFunctions.rej(new Error(errorString)) 113 | } else { 114 | promiseFunctions.res({ textWithTimecodes, duration }) 115 | } 116 | delete recognitionPromises[promiseId] 117 | } 118 | } 119 | 120 | async function convert( 121 | url: string, 122 | chat: Partial, 123 | userId?: number, 124 | forwardedFrom?: string 125 | ) { 126 | let ogaPath: string 127 | let flacPath: string 128 | try { 129 | // Download audio file 130 | ogaPath = temp.path({ suffix: '.oga' }) 131 | try { 132 | const data = await download(url) 133 | writeFileSync(ogaPath, data) 134 | } catch (error) { 135 | throw augmentError(error, 'download url') 136 | } 137 | // Convert audio file to flac 138 | let duration: number 139 | try { 140 | const result = await flac(ogaPath) 141 | flacPath = result.flacPath 142 | duration = result.duration 143 | } catch (error) { 144 | throw augmentError(error, 'convert to flac') 145 | } 146 | // Get transcription 147 | const textWithTimecodes = await getTextFromAudio( 148 | flacPath, 149 | chat, 150 | duration, 151 | ogaPath, 152 | userId, 153 | forwardedFrom 154 | ) 155 | // Return result 156 | return { 157 | textWithTimecodes, 158 | duration, 159 | } 160 | } catch (error) { 161 | throw augmentError(error, 'transcribe audio') 162 | } finally { 163 | deleteFile(flacPath) 164 | deleteFile(ogaPath) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/middlewares/adminLock.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | import report from '@/helpers/report' 4 | 5 | export default async function checkAdminLock(ctx: Context, next: NextFunction) { 6 | // If not admin locked or a channel post, continue 7 | if (!ctx.dbchat.adminLocked || !!ctx.channelPost) { 8 | return next() 9 | } 10 | // Check if from anonymous admin 11 | if (ctx.from.username === 'GroupAnonymousBot') { 12 | return next() 13 | } 14 | const member = await ctx.getChatMember(ctx.from.id) 15 | if (['creator', 'administrator'].includes(member.status)) { 16 | return next() 17 | } else { 18 | try { 19 | await ctx.deleteMessage() 20 | } catch (err) { 21 | report(err, { ctx }) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/middlewares/attachChat.ts: -------------------------------------------------------------------------------- 1 | import { ChatModel } from '@/models/Chat' 2 | import { NextFunction } from 'grammy' 3 | import Context from '@/models/Context' 4 | 5 | export default async function attachChat(ctx: Context, next: NextFunction) { 6 | if (!ctx.chat?.id) { 7 | return 8 | } 9 | const { doc } = await ChatModel.findOrCreate({ id: ctx.chat.id }) 10 | ctx.dbchat = doc 11 | return next() 12 | } 13 | -------------------------------------------------------------------------------- /src/middlewares/checkDocumentType.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | export default function checkDocumentType(ctx: Context, next: NextFunction) { 5 | const file = ctx.msg?.document || ctx.msg?.audio 6 | if (!file) { 7 | return 8 | } 9 | const mime = file.mime_type 10 | const allowedMimeTypes = ['audio', 'octet-stream'] 11 | for (const allowedType of allowedMimeTypes) { 12 | if (mime.includes(allowedType)) { 13 | return next() 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/middlewares/checkFilesBanned.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | export default function checkFilesBanned(ctx: Context, next: NextFunction) { 5 | if (ctx.dbchat.filesBanned) { 6 | return 7 | } 8 | return next() 9 | } 10 | -------------------------------------------------------------------------------- /src/middlewares/checkSuperAdmin.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | const superAdminId = +process.env.ADMIN_ID 5 | export default function checkSuperAdmin(ctx: Context, next: NextFunction) { 6 | if (ctx.from.id !== superAdminId) { 7 | return 8 | } 9 | return next() 10 | } 11 | -------------------------------------------------------------------------------- /src/middlewares/configureI18n.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | import engines from '@/engines' 4 | 5 | export default function configureI18n(ctx: Context, next: NextFunction) { 6 | const chat = ctx.dbchat 7 | const engine = engines[chat.engine] 8 | const chatLanguage = chat.languages[chat.engine] || engine.defaultLanguageCode 9 | const language = engine.languages.find((l) => l.code === chatLanguage) 10 | ctx.i18n.locale(language?.i18nCode) 11 | return next() 12 | } 13 | -------------------------------------------------------------------------------- /src/middlewares/countMessage.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | import incrementMessageCount from '@/helpers/incrementMessageCount' 4 | 5 | export default function countMessage(_: Context, next: NextFunction) { 6 | incrementMessageCount() 7 | return next() 8 | } 9 | -------------------------------------------------------------------------------- /src/middlewares/disallowPrivate.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | export default function disallowPrivate(ctx: Context, next: NextFunction) { 5 | if (ctx.chat.type === 'private') { 6 | return ctx.reply(ctx.i18n.t('error_group'), { 7 | parse_mode: 'Markdown', 8 | }) 9 | } 10 | return next() 11 | } 12 | -------------------------------------------------------------------------------- /src/middlewares/ignoreOldMessageUpdates.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import Context from '@/models/Context' 3 | 4 | const threshold = 5 * 60 // 5 minutes 5 | export default function ignoreOldMessageUpdates( 6 | ctx: Context, 7 | next: NextFunction 8 | ) { 9 | // Check if context update type is a message 10 | if (ctx.message) { 11 | if (new Date().getTime() / 1000 - ctx.message.date < threshold) { 12 | return next() 13 | } else { 14 | console.log( 15 | `Ignoring message from ${ctx.from.id} at ${ctx.chat.id} (${ 16 | new Date().getTime() / 1000 17 | }:${ctx.message.date})` 18 | ) 19 | } 20 | } else { 21 | return next() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/middlewares/recordTimeReceived.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction } from 'grammy' 2 | import { appendFile } from 'fs' 3 | import Context from '@/models/Context' 4 | 5 | function recordDate(ctx: Context) { 6 | if (!ctx.update.message) { 7 | return 8 | } 9 | appendFile( 10 | `${__dirname}/../../updates.log`, 11 | `\n${Math.floor(Date.now() / 1000)} — ${ctx.update.update_id} — ${ 12 | Math.floor(Date.now() / 1000) - ctx.update.message.date 13 | }s`, 14 | (err) => { 15 | if (err) { 16 | console.error(err) 17 | } 18 | } 19 | ) 20 | } 21 | 22 | export default function recordTimeReceived(ctx: Context, next: NextFunction) { 23 | ctx.timeReceived = new Date() 24 | recordDate(ctx) 25 | return next() 26 | } 27 | -------------------------------------------------------------------------------- /src/models/Chat.ts: -------------------------------------------------------------------------------- 1 | import * as findorcreate from 'mongoose-findorcreate' 2 | import { FindOrCreate } from '@typegoose/typegoose/lib/defaultClasses' 3 | import { Schema } from 'mongoose' 4 | import { 5 | Severity, 6 | getModelForClass, 7 | modelOptions, 8 | plugin, 9 | prop, 10 | } from '@typegoose/typegoose' 11 | import Engine from '@/helpers/engine/Engine' 12 | 13 | type Languages = Map 14 | 15 | @plugin(findorcreate) 16 | @modelOptions({ 17 | schemaOptions: { timestamps: true }, 18 | options: { allowMixed: Severity.ALLOW }, 19 | }) 20 | export class Chat extends FindOrCreate { 21 | @prop({ required: true, unique: true, index: true }) 22 | id: string 23 | @prop({ required: true, index: true, enum: Engine, default: Engine.wit }) 24 | engine: Engine 25 | @prop({ required: true, default: false }) 26 | adminLocked: boolean 27 | @prop({ required: true, default: false }) 28 | silent: boolean 29 | @prop({ required: true, default: true }) 30 | filesBanned: boolean 31 | @prop({ required: true, default: true }) 32 | transcribeAllAudio: boolean 33 | @prop() 34 | googleSetupMessageId?: number 35 | @prop() 36 | googleKey?: string 37 | @prop() 38 | witToken?: string 39 | @prop({ required: true, default: false }) 40 | timecodesEnabled: boolean 41 | @prop() 42 | lastVoiceMessageSentAt?: Date 43 | @prop({ 44 | required: true, 45 | type: Schema.Types.Mixed, 46 | default: { 47 | ashmanov: 'ru', 48 | google: 'en-US', 49 | wit: 'English', 50 | platinumfund: 'English', 51 | }, 52 | }) 53 | languages: Languages 54 | @prop({ required: true, default: false }) 55 | paid: boolean 56 | @prop({ required: true, default: false }) 57 | banned: boolean 58 | @prop({ required: true, default: 0 }) 59 | freeVoicesUsed: number 60 | } 61 | 62 | export const ChatModel = getModelForClass(Chat) 63 | -------------------------------------------------------------------------------- /src/models/Context.ts: -------------------------------------------------------------------------------- 1 | import { Context as BaseContext } from 'grammy' 2 | import { Chat } from '@/models/Chat' 3 | import { DocumentType } from '@typegoose/typegoose' 4 | import { I18nContext } from '@grammyjs/i18n/dist/source' 5 | 6 | export default interface Context extends BaseContext { 7 | readonly i18n: I18nContext 8 | dbchat: DocumentType 9 | timeReceived: Date 10 | } 11 | -------------------------------------------------------------------------------- /src/models/MessageStats.ts: -------------------------------------------------------------------------------- 1 | import * as findorcreate from 'mongoose-findorcreate' 2 | import { FindOrCreate } from '@typegoose/typegoose/lib/defaultClasses' 3 | import { 4 | getModelForClass, 5 | modelOptions, 6 | plugin, 7 | prop, 8 | } from '@typegoose/typegoose' 9 | 10 | @plugin(findorcreate) 11 | @modelOptions({ schemaOptions: { timestamps: true } }) 12 | export class MessageStats extends FindOrCreate { 13 | @prop({ required: true, index: true }) 14 | date: Date 15 | @prop({ required: true, default: 0 }) 16 | count: number 17 | } 18 | 19 | export const MessageStatsModel = getModelForClass(MessageStats) 20 | -------------------------------------------------------------------------------- /src/models/PromoException.ts: -------------------------------------------------------------------------------- 1 | import { getModelForClass, modelOptions, prop } from '@typegoose/typegoose' 2 | 3 | @modelOptions({ schemaOptions: { timestamps: true } }) 4 | export class PromoException { 5 | @prop({ required: true, index: true, unique: true }) 6 | id: number 7 | } 8 | 9 | export const PromoExceptionModel = getModelForClass(PromoException) 10 | 11 | export const promoExceptions = [] as number[] 12 | async function populatePromoExceptions() { 13 | const dbPromoExceptions = await PromoExceptionModel.find({}) 14 | dbPromoExceptions.forEach((promoException) => { 15 | promoExceptions.push(promoException.id) 16 | }) 17 | } 18 | void populatePromoExceptions() 19 | 20 | export async function addPromoException(id: number) { 21 | if (promoExceptions.includes(id)) { 22 | return 23 | } 24 | await PromoExceptionModel.create({ id }) 25 | promoExceptions.push(id) 26 | } 27 | -------------------------------------------------------------------------------- /src/models/Voice.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from '@/models/Chat' 2 | import { 3 | Severity, 4 | getModelForClass, 5 | modelOptions, 6 | prop, 7 | } from '@typegoose/typegoose' 8 | import Engine from '@/helpers/engine/Engine' 9 | import RecognitionResultPart from '@/helpers/engine/RecognitionResultPart' 10 | import engines from '@/engines' 11 | 12 | @modelOptions({ 13 | schemaOptions: { timestamps: true }, 14 | options: { allowMixed: Severity.ALLOW }, 15 | }) 16 | export class Voice { 17 | @prop({ required: true }) 18 | url: string 19 | @prop({ required: true, enum: Engine, default: Engine.wit }) 20 | engine: Engine 21 | @prop({ required: true }) 22 | duration: number 23 | @prop({ required: true }) 24 | language: string 25 | @prop() 26 | text?: string 27 | @prop() 28 | textWithTimecodes?: string[][] 29 | @prop() 30 | file?: string 31 | } 32 | 33 | export const VoiceModel = getModelForClass(Voice) 34 | 35 | export function addVoice({ 36 | url, 37 | chat, 38 | duration, 39 | textWithTimecodes, 40 | fileId, 41 | }: { 42 | url: string 43 | chat: Chat 44 | duration: number 45 | textWithTimecodes: RecognitionResultPart[] 46 | fileId: string 47 | }) { 48 | const language = 49 | chat.languages[chat.engine] || engines[chat.engine].defaultLanguageCode 50 | return VoiceModel.create({ 51 | url, 52 | text: textWithTimecodes.reduce((p, c) => `${p} ${c.text}`, ''), 53 | language, 54 | duration, 55 | engine: chat.engine, 56 | textWithTimecodes: textWithTimecodes.map((v) => [v.timeCode, v.text]), 57 | fileId, 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "lib": ["es2015"], 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "outDir": "dist", 9 | "baseUrl": "src", 10 | "paths": { 11 | "@/*": ["*"] 12 | }, 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true 15 | }, 16 | "include": ["src/**/*"] 17 | } 18 | --------------------------------------------------------------------------------