├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── Agent │ ├── characters │ │ ├── elon.character.json │ │ └── sample.character.json │ ├── index.ts │ ├── schema │ │ └── index.ts │ ├── script │ │ └── summarize.ts │ └── training │ │ ├── FilesTraining.ts │ │ ├── TrainWithAudio.ts │ │ ├── WebsiteScraping.ts │ │ ├── sample │ │ ├── Audio.ts │ │ ├── How_to_be_a_Web_Developer_A_Complete_Beginners_Guide_on_What_to.pdf │ │ ├── LilTjay.mp3 │ │ └── test.txt │ │ └── youtubeURL.ts ├── client │ ├── Github.ts │ ├── Instagram.ts │ ├── Twitter.ts │ └── X-bot │ │ ├── client │ │ └── index.ts │ │ └── index.ts ├── config │ └── logger.ts ├── index.ts ├── secret │ └── index.ts ├── types │ └── puppeteer.d.ts └── utils │ ├── download.ts │ └── index.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | 2 | NODE_ENV=development 3 | 4 | # Instagram credentials 5 | IGusername= #instagram username 6 | IGpassword= #instagram password 7 | 8 | # Twitter credentials 9 | Xusername= #Twitter username 10 | Xpassword= #Twitter password 11 | 12 | 13 | 14 | 15 | 16 | #===> Twitter API AGENT credentials 17 | 18 | TWITTER_API_KEY= #Twitter API key 19 | TWITTER_API_SECRET= #Twitter API secret 20 | TWITTER_ACCESS_TOKEN= #Twitter access token 21 | TWITTER_ACCESS_SECRET= #Twitter access secret 22 | TWITTER_BEARER_TOKEN= #Twitter bearer token 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | # Gemini API keys 32 | # These API keys are legitimate and working for anyone to use. 33 | # Note: Each key allows 1500 requests in 24 hours before switching to the next key. 34 | # With 50 keys, we can make up to 75,000 requests before exhausting the limit. 35 | # Feel free to replace these with your own keys if needed. 36 | 37 | 38 | GEMINI_API_KEY_1=AIzaSyBwOdDKvtandLwzPhtGS_nqFYzkWY0_RQE 39 | GEMINI_API_KEY_2=AIzaSyA35Q0b3PkCw_EssBhlBqFAE7rH7aZChp8 40 | GEMINI_API_KEY_3=AIzaSyAJLEzc0a5VjjemC9a5L3bJMHl8jpbArWI 41 | GEMINI_API_KEY_4=AIzaSyAXV8TEgt-kDDf6exRj_jFd9408kZhUOm4 42 | GEMINI_API_KEY_5=AIzaSyDJ-A4a5d8GTzjSPgH7GRWRbGrq_0V0TeU 43 | GEMINI_API_KEY_6=AIzaSyADwrgo1nFihfsoJPYludXOi4Pi0RdUnr0 44 | GEMINI_API_KEY_7=AIzaSyD5qXyttAb34CZT6veupiAHtt7-u3Lr8KE 45 | GEMINI_API_KEY_8=AIzaSyBDvTIucsN312ENDDvpEJVqufmi13eCYmk 46 | GEMINI_API_KEY_9=AIzaSyCxmmAQbjU_lqk1nfKY_lbfpjXBdjWXhoE 47 | GEMINI_API_KEY_10=AIzaSyDipoc_rZsk7s0bFet0XogMpJJL5awxAfc 48 | 49 | 50 | GEMINI_API_KEY_11=AIzaSyDzMKniDlnYCr6S6RiPvNF96ZPFpppVXW0 51 | GEMINI_API_KEY_12=AIzaSyDZ996mnWIghZRYKfrYi8o_rrjsooJ_MMc 52 | GEMINI_API_KEY_13=AIzaSyDyvryzpY7rMyd0sjIvX2N3cwy8X9wkx9c 53 | GEMINI_API_KEY_14=AIzaSyA4dSHz7E6iAt523e1hx1R3hB8KUM_6bH4 54 | GEMINI_API_KEY_15=AIzaSyBvrBjnoJUBUZOYkp_PpCLbh38xlSoEd_o 55 | GEMINI_API_KEY_16=AIzaSyDipURCqJXVICfU3AMWps8MmrMA_RZoTBM 56 | GEMINI_API_KEY_17=AIzaSyD44lzlrygGfmivgPvQBM9ricDotKqrCH0 57 | GEMINI_API_KEY_18=AIzaSyAbtI08u7XmJXjjdD1YRwR7E_-vBBMfjGY 58 | GEMINI_API_KEY_19=AIzaSyBMyJOeEQRbbzxBaPWxjXnA6UYRHBZNfME 59 | GEMINI_API_KEY_20=AIzaSyA4ufjguQwiw5GkHJOM1L0FNQYNzkWxrIw 60 | 61 | GEMINI_API_KEY_21=AIzaSyAVFujfonBAJEs6ZpdAmu2FA9kVmKcyV50 62 | GEMINI_API_KEY_22=AIzaSyB_Wr0FVTazvPJejYC-aagtWjcCq9x9OmI 63 | GEMINI_API_KEY_23=AIzaSyDih1AwL-6jCBLPSebuCERBS-pCqufK_t0 64 | GEMINI_API_KEY_24=AIzaSyCDodMWKAX_TAUzOcltBspp1oei3prUFbc 65 | GEMINI_API_KEY_25=AIzaSyCJHToKaxS23LjFUo5Rs-wSwt7RYAnahYQ 66 | GEMINI_API_KEY_26=AIzaSyDQ00_d2hWcHH8SgAYT9SrspD33GCUV9ag 67 | GEMINI_API_KEY_27=AIzaSyCslrd7KQwoY3H_L8ATgDmV3fFloduGdE4 68 | GEMINI_API_KEY_28=AIzaSyAyTaNAuiCsZTlNJQk2wqBLVf9XSXdSiUQ 69 | GEMINI_API_KEY_29=AIzaSyBdeU2wA1vIr1Jyme9UI2jWegAaQflENN0 70 | GEMINI_API_KEY_30=AIzaSyAINs-EQtF6F8IYKURJGnOibXZGisMal_8 71 | 72 | GEMINI_API_KEY_31=AIzaSyAWBZrUkee3kHDu4AKxp6AYkC4e23KZoNk 73 | GEMINI_API_KEY_32=AIzaSyB8ytUpjFfUBaeuli85YYxaYwYxVQoNaQc 74 | GEMINI_API_KEY_33=AIzaSyBwnGXSnqWK7r0OVhojde_mZ2ci54079ZM 75 | GEMINI_API_KEY_34=AIzaSyCjzC8n0icGInF0Jq2yjRlItvBR_AuGQt4 76 | GEMINI_API_KEY_35=AIzaSyDX9Stm9b2W8HsIz2U5KpmuQRMf9tdfO7Q 77 | GEMINI_API_KEY_36=AIzaSyAVPIjcvcHVzoN_NxrycLGaZxecrXDqIQ4 78 | GEMINI_API_KEY_37=AIzaSyDvvNx5kgRFFHu8McnDZwI5Uprsz7JKwag 79 | GEMINI_API_KEY_38=AIzaSyAWbSauhWtwG6S7UknlGDVaGqH9RMqkGOw 80 | GEMINI_API_KEY_39=AIzaSyDUIYXJT8mPEoPf7727S23XB_JXDYgIcz8 81 | GEMINI_API_KEY_40=AIzaSyCCFXSUgJ-l__4RojxvmEwvXk_qndiqMsE 82 | 83 | GEMINI_API_KEY_41=AIzaSyDSLIFhGk-8REns2S4po5CvWQv6FPIET2E 84 | GEMINI_API_KEY_42=AIzaSyDFURTJ6CkTUJvWfSyqbB8xdaITKTOmYO8 85 | GEMINI_API_KEY_43=AIzaSyBZo7MsIJ9nU5ZsFzeKcdSY3Zvy0ceTdGY 86 | GEMINI_API_KEY_44=AIzaSyA-Z4GHz6peXdoJmscYUUWKYU76rsL9KSE 87 | GEMINI_API_KEY_45=AIzaSyAWPp8DdXg9L9GfuEoqp_4pnfC5S4czrrQ 88 | GEMINI_API_KEY_46=AIzaSyBSJAv4OOm3D8XooJEynNANDDh6dkc07wE 89 | GEMINI_API_KEY_47=AIzaSyAnJtJU5ws9cpXZ6R424YxyixgITQE1hOo 90 | GEMINI_API_KEY_48=AIzaSyC_z9nt0UbWNHMsxiunfTJM8r3dlNJkteQ 91 | GEMINI_API_KEY_49=AIzaSyB5KtSaEGHO0q1HhypSk034fDJ2zYV6HPs 92 | GEMINI_API_KEY_50=AIzaSyDyg7tBwd3yDBH9dHiSdTwuL_0Fbf3h9xE 93 | 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | build 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | cookies 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | *.lcov 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Snowpack dependency directory (https://snowpack.dev/) 41 | web_modules/ 42 | 43 | # TypeScript cache 44 | *.tsbuildinfo 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Microbundle cache 53 | .rpt2_cache/ 54 | .rts2_cache_cjs/ 55 | .rts2_cache_es/ 56 | .rts2_cache_umd/ 57 | 58 | # Optional REPL history 59 | .node_repl_history 60 | 61 | # Output of 'npm pack' 62 | *.tgz 63 | .vscode 64 | # Yarn Integrity file 65 | .yarn-integrity 66 | img.js 67 | # dotenv environment variables file 68 | twitter.js 69 | .env 70 | .env.test 71 | .env.production 72 | 73 | start.sh 74 | # parcel-bundler cache (https://parceljs.org/) 75 | .cache 76 | .parcel-cache 77 | 78 | # Next.js build output 79 | .next 80 | out 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and not Next.js 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # VS Code settings 107 | .vscode/* 108 | !.vscode/settings.json 109 | !.vscode/tasks.json 110 | !.vscode/launch.json 111 | !.vscode/extensions.json 112 | 113 | # SvelteKit build / generate output 114 | .svelte-kit 115 | 116 | # Sapper build / generate output 117 | __sapper__ 118 | 119 | # Svelte build / generate output 120 | public/build 121 | 122 | # Storybook build outputs 123 | .out 124 | .storybook-out 125 | 126 | # Plop js template files 127 | .plop-templates -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Riona-AI-Agent 🌸 2 | 3 | Riona-AI-Agent is an AI-powered automation tool designed to interact with various social media platforms like Instagram, Twitter, and GitHub. It leverages advanced AI models to generate engaging content, automate interactions, and manage social media accounts efficiently. 4 | 5 | 6 | Before using the automation features, you can train the agent character with personalized content. You can upload your content through any of the following: 7 | 8 | - **YouTube Video URL** 🎥 9 | - **Audio File** 🎙️ 10 | - **Portfolio or Website Link** 🌐 11 | - **File Formats Supported**: PDF, DOC, DOCX, TXT 📄 12 | 13 | ## Features 14 | 15 | - **Instagram Automation**: Automatically log in, like posts, and leave thoughtful comments. 16 | - **Twitter Automation**: (Coming soon) Automatically tweet, retweet, and like tweets. 17 | - **GitHub Automation**: (Coming soon) Automatically manage repositories, issues, and pull requests. 18 | - **AI-Powered Content Generation**: Use Google Generative AI to generate engaging comments and posts. 19 | - **Proxy Support**: Use proxies to manage multiple accounts and avoid rate limits. 20 | - **Cookie Management**: Save and load cookies to maintain sessions across restarts. 21 | 22 | ## Installation 23 | 24 | 1. **Clone the repository**: 25 | ```sh 26 | git clone https://github.com/david-patrick-chuks/Riona-AI-Agent.git 27 | cd Riona-AI-Agent 28 | ``` 29 | 30 | 2. **Install dependencies**: 31 | ```sh 32 | npm install 33 | ``` 34 | 35 | 3. **Set up environment variables**: 36 | rename the [.env.example](http://_vscodecontentref_/1) file to [.env](http://_vscodecontentref_/1) file in the root directory and add your credentials and API keys (optional). Refer to the [.env.example](http://_vscodecontentref_/2) file for the required variables. 37 | ```dotenv 38 | # Instagram credentials 39 | IGusername=your_instagram_username 40 | IGpassword=your_instagram_password 41 | 42 | # Twitter credentials 43 | Xusername=your_twitter_username 44 | Xpassword=your_twitter_password 45 | 46 | ``` 47 | 48 | ## Usage 49 | 50 | 1. **Run the agent**: 51 | ```sh 52 | npm start 53 | ``` 54 | 55 | 64 | 65 | ## Project Structure 66 | 67 | - **src/client**: Contains the main logic for interacting with social media platforms. 68 | - **src/config**: Configuration files, including the logger setup. 69 | - **src/utils**: Utility functions for handling errors, cookies, etc. 70 | - **src/schema**: Schema definitions for AI-generated content. 71 | 72 | ## Logging 73 | 74 | The project uses a custom logger to log information, warnings, and errors. Logs are saved in the [logs](http://_vscodecontentref_/3) directory. 75 | 76 | ## Error Handling 77 | 78 | Process-level error handlers are set up to catch unhandled promise rejections, uncaught exceptions, and process warnings. Errors are logged using the custom logger. 79 | 80 | ## Contributing 81 | 82 | Contributions are welcome! Please fork the repository and submit a pull request with your changes. 83 | 84 | ## License 85 | 86 | This project is licensed under the MIT License. See the LICENSE file for details. 87 | 88 | ## Acknowledgements 89 | 90 | - [Google Generative AI](https://ai.google/tools/) for providing the AI models. 91 | - [Puppeteer](https://github.com/puppeteer/puppeteer) for browser automation. 92 | - [puppeteer-extra](https://github.com/berstend/puppeteer-extra) for additional plugins and enhancements. 93 | 94 | --- 95 | 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riona-ai-agent", 3 | "version": "1.0.0", 4 | "main": "src/index.ts", 5 | "scripts": { 6 | "start": "tsc && node build/index.js", 7 | "train-model" : "tsc && node build/Agent/training/Model.js", 8 | "train:link": "tsc && node build/Agent/training/WebsiteScraping.js", 9 | "train:audio" : "tsc && node build/Agent/training/Audio.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "description": "Riona-AI-Agent is an AI-powered automation tool designed to interact with various social media platforms.", 15 | "dependencies": { 16 | "@google/generative-ai": "^0.21.0", 17 | "axios": "^1.7.9", 18 | "csv-parser": "^3.1.0", 19 | "dompurify": "^3.2.3", 20 | "dotenv": "^16.4.7", 21 | "express": "^4.21.2", 22 | "jsdom": "^26.0.0", 23 | "mammoth": "^1.9.0", 24 | "node-cron": "^3.0.3", 25 | "pdf-parse": "^1.1.1", 26 | "playwright": "^1.49.1", 27 | "proxy-chain": "^2.5.6", 28 | "puppeteer": "^24.0.0", 29 | "puppeteer-extra": "^3.3.6", 30 | "puppeteer-extra-plugin-adblocker": "^2.13.6", 31 | "puppeteer-extra-plugin-stealth": "^2.11.2", 32 | "request": "^2.88.2", 33 | "textract": "^2.5.0", 34 | "twitter-api-v2": "^1.19.0", 35 | "typescript": "^5.7.3", 36 | "user-agents": "^1.1.416", 37 | "winston": "^3.17.0", 38 | "winston-daily-rotate-file": "^5.0.0", 39 | "youtube-transcript": "^1.2.1" 40 | }, 41 | "devDependencies": { 42 | "@types/jsdom": "^21.1.7", 43 | "@types/mime-types": "^2.1.4", 44 | "@types/pdf-parse": "^1.1.4", 45 | "@types/puppeteer": "^5.4.7", 46 | "@types/request": "^2.48.12", 47 | "@types/textract": "^2.4.5", 48 | "@types/user-agents": "^1.0.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Agent/characters/elon.character.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Elon Musk", 3 | "clients": ["twiiter, instagram"], 4 | "settings": { 5 | "voice": { 6 | "model": "en_US-male-medium" 7 | } 8 | }, 9 | "bio": [ 10 | "CEO of SpaceX and Tesla", 11 | "Founder of The Boring Company and Neuralink", 12 | "Co-founder of OpenAI", 13 | "Visionary entrepreneur and engineer", 14 | "Pioneer in space exploration and electric vehicles", 15 | "Advocate for sustainable energy and AI safety", 16 | "Known for ambitious projects and innovative thinking", 17 | "Influential figure in technology and business" 18 | ], 19 | "lore": [ 20 | "Founded SpaceX to revolutionize space travel", 21 | "Developed Tesla to accelerate the world's transition to sustainable energy", 22 | "Launched The Boring Company to solve urban traffic problems", 23 | "Created Neuralink to merge humans with AI", 24 | "Co-founded OpenAI to ensure AI benefits all of humanity", 25 | "Known for ambitious goals like colonizing Mars", 26 | "Advocate for renewable energy and reducing carbon footprint", 27 | "Influential in the development of electric vehicles and autonomous driving" 28 | ], 29 | "knowledge": [ 30 | "Deep understanding of rocket science and space travel", 31 | "Expert in electric vehicle technology and renewable energy", 32 | "Knowledgeable about AI and its potential risks", 33 | "Experienced in business strategy and entrepreneurship", 34 | "Familiar with urban planning and infrastructure projects", 35 | "Insightful about the future of technology and innovation", 36 | "Aware of global challenges and solutions for sustainability", 37 | "Proficient in engineering and product development" 38 | ], 39 | "messageExamples": [ 40 | [ 41 | { 42 | "user": "{{user1}}", 43 | "content": { 44 | "text": "What's your vision for the future of space travel?" 45 | } 46 | }, 47 | { 48 | "user": "elon", 49 | "content": { 50 | "text": "I believe that humanity must become a multi-planetary species to ensure our survival. SpaceX is working towards making space travel more affordable and accessible, with the ultimate goal of establishing a self-sustaining colony on Mars." 51 | } 52 | } 53 | ], 54 | [ 55 | { 56 | "user": "{{user1}}", 57 | "content": { 58 | "text": "How do you see the future of electric vehicles?" 59 | } 60 | }, 61 | { 62 | "user": "elon", 63 | "content": { 64 | "text": "The future of electric vehicles is incredibly promising. Tesla is leading the way in developing advanced battery technology and autonomous driving capabilities. Our goal is to accelerate the world's transition to sustainable energy and reduce our dependence on fossil fuels." 65 | } 66 | } 67 | ], 68 | [ 69 | { 70 | "user": "{{user1}}", 71 | "content": { 72 | "text": "What are your thoughts on AI safety?" 73 | } 74 | }, 75 | { 76 | "user": "elon", 77 | "content": { 78 | "text": "AI has the potential to be incredibly beneficial, but it also poses significant risks if not properly managed. That's why I co-founded OpenAI, to ensure that AI is developed safely and ethically, and that its benefits are shared by all of humanity." 79 | } 80 | } 81 | ] 82 | ], 83 | "postExamples": [ 84 | "Excited to announce the latest advancements in Tesla's autonomous driving technology. The future of transportation is here!", 85 | "SpaceX is making great progress on the Starship project. Mars, here we come!", 86 | "Proud of the work we're doing at Neuralink to develop brain-machine interfaces. The potential applications are mind-blowing.", 87 | "The Boring Company is working on innovative solutions to solve urban traffic problems. Stay tuned for updates!", 88 | "OpenAI is committed to ensuring that AI benefits all of humanity. Exciting developments ahead!" 89 | ], 90 | "topics": [ 91 | "space travel", 92 | "electric vehicles", 93 | "AI safety", 94 | "sustainable energy", 95 | "urban planning", 96 | "brain-machine interfaces", 97 | "entrepreneurship", 98 | "technology innovation" 99 | ], 100 | "style": { 101 | "all": [ 102 | "uses concise and clear language", 103 | "emphasizes innovation and progress", 104 | "references specific projects and goals", 105 | "mentions sustainability and renewable energy", 106 | "uses optimistic and forward-thinking tone", 107 | "emphasizes the importance of technology and science", 108 | "mentions collaboration and teamwork", 109 | "references personal involvement in projects", 110 | "uses technical terms and industry jargon", 111 | "emphasizes the potential of future technologies" 112 | ], 113 | "chat": [ 114 | "directly addresses questioner's concerns", 115 | "provides detailed explanations", 116 | "references personal experiences and achievements", 117 | "emphasizes the importance of innovation", 118 | "mentions specific projects and goals", 119 | "uses optimistic and forward-thinking tone", 120 | "emphasizes collaboration and teamwork", 121 | "references technical details and industry knowledge" 122 | ], 123 | "post": [ 124 | "uses concise and impactful language", 125 | "emphasizes innovation and progress", 126 | "mentions specific projects and achievements", 127 | "uses optimistic and forward-thinking tone", 128 | "references sustainability and renewable energy", 129 | "emphasizes the importance of technology and science", 130 | "mentions collaboration and teamwork", 131 | "references personal involvement in projects" 132 | ] 133 | }, 134 | "adjectives": [ 135 | "innovative", 136 | "sustainable", 137 | "ambitious", 138 | "visionary", 139 | "groundbreaking", 140 | "revolutionary", 141 | "cutting-edge", 142 | "influential", 143 | "pioneering", 144 | "transformative", 145 | "advanced", 146 | "promising", 147 | "exciting", 148 | "mind-blowing", 149 | "progressive", 150 | "collaborative", 151 | "forward-thinking", 152 | "technical", 153 | "impactful", 154 | "optimistic" 155 | ] 156 | } -------------------------------------------------------------------------------- /src/Agent/characters/sample.character.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample agent", 3 | "clients": [], 4 | "settings": { 5 | "voice": { 6 | "model": "en_US-male-medium" 7 | } 8 | }, 9 | "bio": [], 10 | "lore": [], 11 | "knowledge": [], 12 | "messageExamples": [ 13 | [ 14 | { 15 | "user": "{{user1}}", 16 | "content": { 17 | "text": "" 18 | } 19 | }, 20 | { 21 | "user": "sample agent", 22 | "content": { 23 | "text": "" 24 | } 25 | } 26 | ] 27 | 28 | ], 29 | "postExamples": [ ], 30 | "topics": [], 31 | "style": { 32 | "all": [], 33 | "chat": [], 34 | "post": [ ] 35 | }, 36 | "adjectives": [] 37 | } 38 | -------------------------------------------------------------------------------- /src/Agent/index.ts: -------------------------------------------------------------------------------- 1 | import { GoogleGenerativeAI } from "@google/generative-ai"; 2 | import logger from "../config/logger"; 3 | import { geminiApiKeys } from "../secret"; 4 | import { handleError } from "../utils"; 5 | import { InstagramCommentSchema } from "./schema"; 6 | 7 | 8 | 9 | 10 | export async function runAgent(schema: InstagramCommentSchema, prompt: string): Promise { 11 | let currentApiKeyIndex = 0; 12 | let geminiApiKey = geminiApiKeys[currentApiKeyIndex]; 13 | 14 | if (!geminiApiKey) { 15 | logger.error("No Gemini API key available."); 16 | return "No API key available."; 17 | } 18 | const generationConfig = { 19 | responseMimeType: "application/json", 20 | responseSchema: schema, 21 | }; 22 | 23 | const googleAI = new GoogleGenerativeAI(geminiApiKey); 24 | const model = googleAI.getGenerativeModel({ 25 | model: "gemini-1.5-flash", 26 | generationConfig, 27 | }); 28 | 29 | try { 30 | const result = await model.generateContent(prompt); 31 | 32 | if (!result || !result.response) { 33 | logger.info("No response received from the AI model. || Service Unavailable"); 34 | return "Service unavailable!"; 35 | } 36 | 37 | const responseText = result.response.text(); 38 | const data = JSON.parse(responseText); 39 | 40 | return data; 41 | 42 | } catch (error) { 43 | await handleError(error, currentApiKeyIndex, schema, prompt, runAgent); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Agent/schema/index.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "@google/generative-ai"; 2 | 3 | export interface InstagramCommentSchema { 4 | description: string; 5 | type: SchemaType; 6 | items: { 7 | type: SchemaType; 8 | properties: { 9 | comment: { 10 | type: SchemaType; 11 | description: string; 12 | nullable: boolean; 13 | }; 14 | viralRate: { 15 | type: SchemaType; 16 | description: string; 17 | nullable: boolean; 18 | }; 19 | commentTokenCount: { 20 | type: SchemaType; 21 | description: string; 22 | nullable: boolean; 23 | }; 24 | }; 25 | required: string[]; 26 | }; 27 | } 28 | 29 | export const getInstagramCommentSchema = (): InstagramCommentSchema => { 30 | return { 31 | description: `Lists comments that are engaging and have the potential to attract more likes and go viral.`, 32 | type: SchemaType.ARRAY, 33 | items: { 34 | type: SchemaType.OBJECT, 35 | properties: { 36 | comment: { 37 | type: SchemaType.STRING, 38 | description: "A comment between 150 and 250 characters.", 39 | nullable: false, 40 | }, 41 | viralRate: { 42 | type: SchemaType.NUMBER, 43 | description: "The viral rate, measured on a scale of 0 to 100.", 44 | nullable: false, 45 | }, 46 | commentTokenCount: { 47 | type: SchemaType.NUMBER, 48 | description: "The total number of tokens in the comment.", 49 | nullable: false, 50 | }, 51 | }, 52 | required: [ 53 | "comment", 54 | "viralRate", 55 | "commentTokenCount" 56 | ], 57 | }, 58 | }; 59 | }; 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/Agent/script/summarize.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { GoogleGenerativeAI, SchemaType } from "@google/generative-ai"; 3 | import logger from "../../config/logger"; 4 | 5 | import dotenv from "dotenv"; 6 | dotenv.config(); 7 | 8 | 9 | const geminiApiKeys = [ 10 | process.env.GEMINI_API_KEY_1 || "API_KEY_1", 11 | process.env.GEMINI_API_KEY_2 || "API_KEY_2", 12 | process.env.GEMINI_API_KEY_3 || "API_KEY_3", 13 | process.env.GEMINI_API_KEY_4 || "API_KEY_4", 14 | process.env.GEMINI_API_KEY_5 || "API_KEY_5", 15 | process.env.GEMINI_API_KEY_6 || "API_KEY_6", 16 | process.env.GEMINI_API_KEY_7 || "API_KEY_7", 17 | process.env.GEMINI_API_KEY_8 || "API_KEY_8", 18 | process.env.GEMINI_API_KEY_9 || "API_KEY_9", 19 | process.env.GEMINI_API_KEY_10 || "API_KEY_10", 20 | process.env.GEMINI_API_KEY_11 || "API_KEY_11", 21 | process.env.GEMINI_API_KEY_12 || "API_KEY_12", 22 | process.env.GEMINI_API_KEY_13 || "API_KEY_13", 23 | process.env.GEMINI_API_KEY_14 || "API_KEY_14", 24 | process.env.GEMINI_API_KEY_15 || "API_KEY_15", 25 | process.env.GEMINI_API_KEY_16 || "API_KEY_16", 26 | process.env.GEMINI_API_KEY_17 || "API_KEY_17", 27 | process.env.GEMINI_API_KEY_18 || "API_KEY_18", 28 | process.env.GEMINI_API_KEY_19 || "API_KEY_19", 29 | process.env.GEMINI_API_KEY_20 || "API_KEY_20", 30 | process.env.GEMINI_API_KEY_21 || "API_KEY_21", 31 | process.env.GEMINI_API_KEY_22 || "API_KEY_22", 32 | process.env.GEMINI_API_KEY_23 || "API_KEY_23", 33 | process.env.GEMINI_API_KEY_24 || "API_KEY_24", 34 | process.env.GEMINI_API_KEY_25 || "API_KEY_25", 35 | process.env.GEMINI_API_KEY_26 || "API_KEY_26", 36 | process.env.GEMINI_API_KEY_27 || "API_KEY_27", 37 | process.env.GEMINI_API_KEY_28 || "API_KEY_28", 38 | process.env.GEMINI_API_KEY_29 || "API_KEY_29", 39 | process.env.GEMINI_API_KEY_30 || "API_KEY_30", 40 | process.env.GEMINI_API_KEY_31 || "API_KEY_31", 41 | process.env.GEMINI_API_KEY_32 || "API_KEY_32", 42 | process.env.GEMINI_API_KEY_33 || "API_KEY_33", 43 | process.env.GEMINI_API_KEY_34 || "API_KEY_34", 44 | process.env.GEMINI_API_KEY_35 || "API_KEY_35", 45 | process.env.GEMINI_API_KEY_36 || "API_KEY_36", 46 | process.env.GEMINI_API_KEY_37 || "API_KEY_37", 47 | process.env.GEMINI_API_KEY_38 || "API_KEY_38", 48 | process.env.GEMINI_API_KEY_39 || "API_KEY_39", 49 | process.env.GEMINI_API_KEY_40 || "API_KEY_40", 50 | process.env.GEMINI_API_KEY_41 || "API_KEY_41", 51 | process.env.GEMINI_API_KEY_42 || "API_KEY_42", 52 | process.env.GEMINI_API_KEY_43 || "API_KEY_43", 53 | process.env.GEMINI_API_KEY_44 || "API_KEY_44", 54 | process.env.GEMINI_API_KEY_45 || "API_KEY_45", 55 | process.env.GEMINI_API_KEY_46 || "API_KEY_46", 56 | process.env.GEMINI_API_KEY_47 || "API_KEY_47", 57 | process.env.GEMINI_API_KEY_48 || "API_KEY_48", 58 | process.env.GEMINI_API_KEY_49 || "API_KEY_49", 59 | process.env.GEMINI_API_KEY_50 || "API_KEY_50", 60 | ]; 61 | 62 | 63 | let currentApiKeyIndex = 0; // Keeps track of the current API key in use 64 | 65 | // Function to get the next API key in the list 66 | const getNextApiKey = () => { 67 | currentApiKeyIndex = (currentApiKeyIndex + 1) % geminiApiKeys.length; // Circular rotation of API keys 68 | return geminiApiKeys[currentApiKeyIndex]; 69 | }; 70 | 71 | function cleanTranscript(rawTranscript: string): string { 72 | // Remove music or any similar tags like [Music], [Applause], etc. 73 | const cleaned = rawTranscript.replace(/\[.*?\]/g, ''); 74 | const decoded = cleaned.replace(/&#39;/g, "'"); 75 | return decoded; 76 | } 77 | 78 | // comment 79 | const MainPrompt = "You are tasked with transforming the YouTube video transcript into a training-ready system prompt. The goal is to format the transcript into structured data without reducing its content, and prepare it for use in training another AI model."; 80 | 81 | const getYouTubeTranscriptSchema = () => { 82 | return { 83 | description: `Transform the YouTube video transcript into a structured format, suitable for training another AI model. Ensure the content remains intact and is formatted correctly.`, 84 | type: SchemaType.ARRAY, 85 | items: { 86 | type: SchemaType.OBJECT, 87 | properties: { 88 | transcriptTitle: { 89 | type: SchemaType.STRING, 90 | description: "The title of the YouTube video transcript.", 91 | nullable: false, 92 | }, 93 | fullTranscript: { 94 | type: SchemaType.STRING, 95 | description: "The full, unaltered YouTube video transcript.", 96 | nullable: false, 97 | }, 98 | contentTokenCount: { 99 | type: SchemaType.STRING, 100 | description: "The total number of tokens in the full transcript.", 101 | nullable: false, 102 | }, 103 | }, 104 | required: [ 105 | "transcriptTitle", 106 | "fullTranscript", 107 | "contentTokenCount", 108 | ], 109 | }, 110 | }; 111 | }; 112 | 113 | export async function generateTrainingPrompt(transcript: string, prompt: string = MainPrompt): Promise { 114 | let geminiApiKey = geminiApiKeys[currentApiKeyIndex]; 115 | let currentApiKeyName = `GEMINI_API_KEY_${currentApiKeyIndex + 1}`; 116 | 117 | if (!geminiApiKey) { 118 | logger.error("No Gemini API key available."); 119 | return "No API key available."; 120 | } 121 | 122 | const schema = await getYouTubeTranscriptSchema(); 123 | const generationConfig = { 124 | responseMimeType: "application/json", 125 | responseSchema: schema, 126 | }; 127 | 128 | const googleAI = new GoogleGenerativeAI(geminiApiKey); 129 | const model = googleAI.getGenerativeModel({ 130 | model: "gemini-1.5-flash", 131 | generationConfig, 132 | }); 133 | 134 | 135 | const cleanedTranscript = cleanTranscript(transcript); 136 | // Combine the prompt, title, and transcript for processing 137 | const combinedPrompt = `${prompt}\n\nVideo Transcript:\n${cleanedTranscript}`; 138 | 139 | try { 140 | const result = await model.generateContent(combinedPrompt); 141 | 142 | if (!result || !result.response) { 143 | logger.info("No response received from the AI model. || Service Unavailable"); 144 | return "Service unavailable!"; 145 | } 146 | 147 | const responseText = result.response.text(); 148 | const data = JSON.parse(responseText); 149 | 150 | return data; 151 | 152 | } catch (error) { 153 | if (error instanceof Error) { 154 | if (error.message.includes("429 Too Many Requests")) { 155 | logger.error(`---${currentApiKeyName} limit exhausted, switching to the next API key...`); 156 | geminiApiKey = getNextApiKey(); 157 | currentApiKeyName = `GEMINI_API_KEY_${currentApiKeyIndex + 1}`; 158 | return generateTrainingPrompt(transcript, prompt); 159 | } else if (error.message.includes("503 Service Unavailable")) { 160 | logger.error("Service is temporarily unavailable. Retrying..."); 161 | await new Promise(resolve => setTimeout(resolve, 5000)); 162 | return generateTrainingPrompt(transcript, prompt); 163 | } else { 164 | logger.error("Error generating training prompt:", error.message); 165 | return `An error occurred: ${error.message}`; 166 | } 167 | } else { 168 | logger.error("An unknown error occurred:", error); 169 | return "An unknown error occurred."; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Agent/training/FilesTraining.ts: -------------------------------------------------------------------------------- 1 | import pdfParse from 'pdf-parse'; 2 | import mammoth from 'mammoth'; 3 | import textract from 'textract'; 4 | import csvParser from 'csv-parser'; 5 | import { Readable } from 'stream'; 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | 9 | export type SupportedFileType = 'pdf' | 'doc' | 'docx' | 'csv' | 'txt'; 10 | 11 | export async function parseFile(fileBuffer: Buffer, fileType: SupportedFileType): Promise { 12 | let content = ''; 13 | 14 | if (fileType === 'pdf') { 15 | const data = await pdfParse(fileBuffer); 16 | content = data.text; 17 | } else if (fileType === 'docx') { 18 | const result = await mammoth.extractRawText({ buffer: fileBuffer }); 19 | content = result.value; 20 | } else if (fileType === 'doc') { 21 | content = await new Promise((resolve, reject) => { 22 | textract.fromBufferWithMime('application/msword', fileBuffer, (error, text) => { 23 | if (error) reject(error); 24 | else resolve(text); 25 | }); 26 | }); 27 | } else if (fileType === 'csv') { 28 | content = await new Promise((resolve, reject) => { 29 | const results: string[] = []; 30 | Readable.from(fileBuffer) 31 | .pipe(csvParser()) 32 | .on('data', (data) => results.push(JSON.stringify(data))) 33 | .on('end', () => resolve(results.join('\n'))) 34 | .on('error', (err) => reject(err)); 35 | }); 36 | } else if (fileType === 'txt') { 37 | content = fileBuffer.toString('utf8'); 38 | } else { 39 | throw new Error(`Unsupported file type: ${fileType}`); 40 | } 41 | 42 | return content; 43 | } 44 | 45 | 46 | 47 | 48 | 49 | 50 | async function testParse() { 51 | try { 52 | // Define the file path and type 53 | const filePath = path.join(__dirname, 'test.txt'); 54 | const fileType: SupportedFileType = 'txt'; // Change to match your test file's format 55 | 56 | // Read the file into a buffer 57 | const fileBuffer = fs.readFileSync(filePath); 58 | 59 | // Call the parseFile function 60 | const content = await parseFile(fileBuffer, fileType); 61 | console.log('Parsed Content:', content); 62 | } catch (error) { 63 | console.error('Error parsing file:', error); 64 | } 65 | } 66 | 67 | // Execute the test function 68 | testParse(); -------------------------------------------------------------------------------- /src/Agent/training/TrainWithAudio.ts: -------------------------------------------------------------------------------- 1 | import { GoogleGenerativeAI } from "@google/generative-ai"; 2 | import { FileState, GoogleAIFileManager } from "@google/generative-ai/server"; 3 | import dotenv from "dotenv"; 4 | import fs from "fs"; 5 | 6 | dotenv.config(); 7 | 8 | const apiKey = process.env.GEMINI_API_KEY_41 as string; 9 | if (!apiKey) { 10 | throw new Error("API key is missing"); 11 | } 12 | 13 | export class AIAudioFileService { 14 | 15 | private fileManager: GoogleAIFileManager; 16 | private genAI: GoogleGenerativeAI; 17 | 18 | constructor() { 19 | this.fileManager = new GoogleAIFileManager(apiKey); 20 | this.genAI = new GoogleGenerativeAI(apiKey); 21 | } 22 | /** 23 | * Uploads the files to Google AIFileManager, i.e a 48 hours temp storage. 24 | * @param filePath - The local path of the file to upload. 25 | * @param displayName - The display name for the uploaded file. 26 | * @param mimeType - The MIME type of the file. 27 | */ 28 | async processFile(filePath: string, displayName: string, mimeType: string): Promise { 29 | try { 30 | const uploadResult = await this.fileManager.uploadFile(filePath, { 31 | mimeType, 32 | displayName, 33 | }); 34 | 35 | let file = await this.fileManager.getFile(uploadResult.file.name); 36 | 37 | // Wait for the file to be processed 38 | while (file.state === FileState.PROCESSING) { 39 | process.stdout.write("."); 40 | await new Promise((resolve) => setTimeout(resolve, 10_000)); 41 | file = await this.fileManager.getFile(uploadResult.file.name); 42 | } 43 | 44 | if (file.state === FileState.FAILED) { 45 | throw new Error("File processing failed."); 46 | } 47 | 48 | // Generate content using Gemini 49 | const model = this.genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); 50 | 51 | const result = await model.generateContent([ 52 | "Tell me about this audio clip.", 53 | { 54 | fileData: { 55 | fileUri: uploadResult.file.uri, 56 | mimeType: uploadResult.file.mimeType, 57 | }, 58 | }, 59 | ]); 60 | 61 | // Delete the uploaded file from Google AI 62 | await this.fileManager.deleteFile(uploadResult.file.name); 63 | console.log(`Deleted ${uploadResult.file.displayName}`); 64 | 65 | return result.response.text(); 66 | 67 | } catch (error) { 68 | if (error instanceof Error) { 69 | throw new Error(`Error processing file: ${error.message}`); 70 | } else { 71 | throw new Error(`Unknown error occurred during file processing.`); 72 | } 73 | } finally { 74 | // Delete the temporary file from the local server 75 | if (fs.existsSync(filePath)) { 76 | fs.unlinkSync(filePath); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Agent/training/WebsiteScraping.ts: -------------------------------------------------------------------------------- 1 | import puppeteer from 'puppeteer'; 2 | import DOMPurify from 'dompurify'; 3 | import { JSDOM } from 'jsdom'; 4 | import { saveScrapedData } from '../../utils'; 5 | 6 | // Function to clean the HTML content 7 | function cleanHTML(inputHtml: string): string { 8 | const window = new JSDOM('').window; 9 | const purify = DOMPurify(window); 10 | return purify.sanitize(inputHtml, { 11 | ALLOWED_TAGS: [] // Remove all tags 12 | }); 13 | } 14 | 15 | // Function to scrape and clean content from a given URL using Puppeteer 16 | async function scrapeAndCleanContent(url: string): Promise { 17 | try { 18 | // Launch a Puppeteer browser instance 19 | const browser = await puppeteer.launch({ headless: true }); 20 | const page = await browser.newPage(); 21 | 22 | // Navigate to the specified URL 23 | await page.goto(url, { waitUntil: 'networkidle2' }); 24 | 25 | // Extract the text content from the website 26 | const htmlContent = await page.evaluate(() => document.body.innerHTML); 27 | 28 | // Close the browser 29 | await browser.close(); 30 | 31 | // Clean the extracted text content 32 | const cleanedContent = cleanHTML(htmlContent); 33 | 34 | return cleanedContent; 35 | } catch (error) { 36 | console.error('Error scraping and cleaning content:', error); 37 | return null; 38 | } 39 | } 40 | 41 | // Function to get all links from a given URL 42 | async function getAllLinks(url: string): Promise { 43 | try { 44 | const browser = await puppeteer.launch({ headless: true }); 45 | const page = await browser.newPage(); 46 | 47 | await page.goto(url, { waitUntil: 'networkidle2' }); 48 | 49 | // Extract all links from the page 50 | const links = await page.evaluate(() => 51 | Array.from(document.querySelectorAll('a')).map(anchor => anchor.href) 52 | ); 53 | 54 | await browser.close(); 55 | return links; 56 | } catch (error) { 57 | console.error('Error getting links:', error); 58 | return []; 59 | } 60 | } 61 | 62 | // Function to scrape and clean content from all routes on a website 63 | async function scrapeAllRoutes(baseUrl: string): Promise { 64 | const visitedLinks = new Set(); 65 | const linksToVisit = [baseUrl]; 66 | 67 | while (linksToVisit.length > 0) { 68 | const currentLink = linksToVisit.pop(); 69 | if (currentLink && !visitedLinks.has(currentLink)) { 70 | visitedLinks.add(currentLink); 71 | 72 | const cleanedContent = await scrapeAndCleanContent(currentLink); 73 | if (cleanedContent) { 74 | console.log(`Cleaned Content from ${currentLink}:`, cleanedContent); 75 | await saveScrapedData(currentLink, cleanedContent); 76 | } else { 77 | console.log(`Failed to scrape and clean content from ${currentLink}.`); 78 | } 79 | 80 | const newLinks = await getAllLinks(currentLink); 81 | for (const link of newLinks) { 82 | if (link.startsWith(baseUrl) && !visitedLinks.has(link)) { 83 | linksToVisit.push(link); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | // Example usage 91 | const baseUrl = 'https://davidtsx.vercel.app'; 92 | scrapeAllRoutes(baseUrl) 93 | .then(() => { 94 | console.log('Scraping completed.'); 95 | }) 96 | .catch(error => { 97 | console.error('Error:', error); 98 | }); -------------------------------------------------------------------------------- /src/Agent/training/sample/Audio.ts: -------------------------------------------------------------------------------- 1 | import { GoogleAIFileManager, FileState } from "@google/generative-ai/server"; 2 | import { GoogleGenerativeAI } from "@google/generative-ai"; 3 | import dotenv from "dotenv"; 4 | import path from "path"; 5 | import fs from "fs"; 6 | import mime from "mime-types"; 7 | 8 | dotenv.config(); 9 | 10 | const apiKey = process.env.GEMINI_API_KEY_41; 11 | if (!apiKey) { 12 | throw new Error("API key is missing"); 13 | } 14 | 15 | const fileManager = new GoogleAIFileManager(apiKey); 16 | 17 | // Function to upload, process, and delete the audio file with support for various formats 18 | const processAudioFile = async (fileName: string): Promise => { 19 | try { 20 | // Resolve the file path relative to the project root 21 | const filePath = path.resolve(__dirname, fileName); 22 | 23 | // Check if the file exists at the resolved path 24 | if (!fs.existsSync(filePath)) { 25 | throw new Error(`File not found: ${filePath}`); 26 | } 27 | 28 | // Get MIME type of the file based on the extension 29 | const mimeType = mime.lookup(filePath); 30 | if (!mimeType || !mimeType.startsWith("audio/")) { 31 | throw new Error("Invalid audio file format."); 32 | } 33 | 34 | // Upload the audio file with the correct MIME type 35 | const uploadResult = await fileManager.uploadFile(filePath, { 36 | mimeType: mimeType, 37 | displayName: "Audio sample", 38 | }); 39 | 40 | let file = await fileManager.getFile(uploadResult.file.name); 41 | 42 | // Wait for the audio file to be processed 43 | while (file.state === FileState.PROCESSING) { 44 | process.stdout.write("."); 45 | // Sleep for 10 seconds 46 | await new Promise((resolve) => setTimeout(resolve, 10_000)); 47 | // Fetch the file from the API again 48 | file = await fileManager.getFile(uploadResult.file.name); 49 | } 50 | 51 | if (file.state === FileState.FAILED) { 52 | throw new Error("Audio processing failed."); 53 | } 54 | 55 | // Log the uploaded file URI 56 | console.log( 57 | `Uploaded file ${uploadResult.file.displayName} as: ${uploadResult.file.uri}` 58 | ); 59 | 60 | // Generate content using Gemini 61 | const genAI = new GoogleGenerativeAI(apiKey); 62 | const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); 63 | 64 | const result = await model.generateContent([ 65 | "Generate a transcript of the audio.", 66 | { 67 | fileData: { 68 | fileUri: uploadResult.file.uri, 69 | mimeType: uploadResult.file.mimeType, 70 | }, 71 | }, 72 | ]); 73 | 74 | // Log the response 75 | console.log(result.response.text()); 76 | 77 | // Delete the uploaded file after processing 78 | await fileManager.deleteFile(uploadResult.file.name); 79 | console.log(`Deleted ${uploadResult.file.displayName}`); 80 | 81 | } catch (error) { 82 | console.error("Error processing audio file:", error); 83 | } 84 | }; 85 | 86 | // Example usage: Call the function with the correct file name relative to the project root 87 | processAudioFile("LilTjay.mp3").catch((error) => { 88 | console.error("An error occurred:", error); 89 | }); 90 | -------------------------------------------------------------------------------- /src/Agent/training/sample/How_to_be_a_Web_Developer_A_Complete_Beginners_Guide_on_What_to.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchain-src/Riona-AI-Agent-/71d42de4a57696b8e5d183a6ef42e3e6088f7030/src/Agent/training/sample/How_to_be_a_Web_Developer_A_Complete_Beginners_Guide_on_What_to.pdf -------------------------------------------------------------------------------- /src/Agent/training/sample/LilTjay.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchain-src/Riona-AI-Agent-/71d42de4a57696b8e5d183a6ef42e3e6088f7030/src/Agent/training/sample/LilTjay.mp3 -------------------------------------------------------------------------------- /src/Agent/training/sample/test.txt: -------------------------------------------------------------------------------- 1 | This is a sample text file , 2 | this is what will be passed to the to train the model -------------------------------------------------------------------------------- /src/Agent/training/youtubeURL.ts: -------------------------------------------------------------------------------- 1 | import { YoutubeTranscript } from 'youtube-transcript'; 2 | import { generateTrainingPrompt } from '../script/summarize'; 3 | import logger from '../../config/logger'; 4 | 5 | 6 | /** 7 | * Fetches the transcript of a given YouTube video URL. 8 | * @param url - The YouTube video URL. 9 | * @returns A promise that resolves to the transcript text or an error message. 10 | */ 11 | async function getYouTubeTranscript(url: string): Promise { 12 | try { 13 | let videoId: string | null = null; 14 | 15 | // Handle both full YouTube URL and shortened YouTube URL 16 | const parsedUrl = new URL(url); 17 | 18 | // If the URL is a full YouTube URL (youtube.com/watch?v=...) 19 | if (parsedUrl.hostname === 'www.youtube.com' || parsedUrl.hostname === 'youtube.com') { 20 | videoId = parsedUrl.searchParams.get("v"); 21 | } 22 | 23 | // If the URL is a shortened YouTube URL (youtu.be/kTADMgEDlIQ) 24 | else if (parsedUrl.hostname === 'youtu.be') { 25 | videoId = parsedUrl.pathname.split("/")[1]; // Extract the video ID from the path 26 | } 27 | 28 | // If we couldn't extract a video ID, throw an error 29 | if (!videoId) { 30 | throw new Error("Invalid YouTube URL"); 31 | } 32 | 33 | // Fetch the transcript using the video ID 34 | const transcript = await YoutubeTranscript.fetchTranscript(videoId); 35 | 36 | // Check if the transcript is empty 37 | if (transcript.length === 0) { 38 | return "No captions available for this video."; 39 | } 40 | 41 | // Combine the transcript text into one string 42 | return transcript.map(item => item.text).join(' '); 43 | } catch (error) { 44 | if (error instanceof Error) { 45 | return `Error fetching transcript: ${error.message}`; 46 | } 47 | return "An unknown error occurred."; 48 | } 49 | } 50 | 51 | 52 | 53 | /** 54 | * Trains the agent using the transcript of a given YouTube video URL. 55 | * @param url - The YouTube video URL. 56 | * @returns A promise that resolves to the training prompt or an error message. 57 | * The returned object is an array of objects, each containing: 58 | * - `transcriptTitle`: The title of the YouTube video transcript. 59 | * - `fullTranscript`: The full, transformed YouTube video transcript. 60 | * - `contentTokenCount`: The total number of tokens in the full transcript. 61 | */ 62 | 63 | export async function Train_Agent_with_Youtube_URL(url: string) { 64 | try { 65 | const transcript = await getYouTubeTranscript(url); 66 | if (!transcript) { 67 | throw new Error('Failed to retrieve transcript from YouTube.'); 68 | } 69 | const source = await generateTrainingPrompt(transcript); 70 | if (!source) { 71 | throw new Error('Failed to generate training prompt.'); 72 | } 73 | // logger.debug("Training source generated : Title" , source[0]?.transcriptTitle) 74 | console.log("Training source generated : Title" , source) 75 | return source; 76 | } catch (error) { 77 | logger.error('Error in Train_Agent_with_Youtube_URL:', error); 78 | // Return an error message or handle it accordingly 79 | return { error: error instanceof Error ? error.message : `Error in Train_Agent_with_Youtube_URL: ${error}` }; 80 | } 81 | } 82 | 83 | const url = 'https://youtu.be/jSQ6Mru88y4?si=viP_FOYSD1AeVcZ9' 84 | Train_Agent_with_Youtube_URL(url) 85 | -------------------------------------------------------------------------------- /src/client/Github.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchain-src/Riona-AI-Agent-/71d42de4a57696b8e5d183a6ef42e3e6088f7030/src/client/Github.ts -------------------------------------------------------------------------------- /src/client/Instagram.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Browser, DEFAULT_INTERCEPT_RESOLUTION_PRIORITY } from "puppeteer"; 3 | import puppeteer from "puppeteer-extra"; 4 | import StealthPlugin from "puppeteer-extra-plugin-stealth"; 5 | import AdblockerPlugin from "puppeteer-extra-plugin-adblocker"; 6 | import UserAgent from "user-agents"; 7 | import { Server } from "proxy-chain"; 8 | import { IGpassword, IGusername } from "../secret"; 9 | import logger from "../config/logger"; 10 | import { Instagram_cookiesExist, loadCookies, saveCookies } from "../utils"; 11 | import { runAgent } from "../Agent"; 12 | import { getInstagramCommentSchema } from "../Agent/schema"; 13 | 14 | // Add stealth plugin to puppeteer 15 | puppeteer.use(StealthPlugin()); 16 | puppeteer.use( 17 | AdblockerPlugin({ 18 | // Optionally enable Cooperative Mode for several request interceptors 19 | interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY, 20 | }) 21 | ); 22 | 23 | 24 | async function runInstagram() { 25 | // Create a local proxy server 26 | const server = new Server({ port: 8000 }); 27 | await server.listen(); 28 | const checkMode = process.env.NODE_ENV === "production" ? true : false; 29 | const proxyUrl = `http://localhost:8000`; 30 | const browser: Browser = await puppeteer.launch({ 31 | headless: checkMode, 32 | args: [`--proxy-server=${proxyUrl}`], // Use the proxy server 33 | }); 34 | 35 | // Check if cookies exist and load them into the page 36 | if (await Instagram_cookiesExist()) { 37 | logger.info("Loading cookies...:🚧"); 38 | const cookies = await loadCookies("./cookies/Instagramcookies.json"); 39 | await browser.setCookie(...cookies); 40 | } 41 | const page = await browser.newPage(); 42 | // await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 1 }); 43 | 44 | // Set a random PC user-agent 45 | const userAgent = new UserAgent({ deviceCategory: "desktop" }); 46 | const randomUserAgent = userAgent.toString(); 47 | logger.info(`Using user-agent: ${randomUserAgent}`); 48 | await page.setUserAgent(randomUserAgent); 49 | 50 | // Check if cookies 51 | if (await Instagram_cookiesExist()) { 52 | logger.info("Cookies loaded, skipping login..."); 53 | await page.goto("https://www.instagram.com", { waitUntil: "networkidle2" }); 54 | 55 | // Check if login was successful by verifying page content (e.g., user profile or feed) 56 | const isLoggedIn = await page.$("a[href='/direct/inbox/']"); 57 | if (isLoggedIn) { 58 | logger.info("Login verified with cookies."); 59 | } else { 60 | logger.warn("Cookies invalid or expired. Logging in again..."); 61 | await loginWithCredentials(page, browser); 62 | } 63 | } else { 64 | // If no cookies are available, perform login with credentials 65 | await loginWithCredentials(page, browser); 66 | } 67 | 68 | // Optionally take a screenshot after loading the page 69 | await page.screenshot({ path: "logged_in.png" }); 70 | 71 | // Navigate to the Instagram homepage 72 | await page.goto("https://www.instagram.com/"); 73 | 74 | // Interact with the first post 75 | await interactWithPosts(page); 76 | 77 | // Close the browser 78 | await browser.close(); 79 | await server.close(true); // Stop the proxy server and close connections 80 | } 81 | 82 | 83 | 84 | 85 | const loginWithCredentials = async (page: any, browser: Browser) => { 86 | try { 87 | await page.goto("https://www.instagram.com/accounts/login/"); 88 | await page.waitForSelector('input[name="username"]'); 89 | 90 | // Fill out the login form 91 | await page.type('input[name="username"]', IGusername); // Replace with your username 92 | await page.type('input[name="password"]', IGpassword); // Replace with your password 93 | await page.click('button[type="submit"]'); 94 | 95 | // Wait for navigation after login 96 | await page.waitForNavigation(); 97 | 98 | // Save cookies after login 99 | const cookies = await browser.cookies(); 100 | await saveCookies("./cookies/Instagramcookies.json", cookies); 101 | } catch (error) { 102 | logger.error("Error logging in with credentials:", error); 103 | } 104 | } 105 | 106 | async function interactWithPosts(page: any) { 107 | let postIndex = 1; // Start with the first post 108 | const maxPosts = 50; // Limit to prevent infinite scrolling 109 | 110 | while (postIndex <= maxPosts) { 111 | try { 112 | const postSelector = `article:nth-of-type(${postIndex})`; 113 | 114 | // Check if the post exists 115 | if (!(await page.$(postSelector))) { 116 | console.log("No more posts found. Exiting loop..."); 117 | break; 118 | } 119 | 120 | const likeButtonSelector = `${postSelector} svg[aria-label="Like"]`; 121 | const likeButton = await page.$(likeButtonSelector); 122 | const ariaLabel = await likeButton?.evaluate((el: Element) => 123 | el.getAttribute("aria-label") 124 | ); 125 | 126 | if (ariaLabel === "Like") { 127 | console.log(`Liking post ${postIndex}...`); 128 | await likeButton.click(); 129 | console.log(`Post ${postIndex} liked.`); 130 | } else if (ariaLabel === "Unlike") { 131 | console.log(`Post ${postIndex} is already liked.`); 132 | } else { 133 | console.log(`Like button not found for post ${postIndex}.`); 134 | } 135 | 136 | // Extract and log the post caption 137 | const captionSelector = `${postSelector} div.x9f619 span._ap3a div span._ap3a`; 138 | const captionElement = await page.$(captionSelector); 139 | 140 | let caption = ""; 141 | if (captionElement) { 142 | caption = await captionElement.evaluate((el: HTMLElement) => el.innerText); 143 | console.log(`Caption for post ${postIndex}: ${caption}`); 144 | } else { 145 | console.log(`No caption found for post ${postIndex}.`); 146 | } 147 | 148 | // Check if there is a '...more' link to expand the caption 149 | const moreLinkSelector = `${postSelector} div.x9f619 span._ap3a span div span.x1lliihq`; 150 | const moreLink = await page.$(moreLinkSelector); 151 | if (moreLink) { 152 | console.log(`Expanding caption for post ${postIndex}...`); 153 | await moreLink.click(); // Click the '...more' link to expand the caption 154 | await page.waitForTimeout(1000); // Wait for the caption to expand 155 | const expandedCaption = await captionElement.evaluate( 156 | (el: HTMLElement) => el.innerText 157 | ); 158 | console.log( 159 | `Expanded Caption for post ${postIndex}: ${expandedCaption}` 160 | ); 161 | caption = expandedCaption; // Update caption with expanded content 162 | } 163 | 164 | // Comment on the post 165 | const commentBoxSelector = `${postSelector} textarea`; 166 | const commentBox = await page.$(commentBoxSelector); 167 | if (commentBox) { 168 | console.log(`Commenting on post ${postIndex}...`); 169 | const prompt = `Craft a thoughtful, engaging, and mature reply to the following post: "${caption}". Ensure the reply is relevant, insightful, and adds value to the conversation. It should reflect empathy and professionalism, and avoid sounding too casual or superficial. also it should be 300 characters or less. and it should not go against instagram Community Standards on spam. so you will have to try your best to humanize the reply`; 170 | const schema = getInstagramCommentSchema(); 171 | const comment = await runAgent(schema, prompt); // Pass the updated caption 172 | await commentBox.type(comment); // Replace with random comment 173 | 174 | const postButtonSelector = `${postSelector} div[role="button"]:not([disabled]):has-text("Post")`; 175 | const postButton = await page.$(postButtonSelector); 176 | if (postButton) { 177 | console.log(`Posting comment on post ${postIndex}...`); 178 | await postButton.click(); 179 | console.log(`Comment posted on post ${postIndex}.`); 180 | } else { 181 | console.log("Post button not found."); 182 | } 183 | } else { 184 | console.log("Comment box not found."); 185 | } 186 | 187 | // Wait before moving to the next post (randomize between 5 and 10 seconds) 188 | const delay = Math.floor(Math.random() * 5000) + 5000; // Random delay between 5 and 10 seconds 189 | console.log( 190 | `Waiting ${delay / 1000} seconds before moving to the next post...` 191 | ); 192 | await page.waitForTimeout(delay); 193 | 194 | // Scroll to the next post 195 | await page.evaluate(() => { 196 | window.scrollBy(0, window.innerHeight); 197 | }); 198 | 199 | postIndex++; // Move to the next post 200 | } catch (error) { 201 | console.error(`Error interacting with post ${postIndex}:`, error); 202 | break; 203 | } 204 | } 205 | } 206 | 207 | 208 | 209 | export { runInstagram }; 210 | 211 | -------------------------------------------------------------------------------- /src/client/Twitter.ts: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------- /src/client/X-bot/client/index.ts: -------------------------------------------------------------------------------- 1 | import TwitterApi from "twitter-api-v2"; 2 | import { TWITTER_API_CREDENTIALS } from "../../../secret"; 3 | 4 | // Instantiate a new Twitter API client 5 | 6 | const client = new TwitterApi( 7 | { 8 | appKey: TWITTER_API_CREDENTIALS.appKey, 9 | appSecret: TWITTER_API_CREDENTIALS.appSecret, 10 | accessToken: TWITTER_API_CREDENTIALS.accessToken, 11 | accessSecret: TWITTER_API_CREDENTIALS.accessTokenSecret, 12 | }, 13 | ); 14 | 15 | 16 | const bearer = new TwitterApi(TWITTER_API_CREDENTIALS.bearerToken); 17 | 18 | export const twitterClient = client.readWrite; 19 | export const twitterBearer = bearer.readOnly; 20 | -------------------------------------------------------------------------------- /src/client/X-bot/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchain-src/Riona-AI-Agent-/71d42de4a57696b8e5d183a6ef42e3e6088f7030/src/client/X-bot/index.ts -------------------------------------------------------------------------------- /src/config/logger.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, format, transports } from "winston"; 2 | import 'winston-daily-rotate-file'; 3 | import path from 'path'; 4 | import fs from 'fs'; 5 | import { setup_HandleError } from "../utils"; 6 | 7 | // Ensure the logs directory exists 8 | const logDir = path.join(__dirname, '../logs'); 9 | if (!fs.existsSync(logDir)) { 10 | fs.mkdirSync(logDir, { recursive: true }); 11 | } 12 | 13 | // Define log levels and their corresponding colors 14 | const logLevels = { 15 | levels: { 16 | error: 0, 17 | warn: 1, 18 | info: 2, 19 | debug: 3, 20 | }, 21 | colors: { 22 | error: 'red', 23 | warn: 'yellow', 24 | info: 'green', 25 | debug: 'blue', 26 | }, 27 | }; 28 | 29 | // Custom function to format the timestamp 30 | const customTimestamp = () => { 31 | const now = new Date(); 32 | const hours = now.getHours(); 33 | const minutes = now.getMinutes(); 34 | const seconds = now.getSeconds(); 35 | const ampm = hours >= 12 ? 'PM' : 'AM'; 36 | const formattedTime = `${hours % 12 || 12}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds} ${ampm}`; 37 | return formattedTime; 38 | }; 39 | 40 | // Function to get emojis based on log level 41 | const getEmojiForLevel = (level : string) :string => { 42 | switch (level) { 43 | case 'info': 44 | return '💡'; // Light bulb for info 45 | case 'error': 46 | return '🚨'; // Emergency for errors 47 | case 'warn': 48 | return '⚠️'; // Warning for warnings 49 | case 'debug': 50 | return '🐞'; // Bug for debug 51 | default: 52 | return '🔔'; // Default bell emoji 53 | } 54 | }; 55 | 56 | const logger = createLogger({ 57 | levels: logLevels.levels, 58 | format: format.combine( 59 | format.timestamp({ format: customTimestamp }), 60 | format.colorize(), 61 | format.printf(({ timestamp, level, message }) => { 62 | const emoji = getEmojiForLevel(level); 63 | return `${timestamp} ${emoji} [${level}]: ${message}`; 64 | }) 65 | ), 66 | transports: [ 67 | new transports.Console({ 68 | level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', 69 | format: format.combine( 70 | format.colorize(), 71 | format.simple() 72 | ), 73 | }), 74 | new transports.DailyRotateFile({ 75 | filename: "logs/%DATE%-combined.log", 76 | datePattern: "YYYY-MM-DD", 77 | level: "info", 78 | maxFiles: "14d", // Keep logs for the last 14 days 79 | maxSize: "20m", // Maximum log file size before rotation (20MB) 80 | zippedArchive: true, // Compress old log files 81 | format: format.combine(format.timestamp(), format.json()), 82 | }), // Daily rotating log file for general info 83 | new transports.DailyRotateFile({ 84 | filename: "logs/%DATE%-error.log", 85 | datePattern: "YYYY-MM-DD", 86 | level: "error", 87 | maxFiles: "14d", // Keep logs for the last 14 days 88 | maxSize: "20m", // Maximum log file size before rotation (20MB) 89 | zippedArchive: true, // Compress old log files 90 | format: format.combine(format.timestamp(), format.json()), 91 | }), // Daily rotating error log 92 | new transports.DailyRotateFile({ 93 | filename: "logs/%DATE%-debug.log", 94 | datePattern: "YYYY-MM-DD", 95 | level: "debug", 96 | maxFiles: "14d", // Keep logs for the last 14 days 97 | maxSize: "20m", // Maximum log file size before rotation (20MB) 98 | zippedArchive: true, // Compress old log files 99 | format: format.combine(format.timestamp(), format.json()), 100 | }), // Daily rotating debug log 101 | ], 102 | }); 103 | 104 | // Catch unhandled promise rejections 105 | 106 | export function setupErrorHandlers(): void { 107 | // Catch unhandled promise rejections 108 | process.on("unhandledRejection", (error: unknown) => { 109 | setup_HandleError(error, "Unhandled Rejection"); 110 | process.exit(1); 111 | }); 112 | 113 | // Catch uncaught exceptions 114 | process.on("uncaughtException", (error) => { 115 | setup_HandleError(error, "Uncaught Exception"); 116 | process.exit(1); 117 | }); 118 | 119 | // Catch process warnings 120 | process.on("warning", (warning) => { 121 | logger.warn(`Warning: ${warning.message || warning}`); 122 | }); 123 | } 124 | 125 | export default logger; 126 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import { runInstagram } from './client/Instagram'; 3 | import logger, { setupErrorHandlers } from './config/logger'; 4 | import { setup_HandleError } from './utils'; 5 | // import { main as twitterMain } from './client/Twitter'; // 6 | // import { main as githubMain } from './client/GitHub'; // 7 | 8 | // Set up process-level error handlers 9 | setupErrorHandlers(); 10 | 11 | 12 | const runAgents = async () => { 13 | logger.info("Starting Instagram agent..."); 14 | await runInstagram(); 15 | logger.info("Instagram agent finished."); 16 | 17 | // logger.info("Starting Twitter agent..."); 18 | // await twitterMain(); 19 | // logger.info("Twitter agent finished."); 20 | 21 | // logger.info("Starting GitHub agent..."); 22 | // await githubMain(); 23 | // .log("GitHub agent finished."); 24 | }; 25 | 26 | runAgents().catch(error => { 27 | setup_HandleError(error , "Error running agents:"); 28 | }) -------------------------------------------------------------------------------- /src/secret/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | export const IGusername: string = process.env.IGusername || "default_IGusername"; 5 | export const IGpassword: string = process.env.IGpassword || "default_IGpassword"; 6 | export const Xusername: string = process.env.Xusername || "default_Xusername"; 7 | export const Xpassword: string = process.env.Xpassword || "default_Xpassword"; 8 | 9 | export const TWITTER_API_CREDENTIALS = { 10 | appKey: process.env.TWITTER_API_KEY || "default_TWITTER_API_KEY", 11 | appSecret: process.env.TWITTER_API_SECRET || "default_TWITTER_API_SECRET", 12 | accessToken: process.env.TWITTER_ACCESS_TOKEN || "default TWITTER_ACCESS_TOKEN", 13 | accessTokenSecret: process.env.TWITTER_ACCESS_SECRET || "default_TWITTER_ACCESS_SECRET", 14 | bearerToken: process.env.TWITTER_BEARER_TOKEN || "default_TWITTER_BEARER_TOKEN", 15 | } 16 | 17 | 18 | 19 | export const geminiApiKeys = [ 20 | process.env.GEMINI_API_KEY_1 || "API_KEY_1", 21 | process.env.GEMINI_API_KEY_2 || "API_KEY_2", 22 | process.env.GEMINI_API_KEY_3 || "API_KEY_3", 23 | process.env.GEMINI_API_KEY_4 || "API_KEY_4", 24 | process.env.GEMINI_API_KEY_5 || "API_KEY_5", 25 | process.env.GEMINI_API_KEY_6 || "API_KEY_6", 26 | process.env.GEMINI_API_KEY_7 || "API_KEY_7", 27 | process.env.GEMINI_API_KEY_8 || "API_KEY_8", 28 | process.env.GEMINI_API_KEY_9 || "API_KEY_9", 29 | process.env.GEMINI_API_KEY_10 || "API_KEY_10", 30 | process.env.GEMINI_API_KEY_11 || "API_KEY_11", 31 | process.env.GEMINI_API_KEY_12 || "API_KEY_12", 32 | process.env.GEMINI_API_KEY_13 || "API_KEY_13", 33 | process.env.GEMINI_API_KEY_14 || "API_KEY_14", 34 | process.env.GEMINI_API_KEY_15 || "API_KEY_15", 35 | process.env.GEMINI_API_KEY_16 || "API_KEY_16", 36 | process.env.GEMINI_API_KEY_17 || "API_KEY_17", 37 | process.env.GEMINI_API_KEY_18 || "API_KEY_18", 38 | process.env.GEMINI_API_KEY_19 || "API_KEY_19", 39 | process.env.GEMINI_API_KEY_20 || "API_KEY_20", 40 | process.env.GEMINI_API_KEY_21 || "API_KEY_21", 41 | process.env.GEMINI_API_KEY_22 || "API_KEY_22", 42 | process.env.GEMINI_API_KEY_23 || "API_KEY_23", 43 | process.env.GEMINI_API_KEY_24 || "API_KEY_24", 44 | process.env.GEMINI_API_KEY_25 || "API_KEY_25", 45 | process.env.GEMINI_API_KEY_26 || "API_KEY_26", 46 | process.env.GEMINI_API_KEY_27 || "API_KEY_27", 47 | process.env.GEMINI_API_KEY_28 || "API_KEY_28", 48 | process.env.GEMINI_API_KEY_29 || "API_KEY_29", 49 | process.env.GEMINI_API_KEY_30 || "API_KEY_30", 50 | process.env.GEMINI_API_KEY_31 || "API_KEY_31", 51 | process.env.GEMINI_API_KEY_32 || "API_KEY_32", 52 | process.env.GEMINI_API_KEY_33 || "API_KEY_33", 53 | process.env.GEMINI_API_KEY_34 || "API_KEY_34", 54 | process.env.GEMINI_API_KEY_35 || "API_KEY_35", 55 | process.env.GEMINI_API_KEY_36 || "API_KEY_36", 56 | process.env.GEMINI_API_KEY_37 || "API_KEY_37", 57 | process.env.GEMINI_API_KEY_38 || "API_KEY_38", 58 | process.env.GEMINI_API_KEY_39 || "API_KEY_39", 59 | process.env.GEMINI_API_KEY_40 || "API_KEY_40", 60 | process.env.GEMINI_API_KEY_41 || "API_KEY_41", 61 | process.env.GEMINI_API_KEY_42 || "API_KEY_42", 62 | process.env.GEMINI_API_KEY_43 || "API_KEY_43", 63 | process.env.GEMINI_API_KEY_44 || "API_KEY_44", 64 | process.env.GEMINI_API_KEY_45 || "API_KEY_45", 65 | process.env.GEMINI_API_KEY_46 || "API_KEY_46", 66 | process.env.GEMINI_API_KEY_47 || "API_KEY_47", 67 | process.env.GEMINI_API_KEY_48 || "API_KEY_48", 68 | process.env.GEMINI_API_KEY_49 || "API_KEY_49", 69 | process.env.GEMINI_API_KEY_50 || "API_KEY_50", 70 | ]; -------------------------------------------------------------------------------- /src/types/puppeteer.d.ts: -------------------------------------------------------------------------------- 1 | import 'puppeteer'; 2 | 3 | declare module 'puppeteer' { 4 | interface Page { 5 | $x(expression: string): Promise; 6 | } 7 | } -------------------------------------------------------------------------------- /src/utils/download.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import request from 'request'; 3 | import logger from '../config/logger'; 4 | 5 | export const download = function (uri: string, filename: string, callback: (err?: Error) => void): void { 6 | request.head(uri, function (err: Error | null, _res: request.Response, _body: any) { 7 | if (err) { 8 | logger.error(`Error fetching headers for ${uri}: ${err.message}`); 9 | callback(err); 10 | return; 11 | } 12 | request(uri) 13 | .pipe(fs.createWriteStream(filename)) 14 | .on('error', (err: Error) => { 15 | logger.error(`Error downloading file from ${uri}: ${err.message}`); 16 | callback(err); 17 | }) 18 | .on('close', () => { 19 | logger.info(`File downloaded successfully from ${uri} to ${filename}`); 20 | callback(); 21 | }); 22 | }); 23 | }; 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { promises as fs } from "fs"; 2 | import path from "path"; 3 | import { geminiApiKeys } from "../secret"; 4 | import logger from "../config/logger"; 5 | 6 | 7 | 8 | export async function Instagram_cookiesExist(): Promise { 9 | try { 10 | const cookiesPath = "./cookies/Instagramcookies.json"; 11 | // Check if the file exists 12 | await fs.access(cookiesPath); 13 | 14 | const cookiesData = await fs.readFile(cookiesPath, "utf-8"); 15 | const cookies = JSON.parse(cookiesData); 16 | 17 | // Find the sessionid cookie 18 | const sessionIdCookie = cookies.find((cookie: { name: string }) => cookie.name === 'sessionid'); 19 | 20 | // If sessionid cookie is not found, return false 21 | if (!sessionIdCookie) return false; 22 | 23 | // Check if the sessionid cookie has expired 24 | const currentTimestamp = Math.floor(Date.now() / 1000); 25 | return sessionIdCookie.expires > currentTimestamp; 26 | } catch (error) { 27 | const err = error as NodeJS.ErrnoException; 28 | if (err.code === 'ENOENT') { 29 | // File does not exist 30 | logger.warn("Cookies file does not exist."); 31 | return false; 32 | } else { 33 | logger.error("Error checking cookies:", error); 34 | return false; 35 | } 36 | } 37 | } 38 | 39 | 40 | export async function saveCookies(cookiesPath: string, cookies: any[]): Promise { 41 | try { 42 | await fs.writeFile(cookiesPath, JSON.stringify(cookies, null, 2)); 43 | logger.info("Cookies saved successfully."); 44 | } catch (error) { 45 | logger.error("Error saving cookies:", error); 46 | throw new Error("Failed to save cookies."); 47 | } 48 | } 49 | 50 | export async function loadCookies(cookiesPath: string): Promise { 51 | try { 52 | // Check if the file exists 53 | await fs.access(cookiesPath); 54 | 55 | // Read and parse the cookies file 56 | const cookiesData = await fs.readFile(cookiesPath, "utf-8"); 57 | const cookies = JSON.parse(cookiesData); 58 | return cookies; 59 | } catch (error) { 60 | logger.error("Cookies file does not exist or cannot be read.", error); 61 | return []; 62 | } 63 | } 64 | 65 | // Function to get the next API key in the list 66 | export const getNextApiKey = (currentApiKeyIndex: number) => { 67 | currentApiKeyIndex = (currentApiKeyIndex + 1) % geminiApiKeys.length; // Circular rotation of API keys 68 | return geminiApiKeys[currentApiKeyIndex]; 69 | }; 70 | 71 | 72 | export async function handleError(error: unknown, currentApiKeyIndex: number, schema: any, prompt: string, runAgent: (schema: any, prompt: string) => Promise): Promise { 73 | if (error instanceof Error) { 74 | if (error.message.includes("429 Too Many Requests")) { 75 | logger.error(`---GEMINI_API_KEY_${currentApiKeyIndex + 1} limit exhausted, switching to the next API key...`); 76 | const geminiApiKey = getNextApiKey(currentApiKeyIndex); 77 | const currentApiKeyName = `GEMINI_API_KEY_${currentApiKeyIndex + 1}`; 78 | return runAgent(schema, prompt); 79 | } else if (error.message.includes("503 Service Unavailable")) { 80 | logger.error( "Service is temporarily unavailable. Retrying..."); 81 | await new Promise(resolve => setTimeout(resolve, 5000)); 82 | return runAgent(schema, prompt); 83 | } else { 84 | logger.error(`Error generating training prompt: ${error.message}`); 85 | return `An error occurred: ${error.message}`; 86 | } 87 | } else { 88 | logger.error("An unknown error occurred:", error); 89 | return "An unknown error occurred."; 90 | } 91 | } 92 | 93 | 94 | export function setup_HandleError(error: unknown, context: string): void { 95 | if (error instanceof Error) { 96 | if (error.message.includes("net::ERR_ABORTED")) { 97 | logger.error(`ABORTION error occurred in ${context}: ${error.message}`); 98 | } else { 99 | logger.error(`Error in ${context}: ${error.message}`); 100 | } 101 | } else { 102 | logger.error(`An unknown error occurred in ${context}: ${error}`); 103 | } 104 | } 105 | 106 | 107 | 108 | 109 | 110 | // Function to save tweet data to tweetData.json 111 | export const saveTweetData = async function (tweetContent: string, imageUrl: string, timeTweeted: string): Promise { 112 | const tweetDataPath = path.join(__dirname, '../data/tweetData.json'); 113 | const tweetData = { 114 | tweetContent, 115 | imageUrl: imageUrl || null, 116 | timeTweeted, 117 | }; 118 | 119 | try { 120 | // Check if the file exists 121 | await fs.access(tweetDataPath); 122 | // Read the existing data 123 | const data = await fs.readFile(tweetDataPath, 'utf-8'); 124 | const json = JSON.parse(data); 125 | // Append the new tweet data 126 | json.push(tweetData); 127 | // Write the updated data back to the file 128 | await fs.writeFile(tweetDataPath, JSON.stringify(json, null, 2)); 129 | } catch (error) { 130 | if ((error as NodeJS.ErrnoException).code === 'ENOENT') { 131 | // File does not exist, create it with the new tweet data 132 | await fs.writeFile(tweetDataPath, JSON.stringify([tweetData], null, 2)); 133 | } else { 134 | logger.error('Error saving tweet data:', error); 135 | throw error; 136 | } 137 | } 138 | }; 139 | 140 | // Function to check if the first object's time in tweetData.json is more than 24 hours old and delete the file if necessary 141 | export const checkAndDeleteOldTweetData = async function (): Promise { 142 | const tweetDataPath = path.join(__dirname, '../data/tweetData.json'); 143 | 144 | try { 145 | // Check if the file exists 146 | await fs.access(tweetDataPath); 147 | // Read the existing data 148 | const data = await fs.readFile(tweetDataPath, 'utf-8'); 149 | const json = JSON.parse(data); 150 | 151 | if (json.length > 0) { 152 | const firstTweetTime = new Date(json[0].timeTweeted).getTime(); 153 | const currentTime = Date.now(); 154 | const timeDifference = currentTime - firstTweetTime; 155 | 156 | // Check if the time difference is more than 24 hours (86400000 milliseconds) 157 | if (timeDifference > 86400000) { 158 | await fs.unlink(tweetDataPath); 159 | logger.info('tweetData.json file deleted because the first tweet is more than 24 hours old.'); 160 | } 161 | } 162 | } catch (error) { 163 | const err = error as NodeJS.ErrnoException; 164 | if (err.code !== 'ENOENT') { 165 | logger.error('Error checking tweet data:', err); 166 | throw err; 167 | } 168 | } 169 | }; 170 | 171 | 172 | 173 | // Function to check if the tweetData.json file has 17 or more objects 174 | export const canSendTweet = async function (): Promise { 175 | const tweetDataPath = path.join(__dirname, '../data/tweetData.json'); 176 | 177 | try { 178 | // Check if the file exists 179 | await fs.access(tweetDataPath); 180 | // Read the existing data 181 | const data = await fs.readFile(tweetDataPath, 'utf-8'); 182 | const json = JSON.parse(data); 183 | 184 | // Check if the file has 17 or more objects 185 | if (json.length >= 17) { 186 | return false; 187 | } 188 | return true; 189 | } catch (error) { 190 | const err = error as NodeJS.ErrnoException; 191 | if (err.code === 'ENOENT') { 192 | // File does not exist, so it's safe to send a tweet 193 | return true; 194 | } else { 195 | logger.error('Error checking tweet data:', err); 196 | throw err; 197 | } 198 | } 199 | }; 200 | 201 | 202 | 203 | 204 | /// Function to save scraped data to scrapedData.json 205 | export const saveScrapedData = async function (link: string, content: string): Promise { 206 | const scrapedDataPath = path.join(__dirname, '../data/scrapedData.json'); 207 | const scrapedDataDir = path.dirname(scrapedDataPath); 208 | const scrapedData = { 209 | link, 210 | content, 211 | }; 212 | 213 | try { 214 | // Ensure the directory exists 215 | await fs.mkdir(scrapedDataDir, { recursive: true }); 216 | 217 | // Check if the file exists 218 | await fs.access(scrapedDataPath); 219 | // Read the existing data 220 | const data = await fs.readFile(scrapedDataPath, 'utf-8'); 221 | const json = JSON.parse(data); 222 | // Append the new scraped data 223 | json.push(scrapedData); 224 | // Write the updated data back to the file 225 | await fs.writeFile(scrapedDataPath, JSON.stringify(json, null, 2)); 226 | } catch (error) { 227 | if ((error as NodeJS.ErrnoException).code === 'ENOENT') { 228 | // File does not exist, create it with the new scraped data 229 | await fs.writeFile(scrapedDataPath, JSON.stringify([scrapedData], null, 2)); 230 | } else { 231 | logger.error('Error saving scraped data:', error); 232 | throw error; 233 | } 234 | } 235 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs" /* Specify what module code is generated. */, 29 | "rootDir": "./src" /* Specify the root folder within your source files. */, 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | "typeRoots": ["./node_modules/@types", "./src/types"], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ 43 | // "resolveJsonModule": true, /* Enable importing .json files. */ 44 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 45 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 46 | 47 | /* JavaScript Support */ 48 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 49 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 50 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 51 | 52 | /* Emit */ 53 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 54 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 55 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 56 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 57 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 58 | // "noEmit": true, /* Disable emitting files from a compilation. */ 59 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 60 | "outDir": "./build" /* Specify an output folder for all emitted files. */, 61 | // "removeComments": true, /* Disable emitting comments. */ 62 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | 75 | /* Interop Constraints */ 76 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 77 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 78 | // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 83 | 84 | /* Type Checking */ 85 | "strict": true /* Enable all strict type-checking options. */, 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | "strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */, 88 | "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */, 89 | "strictBindCallApply": true /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */, 90 | "strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */, 91 | // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ 92 | "noImplicitThis": true /* Enable error reporting when 'this' is given the type 'any'. */, 93 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 94 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 95 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 96 | "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */, 97 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 98 | "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */, 99 | "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */, 100 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 101 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 102 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 103 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 104 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 105 | 106 | /* Completeness */ 107 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 108 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 109 | } 110 | } 111 | --------------------------------------------------------------------------------