Merge branch 'dev'
This commit is contained in:
commit
93e4ea0778
15 changed files with 7765 additions and 2642 deletions
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
"couchDb": {
|
||||
"dbs": {
|
||||
"changes": ""
|
||||
}
|
||||
"changes": "",
|
||||
"userMetaInfo": "",
|
||||
}
|
||||
},
|
||||
"telegramBotToken": "",
|
||||
"personalMessageTemplate": ""
|
||||
}
|
||||
9941
package-lock.json
generated
9941
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -29,9 +29,11 @@
|
|||
"@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",
|
||||
"node-telegram-bot-api": "^0.59.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rss-parser": "^3.12.0",
|
||||
|
|
@ -42,9 +44,11 @@
|
|||
"@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",
|
||||
"@types/node-telegram-bot-api": "^0.57.1",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ 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 } from 'rxjs';
|
||||
import { switchMap, tap } from 'rxjs';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import configuration from './configs/app';
|
||||
|
|
@ -18,6 +18,12 @@ 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 { UserMetaInfoService } from './user-meta-info/user-meta-info.service';
|
||||
import { UserMetaInfo } from './couchdb-datasources/user-meta-info';
|
||||
import { PersonalNotificationAdapterService } from './notifications/adapters/personal-notification.adapter/personal-notification.adapter.service';
|
||||
import { PublicUrlAdapterService } from './notifications/adapters/public-url.adapter.service';
|
||||
import { StatusChangeAdapterService } from './notifications/adapters/status-change.adapter.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -25,6 +31,9 @@ import { Users } from '@app/event-emitter/couchdb-datasources/users';
|
|||
config: configuration().redmineIssueEventEmitterConfig,
|
||||
}),
|
||||
ConfigModule.forRoot({ load: [configuration] }),
|
||||
CacheModule.register({
|
||||
isGlobal: true,
|
||||
}),
|
||||
],
|
||||
controllers: [AppController, MainController],
|
||||
providers: [
|
||||
|
|
@ -36,6 +45,12 @@ import { Users } from '@app/event-emitter/couchdb-datasources/users';
|
|||
Changes,
|
||||
RedminePublicUrlConverter,
|
||||
ChangesCacheWriterService,
|
||||
TelegramBotService,
|
||||
UserMetaInfoService,
|
||||
UserMetaInfo,
|
||||
PersonalNotificationAdapterService,
|
||||
PublicUrlAdapterService,
|
||||
StatusChangeAdapterService,
|
||||
],
|
||||
})
|
||||
export class AppModule implements OnModuleInit {
|
||||
|
|
@ -50,12 +65,16 @@ export class AppModule implements OnModuleInit {
|
|||
private currentUserEnhancer: CurrentUserEnhancer,
|
||||
private statusChangeNotificationsService: StatusChangeNotificationsService,
|
||||
private changesCacheWriterService: ChangesCacheWriterService,
|
||||
private telegramBotService: TelegramBotService,
|
||||
private personalNotificationAdapterService: PersonalNotificationAdapterService,
|
||||
private statusChangeAdapterService: StatusChangeAdapterService,
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
Issues.getDatasource();
|
||||
Users.getDatasource();
|
||||
Changes.getDatasource();
|
||||
UserMetaInfo.getDatasource();
|
||||
|
||||
this.enhancerService.addEnhancer([
|
||||
this.timestampEnhancer,
|
||||
|
|
@ -63,33 +82,37 @@ export class AppModule implements OnModuleInit {
|
|||
this.currentUserEnhancer,
|
||||
]);
|
||||
|
||||
this.personalNotificationsService.$messages.subscribe((message) => {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
this.logger.log(`Get personal message ${JSON.stringify(message.message)} for recipients ${JSON.stringify(message.recipients)}`);
|
||||
this.personalNotificationsService.$messages.subscribe((resp) => {
|
||||
this.logger.log(
|
||||
`Get personal message ` +
|
||||
JSON.stringify(resp.personalParsedMessage.message) +
|
||||
` for recipients ` +
|
||||
JSON.stringify(resp.personalParsedMessage.recipients),
|
||||
);
|
||||
this.personalNotificationAdapterService.send(resp);
|
||||
});
|
||||
this.statusChangeNotificationsService.$changes.subscribe((change) => {
|
||||
const messages = change.messages
|
||||
.map((m) => m.change_message)
|
||||
.filter((m) => !!m);
|
||||
const notifications = change.messages
|
||||
.map((m) => m.notification_message)
|
||||
.filter((m) => !!m);
|
||||
this.logger.log(
|
||||
`Get status changes messages for ` +
|
||||
`issue_id = ${change.issue_id}, ` +
|
||||
`messages = ${JSON.stringify(
|
||||
change.messages.map((m) => m.change_message).filter((m) => !!m),
|
||||
)}`,
|
||||
`messages = ${JSON.stringify(messages)}, ` +
|
||||
`notifications = ${JSON.stringify(notifications)}`,
|
||||
);
|
||||
this.statusChangeAdapterService.send(change);
|
||||
});
|
||||
|
||||
this.redmineIssuesCacheWriterService.subject.subscribe(
|
||||
async (saveResult) => {
|
||||
await this.personalNotificationsService.analize(saveResult);
|
||||
},
|
||||
);
|
||||
|
||||
this.redmineIssuesCacheWriterService.subject
|
||||
.pipe(
|
||||
switchMap(async (saveResult) => {
|
||||
tap((saveResult) => {
|
||||
this.logger.debug(
|
||||
`Save result process started, issue_id = ${saveResult.current.id}`,
|
||||
);
|
||||
return saveResult;
|
||||
}),
|
||||
)
|
||||
.pipe(
|
||||
|
|
@ -117,6 +140,10 @@ export class AppModule implements OnModuleInit {
|
|||
this.logger.debug('Save changes in couchdb successed');
|
||||
return args;
|
||||
}),
|
||||
switchMap(async (args) => {
|
||||
this.logger.debug(``);
|
||||
return args;
|
||||
}),
|
||||
)
|
||||
.subscribe(async (args) => {
|
||||
this.logger.debug(
|
||||
|
|
|
|||
35
src/couchdb-datasources/user-meta-info.ts
Normal file
35
src/couchdb-datasources/user-meta-info.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ export type AppConfig = {
|
|||
couchDb: {
|
||||
dbs: {
|
||||
changes: string;
|
||||
userMetaInfo: string;
|
||||
};
|
||||
};
|
||||
telegramBotToken: string;
|
||||
};
|
||||
|
|
|
|||
7
src/models/issue-and-personal-parsed-message.model.ts
Normal file
7
src/models/issue-and-personal-parsed-message.model.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||
import { PersonalParsedMessage } from './personal-parsed-message.model';
|
||||
|
||||
export type IssueAndPersonalParsedMessageModel = {
|
||||
issue: RedmineTypes.Issue;
|
||||
personalParsedMessage: PersonalParsedMessage;
|
||||
};
|
||||
4
src/models/user-meta-info.model.ts
Normal file
4
src/models/user-meta-info.model.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export type UserMetaInfoModel = {
|
||||
user_id: number;
|
||||
telegram_chat_id: number;
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { IssueAndPersonalParsedMessageModel } from 'src/models/issue-and-personal-parsed-message.model';
|
||||
import { TelegramBotService } from 'src/telegram-bot/telegram-bot.service';
|
||||
import Handlebars from 'handlebars';
|
||||
import { PublicUrlAdapterService } from '../public-url.adapter.service';
|
||||
|
||||
@Injectable()
|
||||
export class PersonalNotificationAdapterService {
|
||||
private personalMessageTemplate: HandlebarsTemplateDelegate;
|
||||
|
||||
constructor(
|
||||
private telegramBotService: TelegramBotService,
|
||||
private configService: ConfigService,
|
||||
private usersService: UsersService,
|
||||
private publicUrlAdapterService: PublicUrlAdapterService,
|
||||
) {
|
||||
const template = this.configService.get<string>('personalMessageTemplate');
|
||||
this.personalMessageTemplate = Handlebars.compile(template);
|
||||
}
|
||||
|
||||
async send(
|
||||
issueAndMessages: IssueAndPersonalParsedMessageModel,
|
||||
): Promise<void> {
|
||||
const promises = issueAndMessages.personalParsedMessage.recipients.map(
|
||||
async (recipient) => {
|
||||
const redmineId = recipient;
|
||||
const issueUrlHtml = this.publicUrlAdapterService.getHtmlHref(
|
||||
issueAndMessages.issue,
|
||||
);
|
||||
const sender = await this.usersService.getUser(
|
||||
issueAndMessages.personalParsedMessage.sender,
|
||||
);
|
||||
const data = {
|
||||
data: {
|
||||
issue: issueAndMessages.issue,
|
||||
messages: issueAndMessages.personalParsedMessage,
|
||||
},
|
||||
issue_url: issueUrlHtml,
|
||||
sender_name: sender.name,
|
||||
message: issueAndMessages.personalParsedMessage.message,
|
||||
};
|
||||
const message = this.personalMessageTemplate(data);
|
||||
return await this.telegramBotService.sendMessageByRedmineId(
|
||||
redmineId,
|
||||
message,
|
||||
{ parse_mode: 'HTML' },
|
||||
);
|
||||
},
|
||||
);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
}
|
||||
21
src/notifications/adapters/public-url.adapter.service.ts
Normal file
21
src/notifications/adapters/public-url.adapter.service.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class PublicUrlAdapterService {
|
||||
private publicUrl: string;
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
this.publicUrl = this.configService.get<string>('redmineUrlPublic');
|
||||
}
|
||||
|
||||
getUrl(issueId: number): string {
|
||||
return `${this.publicUrl}/issues/${issueId}`;
|
||||
}
|
||||
|
||||
getHtmlHref(issue: RedmineTypes.Issue): string {
|
||||
const url = this.getUrl(issue.id);
|
||||
return `<a href="${url}">${issue.tracker.name} #${issue.id}</a>`;
|
||||
}
|
||||
}
|
||||
22
src/notifications/adapters/status-change.adapter.service.ts
Normal file
22
src/notifications/adapters/status-change.adapter.service.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Change } from 'src/models/change.model';
|
||||
import { TelegramBotService } from 'src/telegram-bot/telegram-bot.service';
|
||||
|
||||
@Injectable()
|
||||
export class StatusChangeAdapterService {
|
||||
constructor(private telegramBotService: TelegramBotService) {}
|
||||
|
||||
async send(change: Change): Promise<boolean[]> {
|
||||
const promises = change.messages.map((m) => {
|
||||
if (!m || !m.recipient || !m.recipient.id || !m.notification_message) {
|
||||
return true;
|
||||
}
|
||||
return this.telegramBotService.sendMessageByRedmineId(
|
||||
m.recipient.id,
|
||||
m.notification_message,
|
||||
{ parse_mode: 'HTML' },
|
||||
);
|
||||
});
|
||||
return await Promise.all(promises);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import { SaveResponse } from '@app/event-emitter/models/save-response';
|
|||
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Subject } from 'rxjs';
|
||||
import { IssueAndPersonalParsedMessageModel } from 'src/models/issue-and-personal-parsed-message.model';
|
||||
import { PersonalParsedMessage } from 'src/models/personal-parsed-message.model';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -10,11 +11,13 @@ export class PersonalNotificationsService {
|
|||
private userNameRe = /@([\wА-Яа-яЁё]+) ([\wА-Яа-яЁё]+)@/g;
|
||||
private logger = new Logger(PersonalNotificationsService.name);
|
||||
|
||||
$messages = new Subject<PersonalParsedMessage>();
|
||||
$messages = new Subject<IssueAndPersonalParsedMessageModel>();
|
||||
|
||||
constructor(private usersService: UsersService) {}
|
||||
|
||||
async analize(data: SaveResponse): Promise<PersonalParsedMessage[]> {
|
||||
async analize(
|
||||
data: SaveResponse,
|
||||
): Promise<IssueAndPersonalParsedMessageModel[]> {
|
||||
this.logger.debug(
|
||||
`Analize personal messages for issue ` +
|
||||
`#${data.current.id} (${data.current.subject}) start`,
|
||||
|
|
@ -29,11 +32,16 @@ export class PersonalNotificationsService {
|
|||
`from sender ${message.sender} ` +
|
||||
`for recipients ${JSON.stringify(message.recipients)}`,
|
||||
);
|
||||
this.$messages.next(message);
|
||||
this.$messages.next({
|
||||
issue: data.current,
|
||||
personalParsedMessage: message,
|
||||
});
|
||||
}
|
||||
return message;
|
||||
});
|
||||
const res = (await Promise.all(pMessages)).filter((m) => Boolean(m));
|
||||
const res = (await Promise.all(pMessages))
|
||||
.filter((m) => Boolean(m))
|
||||
.map((m) => ({ issue: data.current, personalParsedMessage: m }));
|
||||
this.logger.debug(
|
||||
`Analize personal messages for issue ` +
|
||||
`#${data.current.id} (${data.current.subject}) finished`,
|
||||
|
|
|
|||
165
src/telegram-bot/telegram-bot.service.ts
Normal file
165
src/telegram-bot/telegram-bot.service.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import TelegramBot from 'node-telegram-bot-api';
|
||||
import { UserMetaInfoService } from 'src/user-meta-info/user-meta-info.service';
|
||||
import axios from 'axios';
|
||||
import { UserMetaInfoModel } from 'src/models/user-meta-info.model';
|
||||
|
||||
@Injectable()
|
||||
export class TelegramBotService {
|
||||
private logger = new Logger(TelegramBotService.name);
|
||||
private bot: TelegramBot;
|
||||
private telegramBotToken: string;
|
||||
private redmineApiUrlPrefix: string;
|
||||
private redminePublicUrlPrefix: string;
|
||||
|
||||
private registerRe = /\/register (\d+) (.+)/;
|
||||
|
||||
constructor(
|
||||
private userMetaInfoService: UserMetaInfoService,
|
||||
private usersService: UsersService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
this.telegramBotToken = this.configService.get<string>('telegramBotToken');
|
||||
this.redmineApiUrlPrefix =
|
||||
this.configService.get<string>('redmineUrlPrefix');
|
||||
this.redminePublicUrlPrefix =
|
||||
this.configService.get<string>('redmineUrlPublic');
|
||||
this.initTelegramBot();
|
||||
}
|
||||
|
||||
private async initTelegramBot(): Promise<void> {
|
||||
const Telegram = await require('node-telegram-bot-api');
|
||||
this.bot = new Telegram(this.telegramBotToken, { polling: true });
|
||||
this.bot.onText(/\/start/, async (msg) => {
|
||||
await this.showHelpMessage(msg);
|
||||
});
|
||||
this.bot.onText(/\/help/, async (msg) => {
|
||||
await this.showHelpMessage(msg);
|
||||
});
|
||||
this.bot.onText(this.registerRe, async (msg) => {
|
||||
await this.register(msg);
|
||||
});
|
||||
this.bot.onText(/\/leave/, async (msg) => {
|
||||
await this.leave(msg);
|
||||
});
|
||||
}
|
||||
|
||||
private async showHelpMessage(msg: TelegramBot.Message): Promise<void> {
|
||||
const userMetaInfo = await this.userMetaInfoService.findByTelegramId(
|
||||
msg.chat.id,
|
||||
);
|
||||
let helpMessage: string;
|
||||
if (userMetaInfo) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
helpMessage = [
|
||||
`/current_issues - мои текущие задачи`,
|
||||
`/help`
|
||||
].join('\n');
|
||||
} else {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
helpMessage = [
|
||||
`/register <redmine_id> <redmine_token>`,
|
||||
`/help`
|
||||
].join('\n');
|
||||
}
|
||||
this.logger.debug(
|
||||
`Sent help message for telegramChatId = ${msg.chat.id}, ` +
|
||||
`message = ${helpMessage}`,
|
||||
);
|
||||
this.bot.sendMessage(msg.chat.id, helpMessage);
|
||||
}
|
||||
|
||||
async sendMessageByRedmineId(
|
||||
redmineId: number,
|
||||
msg: string,
|
||||
options?: TelegramBot.SendMessageOptions,
|
||||
): Promise<boolean> {
|
||||
const userMetaInfo = await this.userMetaInfoService.findByRedmineId(
|
||||
redmineId,
|
||||
);
|
||||
if (!userMetaInfo) return false;
|
||||
const chatId = userMetaInfo.telegram_chat_id;
|
||||
await this.bot.sendMessage(chatId, msg, options);
|
||||
this.logger.debug(
|
||||
`Sent message for redmineUserId = ${redmineId}, ` +
|
||||
`telegramChatId = ${chatId}, ` +
|
||||
`message = ${msg}, ` +
|
||||
`options = ${JSON.stringify(options)}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
async sendMessageByName(
|
||||
firstname: string,
|
||||
lastname: string,
|
||||
msg: string,
|
||||
options?: TelegramBot.SendMessageOptions,
|
||||
): Promise<boolean> {
|
||||
const user = await this.usersService.findUserByName(firstname, lastname);
|
||||
if (!user) return false;
|
||||
return await this.sendMessageByRedmineId(user.id, msg, options);
|
||||
}
|
||||
|
||||
private async register(
|
||||
msg: TelegramBot.Message,
|
||||
): Promise<{ result: boolean; message: string }> {
|
||||
const items = (msg.text || '').match(this.registerRe);
|
||||
|
||||
const result = (result: boolean, message: string, logData?: any) => {
|
||||
const logMsg =
|
||||
`Telegram registration ${result ? 'successed' : 'failed'} ` +
|
||||
`with message ${message}, ` +
|
||||
`log data = ${JSON.stringify(logData || null)}`;
|
||||
this.logger.log(logMsg);
|
||||
this.bot.sendMessage(msg.chat.id, message);
|
||||
return { result: result, message: message };
|
||||
};
|
||||
|
||||
if (!items || !items[1] || !items[2]) {
|
||||
return result(false, 'Не указаны необходимые данные');
|
||||
}
|
||||
|
||||
let redmineId: number, redmineToken: string;
|
||||
try {
|
||||
redmineId = Number(items[1]);
|
||||
redmineToken = items[2];
|
||||
} catch (ex) {
|
||||
return result(false, 'Введены неверные значения', {});
|
||||
}
|
||||
|
||||
const url = `${this.redminePublicUrlPrefix}/users/current.json`;
|
||||
|
||||
const resp = await axios.get(url, {
|
||||
headers: { 'X-Redmine-API-Key': redmineToken },
|
||||
});
|
||||
|
||||
if (resp && resp.data && resp.statusText == 'OK') {
|
||||
const data = resp.data;
|
||||
if (data.user.id == redmineId) {
|
||||
const userData = await this.usersService.getUser(redmineId);
|
||||
const userMetaInfo: UserMetaInfoModel = {
|
||||
telegram_chat_id: msg.chat.id,
|
||||
user_id: userData.id,
|
||||
};
|
||||
await this.userMetaInfoService.save(userMetaInfo);
|
||||
return result(true, `Данные для ${userData.name} подтверждены`, {
|
||||
redmineId: redmineId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result(false, 'Не удалось проверить подлинность указанных данных');
|
||||
}
|
||||
|
||||
private async leave(msg: TelegramBot.Message): Promise<void> {
|
||||
const telegramChatId = msg.chat.id;
|
||||
const userMetaInfo = await this.userMetaInfoService.findByTelegramId(
|
||||
telegramChatId,
|
||||
);
|
||||
if (userMetaInfo) {
|
||||
await this.userMetaInfoService.delete(userMetaInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/user-meta-info/user-meta-info.service.ts
Normal file
63
src/user-meta-info/user-meta-info.service.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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 findByRedmineId(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 = {
|
||||
_id: id,
|
||||
...data,
|
||||
};
|
||||
try {
|
||||
item = await db.get(id);
|
||||
} catch (ex) {}
|
||||
if (item) {
|
||||
newItem._id = item._id;
|
||||
newItem._rev = item._rev;
|
||||
}
|
||||
await db.insert(newItem);
|
||||
}
|
||||
|
||||
async delete(data: UserMetaInfoModel): Promise<void> {
|
||||
const id = String(data.user_id);
|
||||
const db = await this.userMetaInfo.getDatasource();
|
||||
let item: (nano.MaybeDocument & UserMetaInfoModel) | null = null;
|
||||
try {
|
||||
item = await db.get(id);
|
||||
} catch (ex) {}
|
||||
if (item) {
|
||||
await db.destroy(item._id, item._rev);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue