Добавление поддержки телеграм

This commit is contained in:
Pavel Gnedov 2022-08-17 04:36:05 +07:00
parent 392b030140
commit 908b927bca
10 changed files with 5872 additions and 2654 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"editor.tabSize": 2
}

View file

@ -1,7 +1,9 @@
{
"couchDb": {
"dbs": {
"changes": ""
}
"changes": "",
"userMetaInfo": "",
}
},
"telegramBotToken": ""
}

8361
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@
"@nestjs/serve-static": "^2.2.2",
"@nestjs/websockets": "^8.4.4",
"axios": "^0.27.2",
"cache-manager": "^4.1.0",
"handlebars": "^4.7.7",
"imap-simple": "^5.1.0",
"nano": "^10.0.0",
@ -37,12 +38,14 @@
"rimraf": "^3.0.2",
"rss-parser": "^3.12.0",
"rxjs": "^7.2.0",
"socket.io": "^4.4.1"
"socket.io": "^4.4.1",
"telegraf": "^4.8.6"
},
"devDependencies": {
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@types/cache-manager": "^4.0.1",
"@types/express": "^4.17.13",
"@types/jest": "27.4.0",
"@types/node": "^16.0.0",

View file

@ -3,7 +3,7 @@ import { RedmineIssuesCacheWriterService } from '@app/event-emitter/issue-cache-
import { EnhancerService } from '@app/event-emitter/issue-enhancers/enhancer.service';
import { TimestampEnhancer } from '@app/event-emitter/issue-enhancers/timestamps-enhancer';
import { MainController } from '@app/event-emitter/main/main.controller';
import { Logger, Module, OnModuleInit } from '@nestjs/common';
import { CacheModule, Logger, Module, OnModuleInit } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { switchMap, tap } from 'rxjs';
import { AppController } from './app.controller';
@ -18,6 +18,10 @@ import { StatusChangeNotificationsService } from './notifications/status-change-
import { ChangesCacheWriterService } from './changes-cache-writer/changes-cache-writer.service';
import { Issues } from '@app/event-emitter/couchdb-datasources/issues';
import { Users } from '@app/event-emitter/couchdb-datasources/users';
import { TelegramBotService } from './telegram-bot/telegram-bot.service';
import { TelegrafModule } from 'nestjs-telegraf';
import { UserMetaInfoService } from './user-meta-info/user-meta-info.service';
import { UserMetaInfo } from './couchdb-datasources/user-meta-info';
@Module({
imports: [
@ -25,6 +29,12 @@ import { Users } from '@app/event-emitter/couchdb-datasources/users';
config: configuration().redmineIssueEventEmitterConfig,
}),
ConfigModule.forRoot({ load: [configuration] }),
TelegrafModule.forRoot({
token: configuration().telegramBotToken,
}),
CacheModule.register({
isGlobal: true,
}),
],
controllers: [AppController, MainController],
providers: [
@ -36,6 +46,9 @@ import { Users } from '@app/event-emitter/couchdb-datasources/users';
Changes,
RedminePublicUrlConverter,
ChangesCacheWriterService,
TelegramBotService,
UserMetaInfoService,
UserMetaInfo,
],
})
export class AppModule implements OnModuleInit {
@ -56,6 +69,7 @@ export class AppModule implements OnModuleInit {
Issues.getDatasource();
Users.getDatasource();
Changes.getDatasource();
UserMetaInfo.getDatasource();
this.enhancerService.addEnhancer([
this.timestampEnhancer,

View file

@ -0,0 +1,35 @@
import { CouchDb } from '@app/event-emitter/couchdb-datasources/couchdb';
import { Injectable, Logger } from '@nestjs/common';
import nano from 'nano';
import { UserMetaInfoModel } from 'src/models/user-meta-info.model';
import configuration from '../configs/app';
const config = configuration();
@Injectable()
export class UserMetaInfo {
private static logger = new Logger(UserMetaInfo.name);
private static db = null;
static async getDatasource(): Promise<nano.DocumentScope<UserMetaInfoModel>> {
if (UserMetaInfo.db) {
return UserMetaInfo.db;
}
const n = CouchDb.getCouchDb();
const userMetaInfoDbName =
config.couchDb.dbs.userMetaInfo || 'user_meta_info';
const dbs = await n.db.list();
if (!dbs.includes(userMetaInfoDbName)) {
await n.db.create(userMetaInfoDbName);
}
UserMetaInfo.db = await n.db.use(userMetaInfoDbName);
UserMetaInfo.logger.log(
`Connected to user_meta_info db - ${userMetaInfoDbName}`,
);
return UserMetaInfo.db;
}
async getDatasource(): Promise<nano.DocumentScope<UserMetaInfoModel>> {
return await UserMetaInfo.getDatasource();
}
}

View file

@ -9,6 +9,8 @@ export type AppConfig = {
couchDb: {
dbs: {
changes: string;
userMetaInfo: string;
};
};
telegramBotToken: string;
};

View file

@ -0,0 +1,4 @@
export type UserMetaInfoModel = {
user_id: number;
telegram_chat_id: number;
};

View file

@ -0,0 +1,44 @@
import { UsersService } from '@app/event-emitter/users/users.service';
import { Injectable } from '@nestjs/common';
import { Update, Ctx, Start, Help, On, Hears } from 'nestjs-telegraf';
import { UserMetaInfoService } from 'src/user-meta-info/user-meta-info.service';
import { Context } from 'telegraf';
@Update()
@Injectable()
export class TelegramBotService {
constructor(
private userMetaInfoService: UserMetaInfoService,
private usersService: UsersService,
) {}
@Start()
async start(@Ctx() ctx: Context): Promise<void> {
const chat = await ctx.getChat();
const chatId = chat.id;
const userMetaInfo = await this.userMetaInfoService.findByTelegramId(
chatId,
);
if (!userMetaInfo) {
await ctx.reply('Привет, незнакомец!');
} else {
const user = await this.usersService.getUser(userMetaInfo.user_id);
await ctx.reply(`Привет, ${user.name}`);
}
}
@Help()
async help(@Ctx() ctx: Context): Promise<void> {
await ctx.reply('Send me a sticker');
}
@On('sticker')
async on(@Ctx() ctx: Context): Promise<void> {
await ctx.reply('👍');
}
@Hears('мои задачи')
async hears(@Ctx() ctx: Context): Promise<void> {
await ctx.reply('В разработке');
}
}

View file

@ -0,0 +1,48 @@
import { CacheTTL, Injectable } from '@nestjs/common';
import { UserMetaInfo } from 'src/couchdb-datasources/user-meta-info';
import { UserMetaInfoModel } from 'src/models/user-meta-info.model';
import nano from 'nano';
@Injectable()
export class UserMetaInfoService {
constructor(private userMetaInfo: UserMetaInfo) {}
@CacheTTL(60)
async findById(id: number): Promise<UserMetaInfoModel | null> {
const db = await this.userMetaInfo.getDatasource();
try {
return await db.get(String(id));
} catch (ex) {
return null;
}
}
@CacheTTL(60)
async findByTelegramId(id: number): Promise<UserMetaInfoModel | null> {
const db = await this.userMetaInfo.getDatasource();
try {
const resp = await db.find({
selector: { telegram_chat_id: id },
limit: 1,
});
return resp.docs && resp.docs[0] ? resp.docs[0] : null;
} catch (ex) {
return null;
}
}
async save(data: UserMetaInfoModel): Promise<void> {
const id = String(data.user_id);
const db = await this.userMetaInfo.getDatasource();
let item: (nano.MaybeDocument & UserMetaInfoModel) | null = null;
const newItem: nano.MaybeDocument & UserMetaInfoModel = { ...data };
try {
item = await db.get(id);
} catch (ex) {}
if (item) {
newItem._id = item._id;
newItem._rev = item._rev;
}
await db.insert(newItem);
}
}