initial
This commit is contained in:
commit
8ea10bafc3
36 changed files with 17026 additions and 0 deletions
6
.dockerignore
Normal file
6
.dockerignore
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
dist
|
||||||
|
.env
|
||||||
25
.eslintrc.js
Normal file
25
.eslintrc.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
};
|
||||||
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
63
Dockerfile
Normal file
63
Dockerfile
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
###################
|
||||||
|
# BUILD FOR LOCAL DEVELOPMENT
|
||||||
|
###################
|
||||||
|
|
||||||
|
FROM --platform=linux/amd64 node:18-alpine As development
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Copy application dependency manifests to the container image.
|
||||||
|
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
|
||||||
|
# Copying this first prevents re-running npm install on every code change.
|
||||||
|
COPY --chown=node:node package*.json ./
|
||||||
|
|
||||||
|
# Install app dependencies using the `npm ci` command instead of `npm install`
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Bundle app source
|
||||||
|
COPY --chown=node:node . .
|
||||||
|
|
||||||
|
# Use the node user from the image (instead of the root user)
|
||||||
|
USER node
|
||||||
|
|
||||||
|
###################
|
||||||
|
# BUILD FOR PRODUCTION
|
||||||
|
###################
|
||||||
|
|
||||||
|
FROM --platform=linux/amd64 node:18-alpine As build
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY --chown=node:node package*.json ./
|
||||||
|
|
||||||
|
# In order to run `npm run build` we need access to the Nest CLI which is a dev dependency. In the previous development stage we ran `npm ci` which installed all dependencies, so we can copy over the node_modules directory from the development image
|
||||||
|
COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules
|
||||||
|
|
||||||
|
COPY --chown=node:node . .
|
||||||
|
|
||||||
|
# Run the build command which creates the production bundle
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Set NODE_ENV environment variable
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
# Running `npm ci` removes the existing node_modules directory and passing in --only=production ensures that only the production dependencies are installed. This ensures that the node_modules directory is as optimized as possible
|
||||||
|
RUN npm ci --only=production && npm cache clean --force
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
###################
|
||||||
|
# PRODUCTION
|
||||||
|
###################
|
||||||
|
|
||||||
|
FROM --platform=linux/amd64 node:18-alpine As production
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Copy the bundled code from the build stage to the production image
|
||||||
|
COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules
|
||||||
|
COPY --chown=node:node --from=build /usr/src/app/dist ./dist
|
||||||
|
|
||||||
|
# Start the server using the production build
|
||||||
|
CMD [ "node", "dist/main.js" ]
|
||||||
73
README.md
Normal file
73
README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<p align="center">
|
||||||
|
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||||
|
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||||
|
|
||||||
|
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||||
|
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||||
|
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||||
|
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||||
|
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||||
|
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||||
|
</p>
|
||||||
|
<!--[](https://opencollective.com/nest#backer)
|
||||||
|
[](https://opencollective.com/nest#sponsor)-->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# development
|
||||||
|
$ npm run start
|
||||||
|
|
||||||
|
# watch mode
|
||||||
|
$ npm run start:dev
|
||||||
|
|
||||||
|
# production mode
|
||||||
|
$ npm run start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# unit tests
|
||||||
|
$ npm run test
|
||||||
|
|
||||||
|
# e2e tests
|
||||||
|
$ npm run test:e2e
|
||||||
|
|
||||||
|
# test coverage
|
||||||
|
$ npm run test:cov
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
|
||||||
|
## Stay in touch
|
||||||
|
|
||||||
|
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||||
|
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||||
|
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Nest is [MIT licensed](LICENSE).
|
||||||
8
nest-cli.json
Normal file
8
nest-cli.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
15857
package-lock.json
generated
Normal file
15857
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
76
package.json
Normal file
76
package.json
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"name": "tgd-telegram-service",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"private": true,
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^10.0.0",
|
||||||
|
"@nestjs/config": "^3.1.1",
|
||||||
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/cqrs": "^10.2.6",
|
||||||
|
"@nestjs/microservices": "^10.2.8",
|
||||||
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
|
"amqp-connection-manager": "^4.1.14",
|
||||||
|
"amqplib": "^0.10.3",
|
||||||
|
"nestjs-telegraf": "^2.7.0",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rxjs": "^7.8.1",
|
||||||
|
"telegraf": "^4.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
"@nestjs/schematics": "^10.0.0",
|
||||||
|
"@nestjs/testing": "^10.0.0",
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/jest": "^29.5.2",
|
||||||
|
"@types/node": "^20.3.1",
|
||||||
|
"@types/supertest": "^2.0.12",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
"eslint": "^8.42.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
|
"supertest": "^6.3.3",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-loader": "^9.4.3",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"tsconfig-paths": "^4.2.0",
|
||||||
|
"typescript": "^5.1.3"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app.controller.spec.ts
Normal file
22
src/app.controller.spec.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
describe('AppController', () => {
|
||||||
|
let appController: AppController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const app: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
appController = app.get<AppController>(AppController);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('root', () => {
|
||||||
|
it('should return "Hello World!"', () => {
|
||||||
|
expect(appController.getHello()).toBe('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
12
src/app.controller.ts
Normal file
12
src/app.controller.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello(): string {
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/app.module.ts
Normal file
25
src/app.module.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import {ConfigModule, ConfigService} from "@nestjs/config";
|
||||||
|
import {TelegrafModule} from "nestjs-telegraf";
|
||||||
|
import {BotModule} from "./bot/bot.module";
|
||||||
|
import {sessionMiddleware} from "./middleware/session.middleware";
|
||||||
|
import {MessageController} from "./message.controller";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigModule.forRoot(),
|
||||||
|
BotModule,
|
||||||
|
TelegrafModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: (configService: ConfigService) => ({
|
||||||
|
token: configService.get<string>('TELEGRAM_TOKEN'),
|
||||||
|
include: [BotModule],
|
||||||
|
middlewares: [sessionMiddleware],
|
||||||
|
}),
|
||||||
|
inject: [ConfigService],
|
||||||
|
})],
|
||||||
|
controllers: [AppController, MessageController],
|
||||||
|
providers: [AppService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return 'TGH Game Server!';
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/bot/EchoService.ts
Normal file
25
src/bot/EchoService.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectBot } from 'nestjs-telegraf';
|
||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { ExtraPoll, ExtraReplyMessage } from 'telegraf/typings/telegram-types';
|
||||||
|
import { Messages } from './tg.text';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EchoService {
|
||||||
|
constructor(
|
||||||
|
@InjectBot() private bot: Telegraf,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async test(chatId: number) {
|
||||||
|
await this.bot.telegram.sendMessage(chatId, 'test');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async enterQuiz(chatId: number) {
|
||||||
|
const extra: ExtraReplyMessage = {};
|
||||||
|
await this.bot.telegram.sendMessage(chatId, '🥇 Да начнется битва!\n', {
|
||||||
|
reply_markup: {
|
||||||
|
keyboard: [[{ text: Messages.GO }]],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/bot/bot.module.ts
Normal file
65
src/bot/bot.module.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import {BotUpdate} from './bot.update';
|
||||||
|
import {Module} from '@nestjs/common';
|
||||||
|
import {RegisterScene} from './scenes/register.scene';
|
||||||
|
import {RegisterNamePrompt} from './scenes/register.name.prompt';
|
||||||
|
import {EchoService} from './EchoService';
|
||||||
|
import {QuizScene} from './scenes/quiz.scene';
|
||||||
|
import {RegisterPhotoScene} from './scenes/register.photo.scene';
|
||||||
|
import {GlobalCommands} from './global-commands';
|
||||||
|
import {ClientProxyFactory, Transport} from '@nestjs/microservices';
|
||||||
|
import * as process from "process";
|
||||||
|
import {ConfigModule} from "@nestjs/config";
|
||||||
|
import AppConsts from "../constants";
|
||||||
|
|
||||||
|
const cmdHandles = [
|
||||||
|
//TgPostCardsToUserCommandHandler,
|
||||||
|
//TgCardSelectionSceneCommandHandler,
|
||||||
|
//RemoveCardFromUserCommandHandler,
|
||||||
|
];
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
BotUpdate,
|
||||||
|
RegisterScene,
|
||||||
|
RegisterNamePrompt,
|
||||||
|
EchoService,
|
||||||
|
QuizScene,
|
||||||
|
RegisterPhotoScene,
|
||||||
|
GlobalCommands,
|
||||||
|
{
|
||||||
|
provide: AppConsts.GameServiceName,
|
||||||
|
useFactory: () =>
|
||||||
|
ClientProxyFactory.create({
|
||||||
|
transport: Transport.RMQ,
|
||||||
|
options: {
|
||||||
|
urls: [process.env.RMQ_URL],
|
||||||
|
queue: process.env.RMQ_OUTBOX_Q,
|
||||||
|
queueOptions: {
|
||||||
|
durable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AppConsts.PhotoServiceName,
|
||||||
|
useFactory: () =>
|
||||||
|
ClientProxyFactory.create({
|
||||||
|
transport: Transport.RMQ,
|
||||||
|
options: {
|
||||||
|
urls: [process.env.RMQ_URL],
|
||||||
|
queue: process.env.RMQ_PHOTOS_Q,
|
||||||
|
queueOptions: {
|
||||||
|
durable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
...cmdHandles,
|
||||||
|
],
|
||||||
|
exports: [EchoService],
|
||||||
|
})
|
||||||
|
export class BotModule {}
|
||||||
130
src/bot/bot.update.ts
Normal file
130
src/bot/bot.update.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
Ctx,
|
||||||
|
Hears,
|
||||||
|
Start,
|
||||||
|
Update,
|
||||||
|
Sender,
|
||||||
|
Help,
|
||||||
|
On, Message,
|
||||||
|
} from 'nestjs-telegraf';
|
||||||
|
import { UpdateType as TelegrafUpdateType } from 'telegraf/typings/telegram-types';
|
||||||
|
import { Context } from './context.interface';
|
||||||
|
import { ExtraReplyMessage } from 'telegraf/typings/telegram-types';
|
||||||
|
import { Markup } from 'telegraf';
|
||||||
|
import {
|
||||||
|
QUIZ_SCENE,
|
||||||
|
REGISTER_PHOTO_SCENE,
|
||||||
|
REGISTER_SCENE_ID,
|
||||||
|
} from './scenes/scenes.const';
|
||||||
|
import { Messages } from './tg.text';
|
||||||
|
import { GlobalCommands } from './global-commands';
|
||||||
|
import {Inject, Logger} from "@nestjs/common";
|
||||||
|
import AppConsts from "../constants";
|
||||||
|
import {ClientProxy} from "@nestjs/microservices";
|
||||||
|
import {catchError} from "rxjs";
|
||||||
|
|
||||||
|
@Update()
|
||||||
|
export class BotUpdate {
|
||||||
|
readonly numbers = Messages.answerNumbers;
|
||||||
|
private readonly logger = new Logger(BotUpdate.name);
|
||||||
|
constructor(
|
||||||
|
@Inject(AppConsts.GameServiceName) private gameService: ClientProxy,
|
||||||
|
private globalCmd: GlobalCommands,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Start()
|
||||||
|
async startCommand(@Ctx() ctx: Context) {
|
||||||
|
this.logger.verbose(`Sending GuestInfo to MQTT for ${ctx.message.from.first_name} / ${ctx.message.from.id}`);
|
||||||
|
this.gameService.send({cmd: 'GuestInfo'}, {user: ctx.from.id})
|
||||||
|
.pipe(catchError((val) => {
|
||||||
|
console.log(val);
|
||||||
|
return 'Error';
|
||||||
|
}),)
|
||||||
|
.subscribe(async (result) => {
|
||||||
|
if (result) {
|
||||||
|
await ctx.reply(
|
||||||
|
`🤟 Все путем, ты уже зарегистрирован, расслабься и жди указаний\r\nМожет быть`,
|
||||||
|
);
|
||||||
|
this.globalCmd.printCommands(ctx);
|
||||||
|
} else {
|
||||||
|
let reply = `👋 Привет, ${ctx.message.from.first_name}\\!\r\n`;
|
||||||
|
reply +=
|
||||||
|
'Я не вижу тебя в списке зарегистрированных участников, пройдем регистрацию?';
|
||||||
|
await ctx.replyWithMarkdownV2(reply, {
|
||||||
|
reply_markup: {
|
||||||
|
keyboard: [[{text: Messages.IM_IN}]],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Hears(Messages.IM_IN)
|
||||||
|
async onRegisterCommand(@Ctx() ctx: Context): Promise<void> {
|
||||||
|
await ctx.scene.enter(REGISTER_SCENE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hears(Messages.GO)
|
||||||
|
async onGoCommand(@Ctx() ctx: Context) {
|
||||||
|
await ctx.scene.enter(QUIZ_SCENE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('photo')
|
||||||
|
async onPhotoCommand(@Ctx() ctx: Context) {
|
||||||
|
await ctx.scene.enter(REGISTER_PHOTO_SCENE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('cards')
|
||||||
|
async onCardCommand(@Ctx() ctx: Context) {
|
||||||
|
this.gameService.emit({ cmd: 'GetCards'}, { user: ctx.from.id, inline: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('next')
|
||||||
|
async onNextCommand(@Ctx() ctx: Context) {
|
||||||
|
if(ctx.from.id === 11178819) {
|
||||||
|
this.gameService.emit({ cmd: 'CompleteQueue'}, { user: ctx.from.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('callback_query')
|
||||||
|
async onInlineQuery(@Ctx() ctx: Context) {
|
||||||
|
console.log('callback query');
|
||||||
|
console.log(ctx.callbackQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hears(Messages.CHANGE_PHOTO)
|
||||||
|
onChangePhoto(@Ctx() ctx: Context) {
|
||||||
|
ctx.scene.enter(REGISTER_PHOTO_SCENE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('text')
|
||||||
|
async onMsg(@Message('text') msg: string, @Ctx() ctx: Context) {
|
||||||
|
const chars = [...msg];
|
||||||
|
if (['1', '2', '3', '4'].includes(chars[0])) {
|
||||||
|
ctx.scene.enter(QUIZ_SCENE, { answering: true }, false);
|
||||||
|
}
|
||||||
|
if (msg.includes(Messages.EMOJI_CARD)) {
|
||||||
|
ctx.scene.enter(QUIZ_SCENE, { answering: true}, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
@Help()
|
||||||
|
async helpCommand(@Ctx() ctx: Context) {
|
||||||
|
await ctx.reply('ты пидор');
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('sticker')
|
||||||
|
async onSticker(@Ctx() ctx: Context) {
|
||||||
|
console.log(ctx.message.from);
|
||||||
|
await ctx.reply('👍');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* import { CommandBus, CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { RemoveCardFromUserCommand } from '../../game/commands/remove-card-from-user.command';
|
||||||
|
import { GuestsService } from '../../guests/guests.service';
|
||||||
|
import { SharedService } from '../../shared/shared.service';
|
||||||
|
import { PostCardsToUserCommand } from '../../game/commands/post-cards-to-user.command';
|
||||||
|
import { InjectBot } from 'nestjs-telegraf';
|
||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { Messages } from '../tg.text';
|
||||||
|
|
||||||
|
@CommandHandler(RemoveCardFromUserCommand)
|
||||||
|
export class RemoveCardFromUserCommandHandler implements ICommandHandler<RemoveCardFromUserCommand> {
|
||||||
|
constructor(
|
||||||
|
@InjectBot() private bot: Telegraf,
|
||||||
|
private guestService: GuestsService,
|
||||||
|
private sharedService: SharedService,
|
||||||
|
private cmdBus: CommandBus,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(command: RemoveCardFromUserCommand): Promise<any> {
|
||||||
|
const guest = await this.guestService.findById(command.telegramId);
|
||||||
|
const data = await this.sharedService.getConfig(`buttons_${command.telegramId}`);
|
||||||
|
const extra = {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: false,
|
||||||
|
keyboard: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const buttons = JSON.parse(data.value);
|
||||||
|
let found = false;
|
||||||
|
buttons.reply_markup.keyboard.forEach((item) => {
|
||||||
|
if (item[0].text.includes(command.card.description) && !found) {
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
extra.reply_markup.keyboard.push(
|
||||||
|
[ { ...item[0] } ]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (extra.reply_markup.keyboard.length === 0) {
|
||||||
|
extra.reply_markup.remove_keyboard = true;
|
||||||
|
}
|
||||||
|
await this.sharedService.setConfig(`buttons_${command.telegramId}`, JSON.stringify(extra));
|
||||||
|
await this.bot.telegram.sendMessage(
|
||||||
|
guest.chatId,
|
||||||
|
Messages.SELECT_CARD,
|
||||||
|
extra,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* import { CommandBus, CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { CardSelectionTimeExceedCommand } from '../../game/commands/card-selection-time-exceed.command';
|
||||||
|
import { Timeout } from '@nestjs/schedule';
|
||||||
|
import { TGD_Config } from '../../../app.config';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
import { HideKeyboardCommand } from '../../game/commands/hide-keyboard.command';
|
||||||
|
|
||||||
|
@CommandHandler(CardSelectionTimeExceedCommand)
|
||||||
|
export class TgCardSelectionSceneCommandHandler implements ICommandHandler<CardSelectionTimeExceedCommand> {
|
||||||
|
private logger = new Logger(TgCardSelectionSceneCommandHandler.name);
|
||||||
|
constructor(private cmdBus: CommandBus) {
|
||||||
|
}
|
||||||
|
execute(command: CardSelectionTimeExceedCommand): Promise<any> {
|
||||||
|
this.logger.verbose(`Timeout of selecting cards`);
|
||||||
|
return this.cmdBus.execute(
|
||||||
|
new HideKeyboardCommand('Время выбора карты истекло'),
|
||||||
|
);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { InjectBot } from 'nestjs-telegraf';
|
||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { Messages } from '../tg.text';
|
||||||
|
/*
|
||||||
|
@CommandHandler(PostCardsToUserCommand)
|
||||||
|
export class TgPostCardsToUserCommandHandler implements ICommandHandler<PostCardsToUserCommand> {
|
||||||
|
constructor(
|
||||||
|
@InjectBot() private bot: Telegraf,
|
||||||
|
private sharedService: SharedService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
|
||||||
|
async execute(command: PostCardsToUserCommand): Promise<any> {
|
||||||
|
const extra = {
|
||||||
|
reply_markup: {
|
||||||
|
keyboard: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (command.cards.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
command.cards.forEach((card) => {
|
||||||
|
extra.reply_markup.keyboard.push([
|
||||||
|
{ text: Messages.EMOJI_CARD + ' ' + card },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
await this.sharedService.setConfig(`buttons_${command.chatId}`,
|
||||||
|
JSON.stringify(extra),
|
||||||
|
);
|
||||||
|
return this.bot.telegram.sendMessage(
|
||||||
|
command.chatId,
|
||||||
|
Messages.SELECT_CARD,
|
||||||
|
extra,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
4
src/bot/context.interface.ts
Normal file
4
src/bot/context.interface.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { Scenes } from 'telegraf';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface Context extends Scenes.SceneContext {}
|
||||||
23
src/bot/global-commands.ts
Normal file
23
src/bot/global-commands.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Context } from './context.interface';
|
||||||
|
import { Messages } from './tg.text';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
@Injectable()
|
||||||
|
export class GlobalCommands {
|
||||||
|
public printCommands(ctx: Context) {
|
||||||
|
ctx.replyWithMarkdown('ты хочешь:', {
|
||||||
|
reply_markup: {
|
||||||
|
keyboard: [
|
||||||
|
[{ text: Messages.CHANGE_PHOTO }],
|
||||||
|
[{ text: Messages.NOTHING_THANKS }],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public hideKeyboard(ctx: Context) {
|
||||||
|
ctx.replyWithMarkdown('-', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
98
src/bot/scenes/quiz.scene.ts
Normal file
98
src/bot/scenes/quiz.scene.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { Command, Ctx, Message, On, Scene, SceneEnter } from 'nestjs-telegraf';
|
||||||
|
import { QUIZ_SCENE } from './scenes.const';
|
||||||
|
import { GlobalCommands } from '../global-commands';
|
||||||
|
import {Inject, Logger} from '@nestjs/common';
|
||||||
|
import { Messages } from '../tg.text';
|
||||||
|
import {Context} from "../context.interface";
|
||||||
|
import AppConsts from "../../constants";
|
||||||
|
import {ClientProxy} from "@nestjs/microservices";
|
||||||
|
|
||||||
|
@Scene(QUIZ_SCENE)
|
||||||
|
export class QuizScene {
|
||||||
|
private readonly logger = new Logger(QuizScene.name);
|
||||||
|
constructor(
|
||||||
|
private globalCmd: GlobalCommands,
|
||||||
|
@Inject(AppConsts.GameServiceName) private gameService: ClientProxy,
|
||||||
|
) {}
|
||||||
|
@SceneEnter()
|
||||||
|
async onSceneEnter(@Ctx() ctx: Context, @Message('text') text: string) {
|
||||||
|
if (ctx.session.__scenes.state.hasOwnProperty('answering')) {
|
||||||
|
return this.onText(text, ctx);
|
||||||
|
}
|
||||||
|
await ctx.reply(
|
||||||
|
'Ответы на вопросы будут появляться туть, кто первый тот победил',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('leave')
|
||||||
|
async onLeaveScene(@Ctx() ctx: Context) {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('cards')
|
||||||
|
async onCardCommand(@Ctx() ctx: Context) {
|
||||||
|
this.logger.verbose(`cards command (quiz)`);
|
||||||
|
this.gameService.emit({ cmd: 'GetCards'}, { user: ctx.from.id, inline: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('next')
|
||||||
|
async onNextCommand(@Ctx() ctx: Context) {
|
||||||
|
if(ctx.from.id === 11178819) {
|
||||||
|
this.gameService.emit({ cmd: 'CompleteQueue'}, { user: ctx.from.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('start')
|
||||||
|
async onCommandStart(@Ctx() ctx: Context) {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
this.globalCmd.printCommands(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('callback_query')
|
||||||
|
async onInlineQuery(@Ctx() ctx: Context) {
|
||||||
|
this.gameService.emit({ cmd: 'ApplyDebuff'}, { ...ctx.callbackQuery, from: ctx.from.id });
|
||||||
|
this.logger.verbose(`emit callback for ${ctx.callbackQuery}`);
|
||||||
|
}
|
||||||
|
@On('text')
|
||||||
|
async onText(@Message('text') text: string, @Ctx() ctx: Context) {
|
||||||
|
console.log(text);
|
||||||
|
//console.log(JSON.stringify(ctx));
|
||||||
|
if(text.startsWith('/')) {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (text.includes(Messages.EMOJI_CARD)) {
|
||||||
|
this.gameService.emit({ cmd: 'CardPlayed'}, { text, user: ctx.message.from.id })
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (text.includes(Messages.EMOJI_PLAYER)) {
|
||||||
|
this.gameService.emit({
|
||||||
|
cmd: 'PLayerSelected'
|
||||||
|
}, {
|
||||||
|
text, user: ctx.message.from.id
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.logger.verbose(`Answer from: ${ctx.message.from.first_name}, validating`);
|
||||||
|
this.gameService.send(
|
||||||
|
{ cmd: 'ValidateAnswer'},
|
||||||
|
{ answer: text, user: ctx.message.from.id, name: ctx.message.from.first_name })
|
||||||
|
.subscribe(
|
||||||
|
(res) => {
|
||||||
|
if(res.valid) {
|
||||||
|
ctx.replyWithMarkdownV2('Верно', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.replyWithMarkdownV2('Ответ неверный', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/bot/scenes/register.name.prompt.ts
Normal file
40
src/bot/scenes/register.name.prompt.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { REGISTER_NAME_PROMPT_SCENE, REGISTER_PHOTO_SCENE } from "./scenes.const";
|
||||||
|
import { Command, Ctx, Hears, On, Scene, SceneEnter, SceneLeave } from "nestjs-telegraf";
|
||||||
|
import {Context} from "../context.interface";
|
||||||
|
import {Inject, Logger} from "@nestjs/common";
|
||||||
|
import AppConsts from "../../constants";
|
||||||
|
import {ClientProxy} from "@nestjs/microservices";
|
||||||
|
@Scene(REGISTER_NAME_PROMPT_SCENE)
|
||||||
|
export class RegisterNamePrompt {
|
||||||
|
private logger = new Logger(RegisterNamePrompt.name);
|
||||||
|
constructor(@Inject(AppConsts.GameServiceName) private gameService: ClientProxy) {
|
||||||
|
}
|
||||||
|
@On('message')
|
||||||
|
async onMessage(@Ctx() ctx: Context) {
|
||||||
|
const name = (<any>ctx.message).text;
|
||||||
|
this.logger.verbose(`Message from ${ctx.message.from.first_name} [Scene register]`);
|
||||||
|
this.gameService.send(
|
||||||
|
{ cmd: 'RegisterUser'},
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
telegramId: ctx.message.from.id,
|
||||||
|
chatId: ctx.chat.id
|
||||||
|
}).subscribe(async (result) => {
|
||||||
|
this.logger.verbose(`${ctx.message.from.first_name} User registered`);
|
||||||
|
await ctx.replyWithMarkdownV2('Охуенчик, добро пожаловать\\!', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await ctx.scene.enter(REGISTER_PHOTO_SCENE);
|
||||||
|
await ctx.reply(`Приятно познакомиться, ${name}!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SceneLeave()
|
||||||
|
async sceneLeave(@Ctx() ctx: Context)
|
||||||
|
{
|
||||||
|
await ctx.scene.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/bot/scenes/register.photo.scene.ts
Normal file
93
src/bot/scenes/register.photo.scene.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
Ctx,
|
||||||
|
InjectBot,
|
||||||
|
Message,
|
||||||
|
On,
|
||||||
|
Scene,
|
||||||
|
SceneEnter,
|
||||||
|
SceneLeave,
|
||||||
|
} from 'nestjs-telegraf';
|
||||||
|
import { REGISTER_PHOTO_SCENE } from './scenes.const';
|
||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { GlobalCommands } from '../global-commands';
|
||||||
|
import { Inject, Logger } from '@nestjs/common';
|
||||||
|
import { ClientProxy } from '@nestjs/microservices';
|
||||||
|
import { catchError } from 'rxjs';
|
||||||
|
import {Context} from "../context.interface";
|
||||||
|
import AppConsts from "../../constants";
|
||||||
|
|
||||||
|
|
||||||
|
@Scene(REGISTER_PHOTO_SCENE)
|
||||||
|
export class RegisterPhotoScene {
|
||||||
|
readonly logger = new Logger(RegisterPhotoScene.name);
|
||||||
|
constructor(
|
||||||
|
@InjectBot() private bot: Telegraf,
|
||||||
|
// private guestService: GuestsService,
|
||||||
|
// private sharedService: SharedService,
|
||||||
|
private globalCmd: GlobalCommands,
|
||||||
|
@Inject(AppConsts.PhotoServiceName) private photoQueue: ClientProxy,
|
||||||
|
@Inject(AppConsts.GameServiceName) private gameService: ClientProxy,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@SceneEnter()
|
||||||
|
async onSceneEnter(@Ctx() ctx: Context) {
|
||||||
|
await ctx.reply('давай, шли свою фотографию сучка');
|
||||||
|
this.globalCmd.hideKeyboard(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('document')
|
||||||
|
async onDocument(@Ctx() ctx: Context) {
|
||||||
|
this.logger.warn(
|
||||||
|
`${ctx.message.from.first_name} tried to use invalid photo`,
|
||||||
|
);
|
||||||
|
ctx.replyWithMarkdown(
|
||||||
|
'[](https://i.ytimg.com/vi/pd342TR6PCM/hqdefault.jpg) Неправильно, попробуй еще раз',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('photo')
|
||||||
|
async onPhoto(@Message('photo') photo, @Ctx() ctx: Context) {
|
||||||
|
const link = await this.bot.telegram.getFileLink(
|
||||||
|
photo[photo.length - 1].file_id,
|
||||||
|
);
|
||||||
|
const regexp = new RegExp(/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i);
|
||||||
|
if (!regexp.test(link.toString())) {
|
||||||
|
await ctx.reply('не тот формат, давай жипег');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await ctx.reply(`Ожидай, наша нейросеточка находит твою мородчку 😼`);
|
||||||
|
|
||||||
|
this.photoQueue
|
||||||
|
.send('parse-photo', {
|
||||||
|
user: ctx.message.from.id,
|
||||||
|
url: link.toString(),
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
catchError((val) => {
|
||||||
|
console.log(val);
|
||||||
|
return 'Error';
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe(async (r) => {
|
||||||
|
//throw new Error('not implemented');
|
||||||
|
// TODO: Send to GameService for updating image
|
||||||
|
// this.sharedService.sendSocketNotificationToAllClients(
|
||||||
|
// SocketEvents.PHOTOS_UPDATED_EVENT,
|
||||||
|
// {
|
||||||
|
// id: ctx.message.from.id,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
this.gameService.emit({ cmd: 'PhotoUpdated' }, { id: ctx.message.from.id })
|
||||||
|
await ctx.reply('Кажись все получилось, чекни на экране что все ок');
|
||||||
|
await this.onLeaveCommand(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('leave')
|
||||||
|
async onLeaveCommand(@Ctx() ctx: Context) {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/bot/scenes/register.scene.ts
Normal file
65
src/bot/scenes/register.scene.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { REGISTER_NAME_PROMPT_SCENE, REGISTER_PHOTO_SCENE, REGISTER_SCENE_ID } from "./scenes.const";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
Ctx,
|
||||||
|
Hears,
|
||||||
|
Scene,
|
||||||
|
SceneEnter,
|
||||||
|
SceneLeave,
|
||||||
|
} from 'nestjs-telegraf';
|
||||||
|
import { Context } from '../context.interface';
|
||||||
|
import { Markup } from 'telegraf';
|
||||||
|
import { Messages } from '../tg.text';
|
||||||
|
import AppConsts from "../../constants";
|
||||||
|
import {Inject} from "@nestjs/common";
|
||||||
|
import {ClientProxy} from "@nestjs/microservices";
|
||||||
|
@Scene(REGISTER_SCENE_ID)
|
||||||
|
export class RegisterScene {
|
||||||
|
constructor(@Inject(AppConsts.GameServiceName) private gameService: ClientProxy) {}
|
||||||
|
@SceneEnter()
|
||||||
|
onSceneEnter(@Ctx() ctx: Context) {
|
||||||
|
const reply = `Шалом-шалом ✋\r\n
|
||||||
|
Я могу тебя звать ${ctx.message.from.first_name}?
|
||||||
|
`;
|
||||||
|
ctx.reply(
|
||||||
|
reply,
|
||||||
|
Markup.keyboard([
|
||||||
|
Markup.button.text(Messages.THATS_ME),
|
||||||
|
Markup.button.text(Messages.NOT_ME),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hears(Messages.THATS_ME)
|
||||||
|
async onAgree(@Ctx() ctx: Context) {
|
||||||
|
this.gameService.send(
|
||||||
|
{ cmd: 'RegisterUser'},
|
||||||
|
{
|
||||||
|
name: ctx.message.from.first_name,
|
||||||
|
telegramId: ctx.message.from.id,
|
||||||
|
chatId: ctx.chat.id
|
||||||
|
}).subscribe(async (result) => {
|
||||||
|
await ctx.replyWithMarkdownV2('Охуенчик, добро пожаловать\\!', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await ctx.scene.enter(REGISTER_PHOTO_SCENE);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hears(Messages.NOT_ME)
|
||||||
|
async onDisagree(@Ctx() ctx: Context) {
|
||||||
|
await ctx.replyWithMarkdownV2('Тогда назови себя', {
|
||||||
|
reply_markup: {
|
||||||
|
remove_keyboard: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await ctx.scene.enter(REGISTER_NAME_PROMPT_SCENE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('leave')
|
||||||
|
async onLeaveCommand(ctx: Context): Promise<void> {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/bot/scenes/scenes.const.ts
Normal file
4
src/bot/scenes/scenes.const.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const REGISTER_SCENE_ID = 'REGISTER_SCENE';
|
||||||
|
export const REGISTER_NAME_PROMPT_SCENE = 'REGISTER_NAME_PROMPT_SCENE';
|
||||||
|
export const QUIZ_SCENE = 'QUIZ_SCENE';
|
||||||
|
export const REGISTER_PHOTO_SCENE = 'REGISTER_PHOTO_SCENE';
|
||||||
7
src/bot/send-message.interface.ts
Normal file
7
src/bot/send-message.interface.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {ExtraReplyMessage} from "telegraf/typings/telegram-types";
|
||||||
|
|
||||||
|
export interface SendMessageInterface {
|
||||||
|
chatId: number;
|
||||||
|
message: string;
|
||||||
|
extra: ExtraReplyMessage | undefined;
|
||||||
|
}
|
||||||
13
src/bot/tg.text.ts
Normal file
13
src/bot/tg.text.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
export class Messages {
|
||||||
|
static IM_IN = 'Я в деле 😎';
|
||||||
|
static THATS_ME = '🥸 Да, енто я';
|
||||||
|
static NOT_ME = '🙅 Нет, зови меня иначе';
|
||||||
|
static GO = 'Поехали';
|
||||||
|
static CHANGE_PHOTO = 'Залить фоточку новую';
|
||||||
|
static NOTHING_THANKS = 'Не, спасибо, ничего не надо';
|
||||||
|
static answerNumbers: string[] = ['1. ', '2. ', '3. ', '4. '];
|
||||||
|
static SELECT_CARD = 'Сыграть карту?';
|
||||||
|
static EMOJI_CARD = '🃏';
|
||||||
|
static EMOJI_PLAYER = '👤';
|
||||||
|
}
|
||||||
|
|
||||||
4
src/constants.ts
Normal file
4
src/constants.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default class AppConsts {
|
||||||
|
static GameServiceName = 'GameService';
|
||||||
|
static PhotoServiceName = 'tgd_photos';
|
||||||
|
}
|
||||||
31
src/main.ts
Normal file
31
src/main.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {NestFactory} from '@nestjs/core';
|
||||||
|
import {AppModule} from './app.module';
|
||||||
|
import {MicroserviceOptions, Transport} from "@nestjs/microservices";
|
||||||
|
import * as process from "process";
|
||||||
|
import {Logger} from "@nestjs/common";
|
||||||
|
import {ConfigService} from "@nestjs/config";
|
||||||
|
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
//const nestApp = await NestFactory.create(AppModule, {
|
||||||
|
// logger: new Logger(),
|
||||||
|
//});
|
||||||
|
//const configService = nestApp.get<ConfigService>(ConfigService);
|
||||||
|
//const rmq_url = configService.get<string>('RMQ_URL');
|
||||||
|
//const rmq_outbox_q = configService.get<string>('RMQ_OUTBOX_Q');
|
||||||
|
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
||||||
|
AppModule,
|
||||||
|
{
|
||||||
|
transport: Transport.RMQ,
|
||||||
|
options: {
|
||||||
|
urls: [process.env.RMQ_URL],
|
||||||
|
queue: process.env.RMQ_COMMANDS_OUTPUT_Q,
|
||||||
|
queueOptions: {
|
||||||
|
durable: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await app.listen();
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
37
src/message.controller.ts
Normal file
37
src/message.controller.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {Controller, Inject, Logger} from "@nestjs/common";
|
||||||
|
import {MessagePattern, Payload} from "@nestjs/microservices";
|
||||||
|
import {SendMessageInterface} from "./bot/send-message.interface";
|
||||||
|
import {InjectBot} from "nestjs-telegraf";
|
||||||
|
import {Telegraf} from "telegraf";
|
||||||
|
import { BotCommand } from "telegraf/typings/core/types/typegram";
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class MessageController {
|
||||||
|
private readonly logger = new Logger(MessageController.name);
|
||||||
|
constructor(@InjectBot() private bot: Telegraf) {
|
||||||
|
}
|
||||||
|
@MessagePattern({ cmd: 'SendMessage' } )
|
||||||
|
async sendMessage(@Payload() data: SendMessageInterface) {
|
||||||
|
this.logger.verbose(`SendMessage action ${JSON.stringify(data)}`);
|
||||||
|
await this.bot.telegram.sendMessage(data.chatId, data.message, data.extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MessagePattern({ cmd: 'SetCommands'})
|
||||||
|
async setCommands(@Payload() data: BotCommand[]) {
|
||||||
|
this.logger.log('update commands enter')
|
||||||
|
await this.bot.telegram.setMyCommands(data);
|
||||||
|
console.log(await this.bot.telegram.getMyCommands());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MessagePattern({ cmd: 'ResetCommands'})
|
||||||
|
async resetCommands(@Payload() data: null) {
|
||||||
|
this.logger.log('reset commands enter')
|
||||||
|
await this.bot.telegram.deleteMyCommands();
|
||||||
|
await this.bot.telegram.setMyCommands([{
|
||||||
|
command: 'start',
|
||||||
|
description: 'главное меню'
|
||||||
|
}]);
|
||||||
|
console.log(await this.bot.telegram.getMyCommands());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
src/middleware/session.middleware.ts
Normal file
3
src/middleware/session.middleware.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { session } from 'telegraf';
|
||||||
|
|
||||||
|
export const sessionMiddleware = session();
|
||||||
24
test/app.e2e-spec.ts
Normal file
24
test/app.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
|
describe('AppController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AppModule],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication();
|
||||||
|
await app.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/ (GET)', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
9
test/jest-e2e.json
Normal file
9
test/jest-e2e.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": ".e2e-spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
|
}
|
||||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "ES2021",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strictBindCallApply": false,
|
||||||
|
"forceConsistentCasingInFileNames": false,
|
||||||
|
"noFallthroughCasesInSwitch": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue