Skip to content

Commit

Permalink
featfix: converter dev, textpro fix, misc feats
Browse files Browse the repository at this point in the history
- Fixing TextPro returning 403, by adding spoofed user agent
- Development of IG & TikTok downloader, not launch to prod because still have some bugs
- Adding new command : .request (to request a feature and saves it to JSON)
- Adding new command : .botinfo (to know the bot information)
- Adding new entity in the env.ts : ownerName
  • Loading branch information
gensart-ai committed Mar 24, 2024
1 parent a41eac8 commit a2cab83
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
logs/error-log.txt

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down
1 change: 1 addition & 0 deletions assets/feature-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "whatsapp-bot",
"version": "1.1.0",
"version": "1.3.0",
"description": "WhatsApp Bot Replyer, Powered by Node.js+whatsapp-web.js",
"main": "dist/index.js",
"type": "commonjs",
Expand Down Expand Up @@ -45,4 +45,4 @@
"tough-cookie": "^4.1.3",
"whatsapp-web.js": "^1.23.0"
}
}
}
9 changes: 7 additions & 2 deletions src/command-hive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { log } from '@services/internal/log'
import { indoSlangQuote } from '@services/internal/quote-indo-slang'
import { getPpCouple } from '@services/internal/pp-couple'
import { ghola } from '@services/external/ghola'
import { tiktokDownloader } from '@services/external/tiktok-downloader'
import { requestInfo } from '@services/internal/request-info'
import { instagramDownloader } from '@services/external/instagram-downloader'

type Commands = {
[key: string]: (client: Client, message: Message) => any
Expand All @@ -21,15 +22,19 @@ const commands: Commands = {
// ! Administrative commands
'.log': log,

// * Request Feature
'.request': requestInfo,

// * Help
'.help': commandGuide,

// * Quotes
'.quotes': getForismaticQuotes,
'.indoquotes': indoSlangQuote,


// * Converter
'.tiktok': tiktokDownloader,
'.ig': instagramDownloader,

// * Random Image
'.ppcouple': getPpCouple,
Expand Down
4 changes: 2 additions & 2 deletions src/command-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const routeCommand = async (client: Client, message: Message) => {
const type: string = message.type;
const command: string = message.body;

// 1.0, Currently only listen to text/chat or image messages, otherwise ignore it
// From 1.0, for now the bot only listen to text/chat or image messages, otherwise ignore it
if (['chat', 'image', 'video'].includes(type) == false) {
return 0;
}
Expand All @@ -25,7 +25,7 @@ const routeCommand = async (client: Client, message: Message) => {
wweb.sendMessage(
client,
message.from,
`${config.botShortName} tidak mengerti. Harap ketik \`.help\` untuk mengetahui yang ${config.botShortName} pahami, ${contact?.pushname ?? ''}!`
`${config.botShortName} gak ngerti maksudnya apa😢. Coba ketik \`.help\` biar tau yang ${config.botShortName} paham, makasi ${contact?.pushname ?? ''}!`
)
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@ interface EnvironmentConfiguration {
* Bot codename
*/
botCodeName: string,

/**
* Owner name of the bot
*/
ownerName: string
}

const environmentConfiguration: EnvironmentConfiguration = {
isSendingMessageEnabled: true,

botName: 'Sora Erlyana',
botShortName: 'Sora',
botCodeName: 'SoraErlyana'
botCodeName: 'SoraErlyana',
ownerName: 'Genesaret Johnes'
}

export default environmentConfiguration
91 changes: 91 additions & 0 deletions src/services/external/instagram-downloader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { MessageMedia } from 'whatsapp-web.js';
import { Executor } from '@/command-hive';
import axios, { AxiosError } from 'axios';
import * as wweb from '@utils/wweb';
import * as logger from '@utils/logger';
import * as cheerio from 'cheerio';
import config from '@/env';

const IG_DOWNLOADER_URL = 'https://v3.igdownloader.app/api/ajaxSearch';
const SPOOFED_USER_AGENT = 'Mozilla (Firefox Inc.)';

type InstagramDom = {
status: string,
v?: string,
data: string
}

type InstagramMedia = string | undefined;

const retrieveInstagramDom = async (instagramUrl: string) => {
const response = await axios.post(IG_DOWNLOADER_URL, {
q: instagramUrl,
}, {
withCredentials: true,
headers: {
'content-type': 'application/x-www-form-urlencoded',
'User-Agent': SPOOFED_USER_AGENT
}
});

return response.data;
}

const processInstagramDom = (instagramDom: InstagramDom) => {
if (instagramDom.v === undefined) return undefined;

const $ = cheerio.load(instagramDom.data);
const instagramMediaUrl: InstagramMedia = $('.download-items__btn').find('a.abutton').attr('href');

return instagramMediaUrl;
}

const downloadMedia = async (instagramMediaUrl: string) => {
const response = await axios.get(instagramMediaUrl, {
responseType: 'arraybuffer',
headers: {
'User-Agent': SPOOFED_USER_AGENT
}
});

return {
media: Buffer.from(response.data, 'binary').toString('base64'),
mime_type: response.headers['content-type']
};
}

const instagramDownloader: Executor = async (client, message) => {
const instagramUrl = message.body.split(' ')[1];

if (instagramUrl == undefined) {
wweb.replyMessage(message, `${config.botShortName} tidak melihat adanya URL video IG kamu :(.\n\nGunakan format: \`.ig [URL video IG]\` ya!`);
return 0;
}

try {
const instagramDom: InstagramDom = await retrieveInstagramDom(instagramUrl);
const instagramMediaUrl: InstagramMedia = processInstagramDom(instagramDom);

if (instagramMediaUrl === undefined) {
wweb.replyMessage(message, `${config.botShortName} tidak dapat memproses link IG kamu, coba salin ulang lagi dari Instagram nya ya !`);
return 0;
}

wweb.replyMessage(message, `Tunggu sebentar ya, ${config.botShortName} sedang memproses link IG kamu...`);
// const instagramMedia = await downloadMedia(instagramMediaUrl);

downloadMedia(instagramMediaUrl).then(instagramMedia => {
wweb.replyMessage(message, new MessageMedia(instagramMedia.mime_type, instagramMedia.media));
});
} catch (error) {

const err = error as AxiosError;
const contact = await message.getContact();
logger.logError('instagramDownloader - ' + err.cause ?? err.message + ' by ' + contact?.pushname ?? 'unknown');
wweb.replyMessage(message, `Maaf, ${config.botShortName} mengalami kegagalan saat memprosesnya. Silahkan coba kembali nanti ya! 🙏`);
}
}

export {
instagramDownloader
}
6 changes: 6 additions & 0 deletions src/services/external/tiktok-downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ const downloadTikTokVideo = async (tiktokUrl: string) => {
}

const tiktokDownloader: Executor = async (client, message) => {

// ! Hold the feature, it did not work well
wweb.replyMessage(message, 'Fitur ini sedang dilakukan perbaikan, harap stay tuned untuk informasi selanjutnya :).');
return 0;

const tiktokUrl = message.body.split(' ')[1];

if ((tiktokUrl == undefined) || (tiktokUrl == '')) {
wweb.replyMessage(
message,
`${config.botShortName} tidak melihat adanya URL video TikTok kamu :(.\n\nGunakan format: \`.tiktok [URL video TikTok] ya!\``
);
return 0;
}

try {
Expand Down
20 changes: 20 additions & 0 deletions src/services/internal/bot-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Executor } from '@/command-hive';
import * as wweb from '@utils/wweb';
import config from '@/env';

const botInfo: Executor = async (client, message) => {
const informationArray = [
`⚡Informasi tentang ${config.botName}⚡`,
`Nama: ${config.botName}`,
`Nama Panggilan: ${config.botShortName}`,
`Owner: ${config.ownerName}\n`,

'Tahun Rilis:' + 2024,
'Versi Saat Ini:' + '1.3.0',
'Teknologi Digunakan :',
'- NodeJS',
'- TypeScript',
'- whatsapp-web.js (wwebjs.dev)',
];
wweb.sendMessage(client, message.from, informationArray.join('\n'));
}
15 changes: 9 additions & 6 deletions src/services/internal/command-guide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ const commandGuide: Executor = async (client, message) => {
const commandListMessage: Array<string> = [
`Hai, aku ${config.botName}, siap membantu kamu untuk kegiatan harianmu`,
'Btw, dibawah ini list command yang tersedia: (Bisa ketik perintahnya aja untuk informasi setiap perintahnya ya)\n',
'ℹ *Informasi*',
`.botinfo - Informasi tentang ${config.botName}\n`,
'🦜 *Quotes*',
'.quotes - Quotes Formal',
'.indoquotes - Quotes Slang Indonesia\n',
'🎥 *Converter*',
'.tiktok [link TikTok] - Convert link TikTok ke video (dalam tahap pengembangan)\n',
// '🎥 *Converter*',
// '.ig [link Instagram] - Convert link postingan/reels Instagram\n',
'🎲 *Random*',
'.ppcouple - Gambar PP couple random\n',
'🤖 *Fitur AI*',
Expand All @@ -23,7 +25,6 @@ const commandGuide: Executor = async (client, message) => {
'.s (kirim bersama dengan gambarnya)',
'.st [teks] (kirim bersama dengan gambarnya)\n',
'🖼 *TextPro (Buat teks jadi gambar, dengan gaya)*',
'⚠ *DALAM MASA PERBAIKAN*',
'.neon',
'.lunar',
'.thunder',
Expand All @@ -43,10 +44,12 @@ const commandGuide: Executor = async (client, message) => {
'[⚙] Tiktok Downloader',
'[⚙] Instagram Downloader',
'[⚙] YouTube Downloader',
'[] Bot Information (versi, tech used, etc.)',
'[] Feedback Request Fitur',
'[] Bot Information (versi, tech used, etc.)',
'[] Feedback Request Fitur',
'[⚙] Donasi Semi-Kemanusiaan (Apaan nih ? Coming soon ya😄)\n',
`${config.botShortName} masih tahap pengembangan, banyak perintah juga nantinya ${config.botShortName} bisa lakuin loh >_<, stay tuned yaa. Oh iya, saat ini kamu juga bisa request fitur langsung ke creator ${config.botName}. Bye bye~\n\n`,
`😎 Kamu juga bisa request fitur yang belum ada lho, atau punya saran tertentu, bisa langsung kirim ke ${config.botShortName} dengan format :`,
'`.request [request fitur/saran perbaikan]`',
`Request akan langsung ${config.botShortName} informasikan ke creator. 🫡\n`,
`Detail of ${config.botShortName} : https://gensart.notion.site/SoraErlyana-WhatsApp-Bot-7248504bbe18476e912912a9426b9bad`
];

Expand Down
30 changes: 30 additions & 0 deletions src/services/internal/request-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Executor } from '@/command-hive';
import * as wweb from '@utils/wweb';
import fs from 'fs/promises';

const requestInfo: Executor = async (client, message) => {
let request = message.body.split(' ').slice(1).join(' ');

if (request == '') {
wweb.replyMessage(message, 'Request/sarannya apa nih 😥?')
return 0;
}

const contact = await message.getContact();
request = request + ' | oleh ' + contact?.pushname ?? 'unknown'
await recordRequest(request)
wweb.replyMessage(message, 'Terimakasih ! 😁');
}

const recordRequest = async (text: string) => {
fs.readFile('assets/feature-request.json', 'utf-8')
.then(data => {
const records = JSON.parse(data);
records.push(text)
fs.writeFile('assets/feature-request.json', JSON.stringify(records), 'utf-8')
})
}

export {
requestInfo
}
17 changes: 14 additions & 3 deletions src/utils/textpro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type TextProImageObject = {
mimetype: string
}

const SPOOFED_USER_AGENT = 'Mozilla (Firefox Inc.)';

/**
* Retrieves an image from a given URL using the provided Axios instance.
*
Expand All @@ -51,7 +53,10 @@ type TextProImageObject = {
const getImageFromTextPro = async (imageUrl: string, axiosInstance: AxiosInstance): Promise<TextProImageObject> => {
const textProUrl = 'https://textpro.me';
const imageResponse = await axiosInstance.get(textProUrl + imageUrl, {
'responseType': 'arraybuffer'
responseType: 'arraybuffer',
headers: {
'User-Agent': SPOOFED_USER_AGENT
}
});

const imageBase64: string = Buffer.from(imageResponse.data, 'binary').toString('base64');
Expand Down Expand Up @@ -79,7 +84,8 @@ const getImageUrlFromTextPro = async (metadata: object, axiosInstance: AxiosInst

const response = await axiosInstance.post(textProImageHiveUrl, metadata, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': SPOOFED_USER_AGENT
}
});

Expand All @@ -106,6 +112,7 @@ const getImageMetadataFromTextPro = async (metadata: TextProMetadata): Promise<J
}, {
headers: {
'Content-Type': 'multipart/form-data',
'User-Agent': SPOOFED_USER_AGENT
}
});

Expand All @@ -124,7 +131,11 @@ const getImageMetadataFromTextPro = async (metadata: TextProMetadata): Promise<J
* @return {Promise<TextProToken>} the token retrieved from the TextPro page
*/
const getTokenFromTextProPage = async (textProUrl: string, axiosInstance: AxiosInstance): Promise<TextProToken> => {
const response = await axiosInstance.get(textProUrl);
const response = await axiosInstance.get(textProUrl, {
headers: {
'User-Agent': SPOOFED_USER_AGENT
}
});

if (response.status == 200) {
const $ = cheerio.load(response.data);
Expand Down

0 comments on commit a2cab83

Please sign in to comment.