first commit

This commit is contained in:
NAME
2026-05-14 08:42:03 +00:00
commit 5f16ed135d
167 changed files with 29178 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
// auth.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { TelegrafExecutionContext } from 'nestjs-telegraf';
@Injectable()
export class AuthGuard implements CanActivate {
// Danh sách ID được phép (có thể để trong file .env)
private readonly allowedIds =(process.env.TELEGRAM_ALLOW_CHAT_IDS || '').split(',');
canActivate(context: ExecutionContext): boolean {
console.log('AuthGuard');
const ctx = TelegrafExecutionContext.create(context);
const { chat } = ctx.getContext();
// Kiểm tra nếu chat_id nằm trong danh sách cho phép
const isAllowed = this.allowedIds.includes(chat.id);
if (!isAllowed) {
console.warn(`Truy cập trái phép từ ID: ${chat.id}`);
}
return isAllowed;
}
}
@@ -0,0 +1,4 @@
export const WIZARD_WRITER_SCENE_ID = 'WIZARD_WRITER_SCENE_ID';
export const WIZARD_COMMENT_SCENE_ID = 'WIZARD_COMMENT_SCENE_ID';
export const WIZARD_COMMENT2_SCENE_ID = 'WIZARD_COMMENT2_SCENE_ID';
export const WIZARD_QUOTE_SCENE_MAIN = 'WIZARD_QUOTE_SCENE_MAIN';
@@ -0,0 +1,110 @@
// src/modules/telegram/telegram.processor.ts
import {OnWorkerEvent, Processor, WorkerHost} from '@nestjs/bullmq';
import {Job} from 'bullmq';
import {InjectBot} from 'nestjs-telegraf';
import {Context, Telegraf} from 'telegraf';
import {Injectable} from "@nestjs/common";
import {PublishPageService} from "../social-api/publish.page.service";
import {isEmpty, toNumber} from "lodash";
@Injectable()
@Processor('content_writer_completed_queue') // Lắng nghe hàng đợi của AI-B
export class TelegramContentWriterCompletedProcessor extends WorkerHost {
constructor(
@InjectBot() private readonly bot: Telegraf<Context>,
private readonly publishPageService: PublishPageService
) {
super();
}
// Đây là logic xử lý task (nếu cần xử lý thêm ở tầng telegram)
async process(job: Job<any>): Promise<any> {
// Task viết bài đã được AI-B xử lý ở FacebookProcessor
// Ở đây chúng ta có thể thực hiện các logic hậu kỳ nếu muốn
console.log('TelegramProcessor_facebook_content_writer_completed_process ==> begin');
// console.log({job});
const adminChatId = process.env.TELEGRAM_ADMIN_ID || 0;
console.log('TelegramProcessor_facebook_content_writer_completed_process=>posting')
//console.log(fbContent);
if (job.name === 'generate_post_completed') {
const {id, needConfirm, content, autoPublish, telegramChatId, xSubmitProvider} = job.data;
console.log({id, needConfirm, autoPublish, xSubmitProvider})
const X_USERS = process.env.TWITTER_USERNAMES!.split(',');
console.log({X_USERS});
//console.log({job})
// if (autoPublish) {
// // await this.publishPageService.publishTwitter(id);
// // await this.publishPageService.publishToFacebook(id);
// const postId = toNumber(id);
// await this.bot.telegram.sendMessage(adminChatId, '🤖Đã viết xong tin auto:');
//
// try {
// await Promise.allSettled([
// this.publishPageService.publishToFacebook(postId).then(() => {
// this.bot.telegram.sendMessage(adminChatId, `Đăng bài lên FB số ${postId} thành công`);
// }).catch(err => {
// this.bot.telegram.sendMessage(adminChatId, `PublishToFacebook error ${err.message}`);
//
// }),
// this.publishPageService.publishTwitter(postId, xSubmitProvider).then((resp) => {
// this.bot.telegram.sendMessage(adminChatId, `Đăng bài lên X số ${postId} thành công via ${xSubmitProvider}, ${resp?.url}`);
// }).catch(err => {
// this.bot.telegram.sendMessage(adminChatId, `PublishToX error ${err.message}`);
// }),
// ]
// )
// } catch (e) {
// await this.bot.telegram.sendMessage(adminChatId, `❌ Lỗi đăng bài: ${e.message}`);
// }
// return;
// }
await this.bot.telegram.sendMessage(telegramChatId || adminChatId, '🤖Đã viết xong:');
await this.bot.telegram.sendMessage(telegramChatId || adminChatId,
`${content}\n\n`,
{
// parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...X_USERS.map((xuser) => {
return [{
text: `↗️X ${xuser}`,
callback_data: `publish-post_twitter_${id}_${xuser}`
}];
}),
[
// {text: "↗️X", callback_data: `publish-post_twitter_${id}`},
{text: "↗️FB", callback_data: `publish-post_facebook_${id}`},
],
[
// {text: "↗️All", callback_data: `publish-post_all_${id}`},
{text: "🗑️ Hủy bài", callback_data: `delete-post_${id}`}]
]
}
}
);
}
return {};
//return fbContent;
// const postFbId= await this.publishPageService.publishToFacebook(id)
// const twId= await this.publishPageService.publishTwitter(id)
// return job;
return {
//id: id,
// postFbId,
// twId,
// content: postContent,
//image: imageSuggestion,
status: 'posted'
};
}
@OnWorkerEvent('failed')
async onFailed(job: Job<any>, error: Error) {
const adminChatId = process.env.TELEGRAM_ADMIN_ID || 0;
const telegramChatId = job.data.telegramChatId
await this.bot.telegram.sendMessage(telegramChatId || adminChatId, `❌ **Lỗi AI-B:** Job #${job.id} thất bại. \nLý do: ${error.message}`);
}
}
+70
View File
@@ -0,0 +1,70 @@
import {Module, OnModuleInit} from '@nestjs/common';
import {TelegramContentWriterCompletedProcessor} from "./telegram.content.writer.completed.processor";
import {TelegramUpdates} from "./telegram.updates";
import {ManagerModule} from "../manager/manager.module";
import {PgPostService} from "../../shared/pg.post.service";
import {PublishPageService} from "../social-api/publish.page.service";
import {FacebookApi} from "../social-api/facebook.api";
import {SocialModule} from "../social-api/social.module";
import {InjectBot} from "nestjs-telegraf";
import {Context, Telegraf} from "telegraf";
import {XReaderService} from "../x-reader/x-reader.service";
import {TrendsService} from "../trends/trends.service";
import {CollectorModule} from "../collector/collector.module";
import {WriterWizard} from "./wizard/writer.wizard";
import {CommentWizard} from "./wizard/comment.wizard";
import {QuoteWizard} from "./wizard/quote.wizard";
import {XImageUploadService} from "../x-uploader/x-image.upload.service";
import {Comment2Wizard} from "./wizard/comment2.wizard";
import {SqsPostService} from "../sqs-module/sqs.post.service";
@Module({
imports: [
ManagerModule,
SocialModule,
CollectorModule,
],
providers: [
TelegramUpdates,
TelegramContentWriterCompletedProcessor,
FacebookApi,
PgPostService,
PublishPageService,
XReaderService,
TrendsService,
WriterWizard,
CommentWizard,
QuoteWizard,
Comment2Wizard,
XImageUploadService,
SqsPostService
],
})
export class TelegramModule implements OnModuleInit{
constructor(@InjectBot() private readonly bot: Telegraf<Context>) {}
async onModuleInit() {
try {
await this.bot.telegram.setMyCommands( [
{ command: 'start', description: 'Khởi động bot' },
{ command: 'help', description: 'Trợ giúp' },
{ command: 'trendstat', description: 'trend statistic' },
{ command: 'analyzenews', description: 'Tìm trend' },
{ command: 'ask', description: 'Hoi AI' },
{ command: 'write', description: 'Version mới, dùng 2 AI, 1 viết và 1 reviewer' },
{ command: 'write_', description: 'Version mới, dùng 2 AI, 1 viết và 1 reviewer' },
{ command: 'writecheap', description: 'Version cũ, dùng deepseek' },
{ command: 'xreader', description: 'Lấy nội dung băng api' },
{ command: 'xreader_browser', description: 'Lấy nội dung bằng trình duyệt' },
{ command: 'collectrss', description: 'Lấy nội dung' },
{ command: 'comment', description: 'Viet reply X' },
{ command: 'comen', description: '/com[en,ja,vi,ko] comment as text' },
]);
console.log('✅ Đã cập nhật Menu commands thành công');
} catch (error) {
console.error('❌ Lỗi khi cập nhật Menu:', error);
}
}
}
+813
View File
@@ -0,0 +1,813 @@
import {Action, Command, Ctx, On, Start, Update} from 'nestjs-telegraf';
import {Context, Scenes} from 'telegraf';
import {HttpException, Injectable} from "@nestjs/common";
import {ManagerService} from "../manager/manager.service";
import {PublishPageService} from "../social-api/publish.page.service";
import {_toNum} from "../../shared/helper";
import {XReaderService} from "../x-reader/x-reader.service";
import {TrendsService} from "../trends/trends.service";
import {get, isEmpty} from 'lodash'
import {
WIZARD_COMMENT2_SCENE_ID,
WIZARD_COMMENT_SCENE_ID,
WIZARD_QUOTE_SCENE_MAIN,
WIZARD_WRITER_SCENE_ID
} from "./telegram.constants";
import {XImageUploadService} from "../x-uploader/x-image.upload.service";
import {TextUtil} from "../../common/utils/text.util";
import {XStrategy} from "../social-api/x-router.service";
import {XCacheService} from "../x-cache/x-cache.service";
import {SqsPostService} from "../sqs-module/sqs.post.service";
@Injectable()
@Update()
export class TelegramUpdates {
constructor(
private readonly managerService: ManagerService,
private readonly publishPageService: PublishPageService,
private readonly xReaderService: XReaderService,
private readonly trendsService: TrendsService,
private readonly xUpload: XImageUploadService,
private readonly cacheService: XCacheService,
private readonly sqsPostService: SqsPostService
) {
}
@Start()
async onStart(ctx: Context) {
await ctx.reply('Hệ thống Automation đã sẵn sàng! Gửi /help để xem lệnh.');
}
@Command('help')
async onHelpCommand(ctx: Context) {
await ctx.reply('Sau đây là các tính năng... ' +
'\n' +
'/writecheap_[en|ja|ko|vi] your_topic... (viết bài bằng AI deepseek)\n' +
'/write_[vi|en|ja|ko] your_topic... (viết bài bằng AI chatgpt + deepseek)\n' +
'/manual_publish_post postNum\n' +
'/analyzenews ... (tim trend,...) \n' +
'/trendstat \n' +
'/ask ... (hỏi với ai deepseek,...) \n' +
'/trend_stat ... (...) \n' +
'/xreader ... (...) \n' +
'/xreaderbrowser ... (...) \n' +
'/collectrss ... (...) \n' +
'/comment link X... (...) \n' +
'/quote link X... (...) \n' +
'');
}
@Command(['download', 'dl'])
async onDownloadVideo(ctx) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} link tiktok/fb`);
return;
}
// Regex tổng hợp các loại link
const tiktokRegex = /https?:\/\/(?:www\.|vm\.|vt\.|m\.)?tiktok\.com\/[^\s]+/gi;
const youtubeRegex = /https?:\/\/(?:www\.)?(?:youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/)[^\s]+/gi;
const facebookRegex = /https?:\/\/(?:www\.|fb\.)?(?:facebook\.com\/reels\/|watch\/)[^\s]+/gi;
const mp4Regex = /https?:\/\/[^\s^"]+\.mp4(?:\?[^\s^"]*)?/gi;
// Cách 1: Lấy tất cả vào một mảng
const allRegex = new RegExp(`${tiktokRegex.source}|${youtubeRegex.source}|${facebookRegex.source}|${mp4Regex.source}`, 'gi');
const allLinks = payload.match(allRegex) || [];
console.log({allLinks});
console.log("Tất cả link tìm thấy:", allLinks);
if (allLinks.length === 0) {
await ctx.reply(`Khong tim thay link download`);
return;
}
if (allLinks.length === 1) {
await this.managerService.handleDownloadVideo(allLinks[0], ctx.chat.id);
} else {
await this.managerService.handleDownloadVideoMulti(allLinks, ctx.chat.id);
}
await ctx.reply(`vui lòng chờ download ....`);
}
@Command('downloadmp4url')
async onDownloadMp4Url(ctx) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} link mp4`);
return;
}
await ctx.reply(`vui lòng chờ download....`);
await this.managerService.handleDownloadMp4Url(payload, 'x', ctx.chat.id);
}
@Command('comment')
async onWizardCommand(@Ctx() ctx: Scenes.SceneContext): Promise<void> {
await ctx.scene.enter(WIZARD_COMMENT_SCENE_ID);
}
@Command([
'comvi',
'comen',
'comja',
'comko',
'comcn',
])
async onCommentAsTextCommand(@Ctx() ctx: Scenes.SceneContext): Promise<void> {
let language = 'en';
// @ts-ignore
let {command, payload} = ctx;
if (['comvi', 'comvn', 'com_vi'].includes(command)) {
language = 'vi';
}
if (['comko', 'comkr', 'com_ko'].includes(command)) {
language = 'ko';
}
if (['comja', 'comjp', 'com_ja'].includes(command)) {
language = 'ja';
}
if (['comcn'].includes(command)) {
language = 'cn';
}
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} bitcoin approach $100000`);
return;
}
const chatId = ctx.chat?.id;
payload = payload.trim(); // Gets "my_payload"
console.log({payload});
let tweetId = undefined;
let tweetUrl = undefined;
let _linkX = payload.replace('twitter.com', 'x.com').split('?')[0];
const match = _linkX.match(/status\/(\d+)/);
if (match) {
//nêu match => get content x
const xpost = await this.xReaderService.readXPost(_linkX);
console.log('==> content text:' + xpost.text);
payload = xpost.text;
tweetId = xpost.tweetId;
tweetUrl = _linkX;
} else {
}
await ctx.scene.enter(WIZARD_COMMENT2_SCENE_ID, {
language,
userTopic: payload,
tweetId,
tweetUrl,
chatId
});
}
@Command([
'quote_vi',
'quotevi',
'quotevn',
'quote_en',
'quoteen',
'quote_ja',
'quoteja',
'quote_jp',
'quotejp',
'quote_ko',
'quoteko',
'quotecn',
])
async onWizardQuote(@Ctx() ctx: Scenes.SceneContext): Promise<void> {
// @ts-ignore
let {command, payload} = ctx;
if (['quote_jp', 'quotejp', 'quoteja'].includes(command)) {
command = 'quote_ja';
}
if (['quote_vn', 'quotevi', 'quotevn'].includes(command)) {
command = 'quote_vi';
}
if (['quoteen'].includes(command)) {
command = 'quote_en';
}
if (['quoteko'].includes(command)) {
command = 'quote_ko';
}
if (['quotecn'].includes(command)) {
command = 'quote_cn';
}
if (!payload) {
await ctx.reply(`vui lòng đưa url, ví dụ: /${command} link_x_post ?`);
return;
}
const preferLanguage = command.split('_')[1];
await ctx.scene.enter(WIZARD_QUOTE_SCENE_MAIN, {
language: preferLanguage,
linkUrl: payload,
quoteText: '',
quoteAs: 'quotelink',
});
}
@Command([
'quoteentext',
'quotevitext',
'quotejatext',
'quotekotext',
'quotecntext',
])
async onQuoteAsTextCommand(@Ctx() ctx: Scenes.SceneContext): Promise<void> {
// @ts-ignore
let {command, payload} = ctx;
let language = 'en';
if (['quotevitext'].includes(command)) {
language = 'vi';
}
if (['quotejatext'].includes(command)) {
language = 'ja';
}
if (['quotecntext'].includes(command)) {
language = 'cn';
}
if (['quotekotext'].includes(command)) {
language = 'ko';
}
if (!payload) {
await ctx.reply(`ví dụ: /${command} nội dung quote ?`);
return;
}
await ctx.scene.enter(WIZARD_QUOTE_SCENE_MAIN, {
language: language,
quoteText: payload,
quoteAs: 'quotetext',
});
}
@Command('collectrss')
async onCollectRssCommand(ctx: Context) {
const result = await this.trendsService.collectAndStore('rss');
ctx.reply('Collection completed');
await ctx.reply(JSON.stringify(result.stats));
}
@Command(['writecheap_vi', 'writecheap_en', 'writecheap_ja', 'writecheap_ko'])
async onWriteCheapCommand(@Ctx() ctx: Context) {
// @ts-ignore
const {command, payload} = ctx;
const preferLanguage = command.split('_')[1];
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} bitcoin approach $100000`);
return;
}
await this.managerService.manualTrigger(payload, '', preferLanguage);
await ctx.reply(`Đang tìm kiếm tin tức về: ${payload} với ngôn ngữ ${preferLanguage}...`);
}
@Command([
'write_vn',
'write_vi',
'writevi',
'write_en',
'writeen',
'write_jp',
'write_ja',
'writeja',
'write_kr',
'write_ko',
'writeko',
'writecn',
'write_cn',
])
async onWriteSmartWithAIReviewerCommand(@Ctx() ctx: Scenes.SceneContext) {
// @ts-ignore
let {command, payload} = ctx;
if (['write_jp', 'writejp', 'writeja'].includes(command)) {
command = 'write_ja';
}
if (['write_kr', 'writekr', 'writeko'].includes(command)) {
command = 'write_ko';
}
if (['write_vn', 'writevn', 'writevi'].includes(command)) {
command = 'write_vi';
}
if (['writeen'].includes(command)) {
command = 'write_en';
}
if (['writecn'].includes(command)) {
command = 'write_cn';
}
const preferLanguage = command.split('_')[1];
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} bitcoin approach $100000`);
return;
}
const chatId = ctx.chat?.id;
if (payload.length < 200) {
//check has link X
const detectSendLinkX = TextUtil.detectLinkX(payload);
if (detectSendLinkX.hasLinkX) {
const xpost = await this.xReaderService.readXPost(detectSendLinkX.url);
payload = xpost.text;
}
}
await ctx.scene.enter(WIZARD_WRITER_SCENE_ID, {
language: preferLanguage,
userTopic: payload,
chatId,
});
// await this.managerService.manualTriggerWrite2(payload, '', preferLanguage);
// await ctx.reply(`Đang tìm kiếm tin tức về: ${payload} với ngôn ngữ ${preferLanguage}...`);
}
@Command('manual_publish_post')
async manual_publish_post(@Ctx() ctx: Context) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply('thiếu thông tin post num');
return;
}
const postNo = 1 * payload;
await ctx.reply(`Đang đẩy bài ${payload} lên Facebook, Twitter`);
await ctx.reply(
`🤖 **Vui lòng chọn kênh**`,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{text: "🐦 Post X", callback_data: `publish-post_twitter_${postNo}`},
{text: "🚀 Post FB", callback_data: `publish-post_facebook_${postNo}`},
],
[
{text: "🐦 Post X via browser", callback_data: `publish-post_twitterbrowser_${postNo}`},
{text: "🚀 Post all", callback_data: `publish-post_all_${postNo}`},
],
[{text: "🗑️ Hủy bài", callback_data: `delete-post_${postNo}`}]
]
}
}
);
// await this.publishPageService.publishToFacebook(postNo).then(() => {
// ctx.reply(`Đăng bài lên FB số ${postNo} thành công`);
// }).catch(err => {
// ctx.reply(`PublishToFacebook error ${err.message}`);
// });
// await this.publishPageService.publishTwitter(postNo).then(() => {
// ctx.reply(`Đăng bài lên X số ${postNo} thành công`);
// }).catch(err => {
// ctx.reply(`PublishTwitter error ${err.message}`);
// });
}
@Command('analyzenews')
async onAnalyzeNews(ctx: Context) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} Hot news today in japan ?`);
return;
}
await this.managerService.handleAnalyzeNewsTrend(payload);
await ctx.reply(`vui lòng đưa chờ phân tích ....`);
}
@Command('ask')
async onAsk(ctx: Context) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} Hot news today in japan ?`);
return;
}
console.log(ctx.chat);
await this.managerService.handleAskQues(payload, ctx.chat?.id);
await ctx.reply(`vui lòng đưa chờ phân tích ....`);
}
@Command('askx')
async onAskXContext(ctx: Context) {
// @ts-ignore
const {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa topic, ví dụ: /${command} Hot news today in japan ?`);
return;
}
console.log(ctx.chat);
await this.managerService.handleAskXContext(payload, ctx.chat?.id);
await ctx.reply(`vui lòng đưa chờ phân tích ....`);
}
@Command(['xreader', 'xreader_browser'])
async onXReader(ctx: Context) {
// @ts-ignore
let {command, payload} = ctx;
if (!payload) {
await ctx.reply(`vui lòng đưa url, ví dụ: /${command} link_x_post ?`);
return;
}
await ctx.reply(`vui lòng chờ phân tích ....`);
const content = await this.xReaderService.readXPost(
payload,
command === 'xreader_browser' ? 'browser' : 'any'
).catch((err) => {
ctx.reply(err.message);
return err;
});
let textMessage = ``;
try {
textMessage += `🤖 crawler by: ${content.via} \n\n`;
if (content) {
textMessage += `====== CONTENT ===== \n\n`;
textMessage += `text: ${content.text} \n----------------------- \n`;
if (!isEmpty(content.images)) {
textMessage += `images: ${content.images.join(' , ')} \n----------------------- \n`;
}
if (!isEmpty(content.videos)) {
textMessage += `videos: ${content.videos.join(' , ')} \n`;
}
if (!isEmpty(content.quoted)) {
textMessage += `====== QUOTE ===== \n`;
textMessage += `quoted text: ${get(content, 'quoted.text')} \n----------------------- \n`;
if (!isEmpty(content?.quoted?.images)) {
textMessage += `quoted images: ${get(content, 'quoted.images', []).join(' , ')} \n----------------------- \n`;
}
if (!isEmpty(content?.quoted?.videos)) {
textMessage += `quoted videos: ${get(content, 'quoted.videos', []).join(' , ')} \n----------------------- \n`;
}
}
}
await ctx.reply(textMessage, {parse_mode: 'HTML'});
} catch (err) {
console.log(err);
ctx.reply(err.message);
}
}
@Action(/publish-post_(.+)/)
async onPublishFB(ctx: Context) {
const telegramChatId = ctx.chat?.id;
const callbackData = (ctx.callbackQuery as any).data;
const social = callbackData.split('_')[1];
const postId = _toNum(callbackData.split('_')[2]);
const xUsername = callbackData.split('_')[3];
await ctx.answerCbQuery(`Đang đẩy bài lên Fb,X ${xUsername}...`);
const promiseSettedFunc = [];
let allowTw = 0;
let allowFb = 0;
let twStrageryPost = XStrategy.API_ONLY;
let publishTo: any = [];
switch (social) {
case 'all':
allowTw = 1;
allowFb = 1;
publishTo = ['x', 'fb'];
break;
case 'twitter':
allowTw = 1;
publishTo = ['x'];
twStrageryPost=XStrategy.BROWSER_FIRST
break;
case 'twitterbrowser':
allowTw = 1;
publishTo = ['x'];
twStrageryPost = XStrategy.BROWSER_ONLY;
break;
case 'facebook':
allowFb = 1;
publishTo = ['fb'];
break;
}
try {
// @ts-ignore
const messageText = ctx.callbackQuery.message.text;
await this.sqsPostService.sendMessage(xUsername,
{
name: 'generate_post_completed',
type: 'X_POSTER_TWEET',
needConfirm: 1,
content: messageText,
autoPublish: 1,
telegramChatId,
xUsername,
publishTo,
xSubmitProvider: XStrategy.BROWSER_FIRST //cứ 3post api, có 1 post browser
}
)
// const r = await this.publishPageService.relyX(messageText, tweetId)
//@ts-ignore
await ctx.editMessageText(`✅ ** Đã gửi tweet sang queue ${xUsername} ID bài viết: ${postId}`);
// await Promise.allSettled([
// allowFb && this.publishPageService.publishToFacebook(postId).then(() => {
// ctx.reply(`Đăng bài lên FB số ${postId} thành công`);
// }).catch(err => {
// ctx.reply(`PublishToFacebook error ${err.message}`);
// }),
// allowTw && this.publishPageService.publishTwitter(postId, twStrageryPost).then((resp) => {
// ctx.reply(`Đăng bài lên X số ${postId} thành công, ${resp?.url}`);
// }).catch(err => {
// ctx.reply(`PublishTwitter error ${err.message}`);
// }),
// ]
// )
// await ctx.editMessageText(`✅ **ĐÃ ĐĂNG THÀNH CÔNG!**\nID bài viết: ${postId}`);
} catch (e) {
await ctx.reply(`❌ Lỗi gửi bài: ${e.message}`);
}
}
@Action(/delete-post_(.+)/)
async onDelete(ctx: Context) {
const callbackData = (ctx.callbackQuery as any).data;
const postId = _toNum(callbackData.split('_')[1]);
// console.log(ctx.match);
console.log('delete_post_id=', postId);
await this.publishPageService.rejectedPost(postId);
await ctx.answerCbQuery('Đã hủy bài viết.');
await ctx.editMessageText('🗑️ Bài viết đã được loại bỏ khỏi hàng đợi.');
}
// @ts-ignore
@Command(['trendstat', 'trend_stat'])
async onTrendStatCommand(ctx: Context) {
await ctx.reply(`Đang query..`);
const {topTrends, byCategory} = await this.trendsService.getStats();
let message = ``;
topTrends.forEach(trend => {
message += `\n${trend.title} - ${trend.category} - ${trend.score}\n`;
});
await ctx.reply(message, {
parse_mode: 'HTML'
});
message = '';
console.log({byCategory});
byCategory.forEach(trend => {
message += `\n${trend.category} - ${trend._count._all} - ${trend._avg.score} \n`;
});
await ctx.reply(message, {
parse_mode: 'HTML'
});
ctx.reply('Done')
}
@Action(/publish-reply-twitter1_(.+)/)
async onActionSubmitReplyX1(ctx: Context) {
const callbackData = (ctx.callbackQuery as any).data;
// @ts-ignore
const messageText = ctx.callbackQuery.message.text;
const callbackDataArr = callbackData.split('_');
const tweetId = callbackDataArr[1];
const xUsername = callbackDataArr[2];
await ctx.answerCbQuery('Đang đẩy reply lên Twitter...');
console.log(messageText);
console.log(`publish-reply-twitter1_ ${tweetId} ${xUsername}`);
const telegramChatId = ctx.chat?.id;
try {
const tweetUrl = await this.cacheService.getCacheTweetUrlById(tweetId);
if (isEmpty(tweetUrl)) {
throw new HttpException(`Không tìm thấy tweet url từ Id : ${tweetId}`, 500);
}
await this.sqsPostService.sendMessage(xUsername,
{
tweetId: tweetId,
tweetUrl,
// name: 'generate_comment_twitter_completed',
type: 'X_POSTER_REPLY',
needConfirm: 1,
content: messageText,
autoPublish: 1,
telegramChatId,
xUsername,
xSubmitProvider: XStrategy.BROWSER_ONLY //cứ 3post api, có 1 post browser
}
)
// const r = await this.publishPageService.relyX(messageText, tweetId)
//@ts-ignore
if (r.success) {
await ctx.editMessageText(`✅ ** Reply success !**\n ID bài viết: ${tweetId}`);
await ctx.reply(`✅ ** Reply success !`);
} else {
//@ts-ignore
await ctx.reply(`❌ Lỗi reply: ${r.error} `);
}
} catch (err) {
console.log(err);
const detail = get(err, 'data.detail', '')
await ctx.reply(`❌ Lỗi reply: ${err.message} `);
await ctx.reply(`=> ${detail}`);
}
}
@Action(/publish-reply-twitter_(.+)/)
async onActionSubmitReplyX(ctx: Context) {
const callbackData = (ctx.callbackQuery as any).data;
// @ts-ignore
const messageText = ctx.callbackQuery.message.text;
const callbackDataArr = callbackData.split('_');
const tweetId = callbackDataArr[1];
const xUsername = callbackDataArr[2];
await ctx.answerCbQuery('Đang đẩy reply lên Twitter...');
console.log(`Đang đẩy reply lên Twitter 0...`);
console.log(messageText);
const telegramChatId = ctx.chat?.id;
try {
const tweetUrl = await this.cacheService.getCacheTweetUrlById(tweetId);
if (isEmpty(tweetUrl)) {
console.error(`Không tìm thấy tweet url từ Id : ${tweetId}`);
throw new HttpException(`Không tìm thấy tweet url từ Id : ${tweetId}`, 500);
}
await this.sqsPostService.sendMessage(xUsername,
{
tweetId: tweetId,
tweetUrl,
// name: 'generate_comment_twitter_completed',
type: 'X_POSTER_REPLY',
needConfirm: 1,
content: messageText,
autoPublish: 1,
telegramChatId,
xUsername,
xSubmitProvider: XStrategy.BROWSER_ONLY //cứ 3post api, có 1 post browser
}
)
// const r = await this.publishPageService.relyX(messageText, tweetId)
//@ts-ignore
await ctx.editMessageText(`✅ ** Đã gửi tin sang queue ${xUsername} ID bài viết: ${tweetId}`);
// await ctx.reply(`✅ Đã gửi tin sang queue ${xUsername}!, ID bài viết: ${tweetId}`);
// else {
// //@ts-ignore
// await ctx.reply(`❌ Lỗi reply: ${r.error} `);
// }
} catch (err) {
console.log(err);
const detail = get(err, 'data.detail', '')
await ctx.reply(`❌ Lỗi reply: ${err.message} `);
await ctx.reply(`=> ${detail}`);
}
}
@Action(/publish-quote-twitter_(.+)/)
async onActionSubmitQuoteX(ctx: Context) {
const callbackData = (ctx.callbackQuery as any).data;
// @ts-ignore
const messageText = ctx.callbackQuery.message.text;
// const tweetId = callbackData.split('_')[1];
const callbackDataArr = callbackData.split('_');
const tweetId = callbackDataArr[1];
const xUsername = callbackDataArr[2];
await ctx.answerCbQuery('Đang đẩy quote lên Twitter...');
console.log(`Đang đẩy quote lên Twitter...`);
console.log(messageText);
console.log(tweetId);
console.log({xUsername, tweetId});
const telegramChatId = ctx.chat?.id;
try {
const tweetUrl = await this.cacheService.getCacheTweetUrlById(tweetId);
if (isEmpty(tweetUrl)) {
console.error(`Không tìm thấy tweet url từ Id : ${tweetId}`);
throw new HttpException(`Không tìm thấy tweet url từ Id : ${tweetId}`, 500);
}
await this.sqsPostService.sendMessage(xUsername,
{
tweetId: tweetId,
tweetUrl,
// name: 'generate_comment_twitter_completed',
type: 'X_POSTER_QUOTE',
needConfirm: 1,
content: messageText,
autoPublish: 1,
telegramChatId,
xUsername,
xSubmitProvider: XStrategy.BROWSER_ONLY //cứ 3post api, có 1 post browser
}
)
await ctx.reply(`✅ Gửi tin sang queue.`);
//await this.publishPageService.quoteX(messageText, tweetId)
//const r = await this.publishPageService.quoteX(messageText, tweetId)
// @ts-ignore
// if (r.success) {
// await ctx.editMessageText(`✅ ** Quote success !**`);
// await ctx.reply(`✅ ** Quote success !**`);
// } else {
// await ctx.reply(`❌ Lỗi quote`);
// // @ts-ignore
// await ctx.reply(r.error);
// }
} catch (err) {
console.log(err);
const detail = get(err, 'data.detail', '')
await ctx.reply(`❌ Lỗi quote: ${err.message} `);
await ctx.reply(`=> ${detail}`);
}
}
@Action([
/delete-reply_(.+)/,
/delete-quote_(.+)/
])
async onDeleteReplyX(ctx: Context) {
const callbackData = (ctx.callbackQuery as any).data;
const postId = _toNum(callbackData.split('_')[1]);
// console.log(ctx.match);
console.log('delete_post_id=', postId);
// await this.publishPageService.rejectedPost(postId);
await ctx.answerCbQuery('Đã hủy bài viết.');
await ctx.editMessageText('🗑️ Bài viết đã được loại bỏ khỏi hàng đợi.');
}
@On('photo')
async handlePhoto(@Ctx() ctx: Context) {
try {
const photos = (ctx.message as any).photo;
const largest = photos[photos.length - 1];
const fileId = largest.file_id;
// Lấy link để tải ảnh về (nếu cần)
const fileUrl = await ctx.telegram.getFileLink(fileId);
await ctx.reply(`Đã nhận được ảnh! Bạn có thể xem tại: ${fileUrl}`);
// // 1. get file path
// const res = await axios.get(
// `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/getFile`,
// {
// params: {file_id: fileId},
// }
// );
//
// const filePath = res.data.result.file_path;
//
// // 2. build download url
// const fileUrl = `https://api.telegram.org/file/bot${process.env.TELEGRAM_BOT_TOKEN}/${filePath}`;
//
// // 3. download buffer
// const img = await axios.get(fileUrl, {
// responseType: 'arraybuffer',
// });
//
// const buffer = Buffer.from(img.data);
// 4. upload to X
const mediaId = await this.xUpload.uploadImageFromUrl(fileUrl.toString());
await ctx.reply(`✅ Uploaded mediaId: ${mediaId}`);
} catch (e) {
console.error(e);
await ctx.reply('❌ Upload failed');
}
}
@Command('chatid')
async onGetChatId(ctx: Context) {
// @ts-ignore
ctx.reply(ctx.chat.id)
}
}
@@ -0,0 +1,100 @@
import {Action, Command, Ctx, Message, On, Wizard, WizardStep} from "nestjs-telegraf";
import {WIZARD_COMMENT_SCENE_ID} from "../telegram.constants";
import * as scenes from "telegraf/scenes";
import {ManagerService} from "../../manager/manager.service";
import {PublishPageService} from "../../social-api/publish.page.service";
import {XReaderService} from "../../x-reader/x-reader.service";
import {TrendsService} from "../../trends/trends.service";
@Wizard(WIZARD_COMMENT_SCENE_ID)
export class CommentWizard {
constructor(
private readonly managerService: ManagerService,
) {
}
// Bắt lệnh /cancel để thoát
@Command('cancel')
async cancel(@Ctx() ctx: scenes.WizardContext) {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave(); // Thoát khỏi wizard ngay lập tức
}
@WizardStep(1)
async onSceneEnter(@Ctx() ctx: scenes.WizardContext) {
// await ctx.wizard.next();
// return 'Welcome to wizard scene ✋ Send me your name';
//@ts-ignore
const text = ctx.message?.text;
console.log(ctx.message);
const linkX = text.split(' ')[1]; // Gets "my_payload"
//
// // Pass it as the second argument (initial state)
//await ctx.scene.enter(WIZARD_COMMENT_SCENE_ID, {customPayload: payload});
console.log(linkX);
let _linkX = linkX.replace('twitter.com', 'x.com').split('?')[0];
const match = _linkX.match(/status\/(\d+)/);
if (!match) {
await ctx.reply('URL X không hợp lệ');
return ctx.scene.leave();
}
const tweetId = match[1];
// 1. Lưu dữ liệu vào state
(ctx.wizard.state as any).linkUrl = _linkX;
(ctx.wizard.state as any).tweetId = tweetId;
await ctx.sendMessage(
`🤖 Bạn muốn viết bằng ngôn ngữ nào , bấm \cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{text: "🇬🇧English", callback_data: `write_comment_en`},
{text: "🇯🇵Japan", callback_data: `write_comment_ja`},
],
[
{text: "🇰🇷Korea", callback_data: `write_comment_ko`},
{text: "🇻🇳VN", callback_data: `write_comment_vi`},
],
[{text: "❌Cancel", callback_data: `write_comment_cancel`},]
]
}
}
);
// const {payload} = ctx.scene;
// await ctx.reply(`Gứi link của bài X bạn cần reply`);
ctx.wizard.next();
}
@WizardStep(2)
@On('text')
@Action(/write_comment_(.+)/)
async step2(@Ctx() ctx: scenes.WizardContext) {
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
const selectedItem = ctx.match[1]
if (selectedItem == 'cancel') {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 2. Lưu vào state để dùng cho các bước sau (nếu cần)
(ctx.wizard.state as any).selectedLanguage = selectedItem;
// 3. Phải gọi answerCbQuery để tắt biểu tượng "loading" trên nút bấm
await ctx.answerCbQuery();
await ctx.reply(`Bạn đã chọn ${selectedItem}. Vui lòng chờ phân tích ...`);
const url = (ctx.wizard.state as any).linkUrl;
await this.managerService.manualTriggerCommentLinkTwitter(url, selectedItem);
// Chuyển sang bước 3 (nhận địa chỉ bằng text)
// ctx.wizard.next();
await ctx.scene.leave();
}
}
@@ -0,0 +1,227 @@
import {Action, Command, Ctx, On, Wizard, WizardStep} from "nestjs-telegraf";
import {WIZARD_COMMENT2_SCENE_ID} from "../telegram.constants";
import * as scenes from "telegraf/scenes";
import {ManagerService} from "../../manager/manager.service";
import {TONE_HINTS_TELEGRAM_BUTTON} from "../../content-writer/prompts/templates";
import {ANGLE_HINTS_TELEGRAM_BUTTON} from "../../content-writer/enum/angle.enum";
@Wizard(WIZARD_COMMENT2_SCENE_ID)
export class Comment2Wizard {
constructor(
private readonly managerService: ManagerService,
) {
}
// Bắt lệnh /cancel để thoát
@Command('cancel')
async cancel(@Ctx() ctx: scenes.WizardContext) {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave(); // Thoát khỏi wizard ngay lập tức
}
@WizardStep(1)
async onSceneEnter(@Ctx() ctx: scenes.WizardContext) {
//@ts-ignore
const userTopic = ctx.message?.text;
// console.log(ctx.message);
// const userTopic = (ctx.wizard.state as any).userTopic;
if (!userTopic) {
await ctx.reply('Không có thông tin về chủ đề bạn cần viết bài');
return ctx.scene.leave();
}
// @ts-ignore
const inline_keyboards = [];
Object.keys(TONE_HINTS_TELEGRAM_BUTTON).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: TONE_HINTS_TELEGRAM_BUTTON[key].text,
callback_data: `comment_tone_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn phong cách viết , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `comment_tone_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `comment_tone_cancel`},
]
]
}
}
);
ctx.wizard.next();
}
@WizardStep(2)
@On('text')
@Action(/comment_tone_(.+)/)
async step2(@Ctx() ctx: scenes.WizardContext) {
if (ctx.text == 'cancel') {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
if (ctx.match === undefined) {
await ctx.reply('Đã hủy quy trình.');
await ctx.deleteMessage();
return ctx.scene.leave();
}
let tone = undefined
// @ts-ignore
const selectedItem = ctx.match[1]
await ctx.answerCbQuery('Đã ghi nhận tone=' + selectedItem);
if (selectedItem == 'cancel') {
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
if (selectedItem == 'aineeddetect') {
tone = undefined;
} else {
tone = selectedItem
}
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
(ctx.wizard.state as any).tone = tone;
// @ts-ignore
const inline_keyboards = [];
Object.keys(ANGLE_HINTS_TELEGRAM_BUTTON).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: ANGLE_HINTS_TELEGRAM_BUTTON[key].text,
callback_data: `comment_agle_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn âm điệu , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `comment_agle_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `comment_agle_cancel`},
]
]
}
}
);
ctx.wizard.next();
}
@WizardStep(3)
@On('text')
@Action(/comment_agle_(.+)/)
async step3(@Ctx() ctx: scenes.WizardContext) {
if (ctx.text == 'cancel') {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
if (ctx.match === undefined) {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
let agle = undefined
// @ts-ignore
const selectedItem = ctx.match[1]
await ctx.answerCbQuery('Đã ghi nhận agle:' + selectedItem);
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
if (selectedItem == 'cancel') {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
if (selectedItem == 'aineeddetect') {
agle = undefined;
} else {
agle = selectedItem
}
(ctx.wizard.state as any).agle = agle;
const chatId = (ctx.wizard.state as any).chatId;
const tone = (ctx.wizard.state as any).tone;
const tweetId = (ctx.wizard.state as any).tweetId;
const tweetUrl = (ctx.wizard.state as any).tweetUrl;
const userTopic = (ctx.wizard.state as any).userTopic;
const language = (ctx.wizard.state as any).language;
const postLength = undefined;
// @ts-ignore
await ctx.reply(`
Sẽ viết bài bằng ngôn ngữ ${language} độ dài ${postLength} với tone=${tone} và agle=${agle}.`);
await this.managerService.manualTriggerCommentAsText({
agle: agle,
comtext: userTopic,
tone: tone,
language: language,
tweetId,
tweetUrl,
chatId,
})
await ctx.scene.leave();
}
async doAskTone(ctx: scenes.WizardContext) {
// @ts-ignore
const inline_keyboards = [];
Object.keys(TONE_HINTS_TELEGRAM_BUTTON).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: TONE_HINTS_TELEGRAM_BUTTON[key].text,
callback_data: `comment_tone_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn phong cách viết , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `comment_tone_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `comment_tone_cancel`},
]
]
}
}
);
}
}
+143
View File
@@ -0,0 +1,143 @@
import {Action, Command, Ctx, On, Wizard, WizardStep} from "nestjs-telegraf";
import {WIZARD_QUOTE_SCENE_MAIN} from "../telegram.constants";
import * as scenes from "telegraf/scenes";
import {ManagerService} from "../../manager/manager.service";
import {QUOTE_TYPE_TELEGRAM_BUTTON_SPECS} from "../../content-writer/prompts/quote.templates";
@Wizard(WIZARD_QUOTE_SCENE_MAIN)
export class QuoteWizard {
constructor(
private readonly managerService: ManagerService,
) {
}
// Bắt lệnh /cancel để thoát
@Command('cancel')
async cancel(@Ctx() ctx: scenes.WizardContext) {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave(); // Thoát khỏi wizard ngay lập tức
}
@WizardStep(1)
async onSceneEnter(@Ctx() ctx: scenes.WizardContext) {
const isQuoteUrl = (ctx.wizard.state as any).quoteAs === 'quotelink';
//@ts-ignore
const text = ctx.message?.text;
console.log(ctx.message);
if (isQuoteUrl) {
const linkX = text.split(' ')[1]; // Gets "my_payload"
if (!linkX) {
await ctx.reply('URL X không hợp lệ');
return ctx.scene.leave();
}
let _linkX = linkX.replace('twitter.com', 'x.com').split('?')[0];
const match = _linkX.match(/status\/(\d+)/);
if (!match) {
await ctx.reply('URL X không hợp lệ');
return ctx.scene.leave();
}
const tweetId = match[1];
// 1. Lưu dữ liệu vào state
(ctx.wizard.state as any).linkUrl = linkX;
(ctx.wizard.state as any).tweetId = tweetId;
}
// @ts-ignore
const inline_keyboards = [];
Object.keys(QUOTE_TYPE_TELEGRAM_BUTTON_SPECS).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: QUOTE_TYPE_TELEGRAM_BUTTON_SPECS[key].text,
callback_data: `quote_type_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn phong cách quote , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
// @ts-ignore
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `quote_type_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `quote_type_cancel`},
]
]
}
}
);
ctx.wizard.next();
}
@WizardStep(2)
@On('text')
@Action(/quote_type_(.+)/)
async step2(@Ctx() ctx: scenes.WizardContext) {
await ctx.answerCbQuery('Đã ghi nhận');
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
if (ctx.text == 'cancel') {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
if (ctx.match === undefined) {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
let quoteType = undefined
// @ts-ignore
const selectedItem = ctx.match[1]
if (selectedItem == 'cancel') {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
if (selectedItem == 'aineeddetect') {
quoteType = undefined;
} else {
quoteType = selectedItem
}
// 2. Lưu vào state để dùng cho các bước sau (nếu cần)
// (ctx.wizard.state as any).type = quoteType;
const language = (ctx.wizard.state as any).language;
const quoteAs = (ctx.wizard.state as any).quoteAs;
const telegramChatId = ''+ctx?.chat?.id!;
// @ts-ignore
await ctx.reply(`Sẽ viết quote bằng ngôn ngữ ${language} với phong cách ${quoteType}-${QUOTE_TYPE_TELEGRAM_BUTTON_SPECS[quoteType]?.text!} ...`);
if (quoteAs === 'quotetext') {
const userTopic = (ctx.wizard.state as any).quoteText;
await this.managerService.manualTriggerQuoteAsTextInput(
userTopic,
quoteType,
language,
telegramChatId
)
} else {
const twitterUrl = (ctx.wizard.state as any).linkUrl;
await this.managerService.manualTriggerQuoteLinkTwitter(
twitterUrl,
quoteType,
language,
telegramChatId
);
}
await ctx.scene.leave();
}
}
@@ -0,0 +1,196 @@
import {Action, Command, Ctx, On, Wizard, WizardStep} from "nestjs-telegraf";
import {WIZARD_WRITER_SCENE_ID} from "../telegram.constants";
import * as scenes from "telegraf/scenes";
import {ManagerService} from "../../manager/manager.service";
import {STYLE_HINTS_TELEGRAM_BUTTON, TONE_HINTS_TELEGRAM_BUTTON} from "../../content-writer/prompts/templates";
@Wizard(WIZARD_WRITER_SCENE_ID)
export class WriterWizard {
constructor(
private readonly managerService: ManagerService,
) {
}
// Bắt lệnh /cancel để thoát
@Command('cancel')
async cancel(@Ctx() ctx: scenes.WizardContext) {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave(); // Thoát khỏi wizard ngay lập tức
}
@WizardStep(1)
async onSceneEnter(@Ctx() ctx: scenes.WizardContext) {
//@ts-ignore
const text = ctx.message?.text;
console.log(ctx.message);
const userTopic = (ctx.wizard.state as any).userTopic;
if (!userTopic) {
await ctx.reply('Không có thông tin về chủ đề bạn cần viết bài');
return ctx.scene.leave();
}
// @ts-ignore
const inline_keyboards = [];
Object.keys(STYLE_HINTS_TELEGRAM_BUTTON).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: STYLE_HINTS_TELEGRAM_BUTTON[key].text,
callback_data: `write_type_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn phong cách viết , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `write_type_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `write_type_cancel`},
]
]
}
}
);
ctx.wizard.next();
}
@WizardStep(2)
@On('text')
@Action(/write_type_(.+)/)
async step2(@Ctx() ctx: scenes.WizardContext) {
if (ctx.text == 'cancel') {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
if (ctx.match === undefined) {
await ctx.reply('Đã hủy quy trình.');
await ctx.deleteMessage();
return ctx.scene.leave();
}
let writeType = undefined
// @ts-ignore
const selectedItem = ctx.match[1]
await ctx.answerCbQuery('Đã ghi nhận writing style=' + selectedItem);
if (selectedItem == 'cancel') {
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
if (selectedItem == 'aineeddetect') {
writeType = undefined;
} else {
writeType = selectedItem
}
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
(ctx.wizard.state as any).writeType = writeType;
// @ts-ignore
const inline_keyboards = [];
Object.keys(TONE_HINTS_TELEGRAM_BUTTON).map(key => {
// @ts-ignore
inline_keyboards.push([{
text: TONE_HINTS_TELEGRAM_BUTTON[key].text,
callback_data: `write_tone_${key}`
}]);
return;
})
await ctx.sendMessage(
`🤖 Chọn âm điệu , bấm \\cancel để huỷ `,
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
...inline_keyboards,
[
{text: "🤖AI tự chọn", callback_data: `write_tone_aineeddetect`},
],
[
{text: "❌Cancel", callback_data: `write_tone_cancel`},
]
]
}
}
);
ctx.wizard.next();
}
@WizardStep(3)
@On('text')
@Action(/write_tone_(.+)/)
async step3(@Ctx() ctx: scenes.WizardContext) {
if (ctx.text == 'cancel') {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
// 1. Lấy dữ liệu từ nút bấm
// @ts-ignore
if (ctx.match === undefined) {
await ctx.deleteMessage();
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
let writeTone = undefined
// @ts-ignore
const selectedItem = ctx.match[1]
await ctx.answerCbQuery('Đã ghi nhận ngữ điệu:' + selectedItem);
// Xóa tin nhắn hiện tại
await ctx.deleteMessage();
if (selectedItem == 'cancel') {
await ctx.reply('Đã hủy quy trình.');
return ctx.scene.leave();
}
if (selectedItem == 'aineeddetect') {
writeTone = undefined;
} else {
writeTone = selectedItem
}
(ctx.wizard.state as any).writeTone = writeTone;
const writeType = (ctx.wizard.state as any).writeType;
const userTopic = (ctx.wizard.state as any).userTopic;
const language = (ctx.wizard.state as any).language;
const telegramChatId = (ctx.wizard.state as any).chatId;
const postLength = undefined;
// @ts-ignore
await ctx.reply(`
Sẽ viết bài bằng ngôn ngữ ${language} độ dài ${postLength} với phong cách ${writeType} và màu sắc ${writeTone} ...
`);
await this.managerService.manualTriggerWriteWithReview({
style: writeType,
keyword: userTopic,
tone: writeTone,
language: language,
telegramChatId,
postLength
})
await ctx.scene.leave();
}
}