├── .gitignore ├── README.md ├── main.py ├── process.py ├── requirements.txt ├── requirements_instapy.txt ├── scripts.py.dist ├── settings.json.dist ├── stringparse.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | env.bak/ 90 | venv.bak/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | 105 | # pylint file 106 | googlecl-pylint.rc.txt 107 | assets/chromedriver 108 | quickstart.py 109 | *,cover 110 | 111 | # Mac files 112 | .DS_Store 113 | 114 | # Storage 115 | instapy.db 116 | logs/ 117 | 118 | # Editors 119 | .idea 120 | .vscode 121 | 122 | # pytest 123 | tests/logs 124 | 125 | # telegram-scheduling 126 | instapy/ 127 | assets/ 128 | scripts.py 129 | proxy_extension.py 130 | *.json 131 | *.pickle 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram-InstaPy-Scheduling v2! 2 | Telegram-InstaPy-Scheduling is bot for telegram which helps user to schedule [*InstaPy*](https://github.com/timgrossmann/InstaPy). 3 | 4 | ### What's new? 5 | - Run multiple scripts simultaneously. 6 | - Configure your scripts in an easy way! 7 | - Create user lists. 8 | 9 | ### What you need 10 | - This repo and all _requirements.txt_ installed. 11 | - InstaPy working on your pc/server. 12 | - Telegram bot token. 13 | 14 | ### How to setup 15 | 1. Create a bot with [@BotFather](https://telegram.me/BotFather). 16 | 2. Rename *settings.json.dist* => *settings.json*. 17 | 3. Contact [@GiveChatId_Bot](https://telegram.me/GiveChatId_Bot) and get your chat id with */chatid* command 18 | 1. Clone this repo into any folder 19 | 1. Install requirements with `pip install -r requirements.txt` 20 | 4. Populate *settings.json* with your data. `instapy_folder` is the path to your InstaPy installation. 21 | ``` 22 | { 23 | "telegram_token": "xxxx", 24 | "instapy_folder": "/home/xxxx/GitHub/instapy_bot", 25 | "allowed_id": [ "chat_id from GiveChatId_Bot", "342342" ], 26 | "project_path": [ "/path_where_you_want_load_your_files" ], # Optional, default: ./ 27 | "users_file": "new_user_list_file.pickle" # Optional, default: users.pickle 28 | } 29 | ``` 30 | 5. Write your personal scripts: 31 | #### How? 32 | - Rename *scripts.py.dist* in *scripts.py* and edit it. 33 | - Create a function with any name and copy your InstaPy script inside it, for example **(Make sure your first param is InstaPy)**: 34 | ```python 35 | def script_for_big_like(InstaPy, username, password, proxy): 36 | session = InstaPy(username=username, password=password) 37 | session.login() 38 | 39 | # your stuff here, e.g. 40 | session.like_by_tags(['natgeo', 'world'], amount=10) 41 | 42 | session.end() 43 | ``` 44 | - Save and exit. 45 | - Launch *main.py*. You can pass the *settings.json* from outside this folder, print help: *main.py -h* for other info. 46 | 47 | ### Avaiable commands 48 | #### Users management 49 | | Command | Parameters | Description | 50 | |--------------|-----------------------------------------------|-----------------------| 51 | | /add_user | \ \ \ | Save new user. | 52 | | /delete_user | \ | Delete an user. | 53 | | /users | | Print all users saved | 54 | 55 | #### Jobs management 56 | | Command | Parameters | Description | 57 | |----------|--------------------------------------------------------|--------------------------------------------------| 58 | | /set | \ \ \ \ | Create a new schedule. Select the day from bot. | 59 | | /unset | \ | Delete a schedule. | 60 | | /jobs | | Print all jobs that have been set | 61 | | /reload | | Jobs are saved in db now. Use this cmd to reload.| 62 | | /scripts | | Print all your scripts | 63 | | /status | \ | Print the status of all your thread or a single thread. | 64 | | /logs | \ \ | Show n lines of username/general.log file. | 65 | | /now | \ \ | Run immediately. | 66 | | /stop | \ | Stop immediately. | 67 | | /time | | Prints current server time, useful for scheduling. | 68 | 69 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Import module 5 | import logging, time, json, datetime 6 | import random, sys, os, pickle, plac, subprocess 7 | 8 | # Colored terminal 9 | from termcolor import colored, cprint 10 | 11 | # Database Jobs and Users storage 12 | from tinydb import TinyDB, Query 13 | 14 | # Telegram imports 15 | from telegram.ext import Updater, CommandHandler, Job, CallbackQueryHandler 16 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 17 | 18 | # Process class in another file 19 | from process import Process, reload_process 20 | 21 | from stringparse import parse_time, clear_lines 22 | import utils 23 | from codecs import open 24 | 25 | # Create array of all users 26 | users = [] 27 | # Create dictionary with all process 28 | process_array = {} 29 | # Create dictionary of all scripts availables 30 | scripts = {} 31 | 32 | dict_settings = { 33 | "telegram_token": None, 34 | "allowed_id": None, 35 | "instapy_folder": None, 36 | "users_path": "users.pickle", 37 | "project_path": "./" 38 | } 39 | 40 | database = None 41 | 42 | def help(bot, update): 43 | update.message.reply_text('Hi! Use /set to start the bot') 44 | 45 | # To display server date and time 46 | def timenow(bot, update): 47 | message = 'Current Date and Time: ' + '{0:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()) 48 | update.message.reply_text(message, parse_mode='HTML') 49 | 50 | def logs(bot, update, args): 51 | if str(update.message.chat_id) in dict_settings['allowed_id']: 52 | try: 53 | usernames = [ a['username'].lower() for a in users ] 54 | if not args[0].lower() in usernames: 55 | update.message.reply_text("Sorry, username {} is not saved.".format(args[0]), parse_mode='HTML') 56 | return 57 | logsline = int( args[1] ) 58 | with open('{}/logs/{}/general.log'.format(dict_settings['instapy_folder'], args[0].lower()), "r", encoding="utf-8") as f: 59 | lines = f.readlines() 60 | lines = lines[-(logsline+20):] # Prevent empty lines 61 | message = '\n'.join(x for x in lines) 62 | message = clear_lines( '\n'.join(x for x in lines), username=args[0].lower() ) 63 | lines = message.split('\n')[-logsline:] 64 | message = '\n'.join(x for x in lines) 65 | update.message.reply_text(message, disable_web_page_preview=True, parse_mode='HTML') 66 | 67 | except (IndexError, ValueError): 68 | update.message.reply_text('Usage: /logs ') 69 | else: 70 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 71 | update.message.reply_text(message, parse_mode='Markdown') 72 | 73 | def now(bot, update, args): 74 | if str(update.message.chat_id) in dict_settings['allowed_id']: 75 | try: 76 | usernames = [ a['username'].lower() for a in users ] 77 | if not args[1].lower() in usernames: 78 | update.message.reply_text("Sorry, username {} is not saved.".format(args[1]), parse_mode='HTML') 79 | return 80 | 81 | if not args[0] in scripts: 82 | update.message.reply_text("Sorry, script named {} is not in your scripts file.".format(args[0]), parse_mode='HTML') 83 | return 84 | 85 | job_name = "{}_temp_{}".format(args[0], int( time.time() )) 86 | for user in users: 87 | if user['username'].lower() == args[1].lower(): 88 | break 89 | 90 | 91 | process_array[job_name] = Process( 92 | dict_settings['instapy_folder'], 93 | job_name, 94 | args[0], 95 | update.message.chat_id, 96 | bot, 97 | user['username'], 98 | user['password'], 99 | scripts, 100 | proxy=user['proxy'] 101 | ) 102 | process_array[job_name].start() 103 | except (IndexError, ValueError): 104 | update.message.reply_text('Usage: /now ') 105 | else: 106 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 107 | update.message.reply_text(message, parse_mode='Markdown') 108 | 109 | def stop(bot, update, args): 110 | if str(update.message.chat_id) in dict_settings['allowed_id']: 111 | try: 112 | if not args[0] in process_array: 113 | update.message.reply_text("Sorry, job named {} is not in jobs array.".format(args[0]), parse_mode='HTML') 114 | return 115 | 116 | if process_array[args[0]].is_alive(): 117 | process_array[args[0]].end() 118 | update.message.reply_text("Job {} ended. Wait for process response".format(args[0]), parse_mode='HTML') 119 | time.sleep(3) 120 | del process_array[args[0]] 121 | else: 122 | update.message.reply_text("Job {} not running".format(args[0]), parse_mode='HTML') 123 | 124 | except (IndexError, ValueError): 125 | update.message.reply_text('Usage: /stop ') 126 | else: 127 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 128 | update.message.reply_text(message, parse_mode='Markdown') 129 | 130 | def exec_process(bot, job): 131 | if process_array[job.name].is_alive(): 132 | bot.send_message(process_array[job.name].chat_id, text="Sorry {} already executing!".format(job.name), parse_mode='HTML') 133 | else: 134 | process_array[job.name] = reload_process(process_array[job.name], scripts) 135 | process_array[job.name].start() 136 | 137 | def create_process(bot, context): 138 | process_array[context['job_name']] = Process( 139 | dict_settings['instapy_folder'], 140 | context['job_name'], 141 | context['script_name'], 142 | context['chat_id'], 143 | bot, 144 | context['user']['username'], 145 | context['user']['password'], 146 | scripts, 147 | proxy=context['user']['proxy'] 148 | ) 149 | 150 | def status_process(bot, update, args): 151 | if str(update.message.chat_id) in dict_settings['allowed_id']: 152 | if len(args) != 0: 153 | message = "" 154 | for arg in args: 155 | if arg in process_array: 156 | message += "\nName: {} Account: {} Script: {} Status: {}".format( 157 | arg, process_array[arg].username, process_array[arg].script_name, "ON" if process_array[arg].is_alive() else "OFF" 158 | ) 159 | else: 160 | message += "\nName: {} not found in process lists.".format(arg) 161 | else: 162 | message = "There are {} process configured.".format(len(process_array)) 163 | index = 1 164 | for proc in process_array: 165 | message += "\n{}) Name: {} Account: {} Script: {} Status: {}".format( 166 | index, proc, process_array[proc].username, process_array[proc].script_name, "ON" if process_array[proc].is_alive() else "OFF" 167 | ) 168 | index += 1 169 | 170 | update.message.reply_text(message, parse_mode='HTML') 171 | else: 172 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 173 | update.message.reply_text(message, parse_mode='Markdown') 174 | 175 | def set_job(bot, update, args, job_queue, chat_data): 176 | if str(update.message.chat_id) in dict_settings['allowed_id']: 177 | try: 178 | usernames = [ a['username'].lower() for a in users ] 179 | if not args[0].lower() in usernames: 180 | update.message.reply_text("Sorry, username {} is not saved.".format(args[0]), parse_mode='HTML') 181 | return 182 | 183 | jobs_list = utils.return_elements(database, owner=str(update.message.chat_id), entity="job") 184 | if args[1] in chat_data or args[1] in jobs_list or args[1] in process_array: 185 | update.message.reply_text("Sorry, job named {} is already used.".format(args[1]), parse_mode='HTML') 186 | return 187 | 188 | if not args[2] in scripts: 189 | update.message.reply_text("Sorry, script named {} is not in your scripts file.".format(args[2]), parse_mode='HTML') 190 | return 191 | 192 | data = { 193 | 'username': args[0], 194 | 'job_name': args[1], 195 | 'script_name': args[2], 196 | 'scheduled': args[3], 197 | 'days': [] 198 | } 199 | chat_data['tmpjob'] = data 200 | 201 | keyboard = [[InlineKeyboardButton("Sunday", callback_data='6'), 202 | InlineKeyboardButton("Monday", callback_data='0'), 203 | InlineKeyboardButton("Tuesday", callback_data='1'), 204 | InlineKeyboardButton("Wednesday", callback_data='2')], 205 | [InlineKeyboardButton("Thursday", callback_data='3'), 206 | InlineKeyboardButton("Friday", callback_data='4'), 207 | InlineKeyboardButton("Saturday", callback_data='5')], 208 | [InlineKeyboardButton("Everyday", callback_data='-1')]] 209 | 210 | update.message.reply_text('Choose a day: ', reply_markup=InlineKeyboardMarkup(keyboard)) 211 | except (IndexError, ValueError): 212 | update.message.reply_text('Usage: /set ') 213 | 214 | else: 215 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 216 | update.message.reply_text(message, parse_mode='Markdown') 217 | 218 | def persistend_job(bot, job_queue, chat_data, days, chat_id, message_id=None, db_persist=True): 219 | if chat_data['tmpjob']['job_name'] not in chat_data: 220 | context = { 221 | "job_name": chat_data['tmpjob']['job_name'], 222 | "script_name": chat_data['tmpjob']['script_name'], 223 | "user": None, 224 | "chat_id": chat_id, 225 | } 226 | 227 | for user in users: 228 | if user['username'].lower() == chat_data['tmpjob']['username']: 229 | context['user'] = user 230 | break 231 | 232 | create_process(bot, context) 233 | job_name = chat_data['tmpjob']['job_name'] 234 | scheduled_time = parse_time(chat_data['tmpjob']['scheduled']) 235 | 236 | # DAYS MANAGEMENT REQUIRE A REFACTORY. 237 | 238 | if chat_data['tmpjob']['days'] == "Everyday": 239 | job = job_queue.run_daily(exec_process, scheduled_time, context=context, name=job_name) 240 | selected_days = "Everyday" 241 | else: 242 | chat_data['tmpjob']['days'] = utils.fix_days(days) 243 | selected_days = ", ".join([utils.days[i] for i in chat_data['tmpjob']['days']]) 244 | job = job_queue.run_daily(exec_process, scheduled_time, days=tuple(chat_data['tmpjob']['days']), context=context, name=job_name) 245 | 246 | data = { 247 | 'name': job_name, 248 | "script_name": chat_data['tmpjob']['script_name'], 249 | 'scheduled': chat_data['tmpjob']['scheduled'], 250 | "username": chat_data['tmpjob']['username'], 251 | } 252 | 253 | data['days'] = selected_days 254 | 255 | if db_persist is True: 256 | utils.save_element(database, data, owner=chat_id, entity="job") 257 | bot.edit_message_text(text="Job {} set!".format(job_name), chat_id=chat_id, message_id=message_id, parse_mode='HTML') 258 | else: 259 | bot.send_message(text="Job {} set!".format(job_name), chat_id=chat_id, parse_mode='HTML') 260 | 261 | data['job'] = job 262 | chat_data[job_name] = data 263 | del chat_data['tmpjob'] 264 | 265 | def day_choose(bot, update, job_queue, chat_data): 266 | query = update.callback_query 267 | if query.data == '-1' or query.data == '-2': 268 | chat_data['tmpjob']['days'] = "Everyday" if query.data == '-1' else chat_data['tmpjob']['days'] 269 | persistend_job(bot, job_queue, chat_data, days=chat_data['tmpjob']['days'], chat_id=query.message.chat_id, message_id=query.message.message_id) 270 | else: 271 | if int(query.data) not in chat_data['tmpjob']['days']: 272 | chat_data['tmpjob']['days'].append(int(query.data)) 273 | 274 | keyboard = [[InlineKeyboardButton("Sunday", callback_data='6'), 275 | InlineKeyboardButton("Monday", callback_data='0'), 276 | InlineKeyboardButton("Tuesday", callback_data='1'), 277 | InlineKeyboardButton("Wednesday", callback_data='2')], 278 | [InlineKeyboardButton("Thursday", callback_data='3'), 279 | InlineKeyboardButton("Friday", callback_data='4'), 280 | InlineKeyboardButton("Saturday", callback_data='5')], 281 | [InlineKeyboardButton("Confirm", callback_data='-2')]] 282 | 283 | selected_days = ", ".join([utils.days[i] for i in chat_data['tmpjob']['days']]) 284 | bot.edit_message_text(text = "Select another day or confirm:\n{}".format(selected_days), 285 | chat_id = query.message.chat_id, 286 | message_id = query.message.message_id, 287 | reply_markup = InlineKeyboardMarkup(keyboard)) 288 | 289 | def unset(bot, update, args, chat_data): 290 | if str(update.message.chat_id) in dict_settings['allowed_id']: 291 | try: 292 | job_name = args[0] 293 | if job_name in chat_data and job_name in process_array: 294 | job = chat_data[job_name]["job"] 295 | job.schedule_removal() 296 | 297 | del process_array[job_name] 298 | del chat_data[job_name] 299 | 300 | utils.delete_job(database, job_name, owner=str(update.message.chat_id)) 301 | 302 | update.message.reply_text('Job {} successfully unset!'.format(job_name), parse_mode='HTML') 303 | else: 304 | update.message.reply_text("Sorry, job named {} was not found.".format(job_name), parse_mode='HTML') 305 | except (IndexError, ValueError): 306 | update.message.reply_text('Usage: /unset ') 307 | else: 308 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 309 | update.message.reply_text(message, parse_mode='Markdown') 310 | 311 | def reload_jobs(bot, update, job_queue, chat_data): 312 | if str(update.message.chat_id) in dict_settings['allowed_id']: 313 | jobs_list = utils.return_elements(database, owner=str(update.message.chat_id), entity="job") 314 | for singlejob in jobs_list: 315 | chat_data['tmpjob'] = singlejob 316 | chat_data['tmpjob']['job_name'] = chat_data['tmpjob']['name'] 317 | persistend_job(bot, job_queue, chat_data, days=chat_data['tmpjob']['days'], chat_id=update.message.chat_id, db_persist=False) 318 | else: 319 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 320 | update.message.reply_text(message, parse_mode='Markdown') 321 | 322 | def list_jobs(bot, update, chat_data): 323 | message = "" 324 | if len(chat_data) > 0: 325 | for job in chat_data: 326 | message = message + "Job name: {}\nScript name: {}\nUsername: {}\nSchedule at: {}\nDays: {}\n==========\n".format( 327 | chat_data[job]["name"], chat_data[job]["script_name"], chat_data[job]["username"], chat_data[job]["scheduled"], chat_data[job]["days"]) 328 | update.message.reply_text(message, parse_mode='HTML') 329 | else: 330 | update.message.reply_text("You are 0 jobs set", parse_mode='HTML') 331 | 332 | def list_scripts(bot, update): 333 | message = "You have {} scripts configured.".format(len(scripts)) 334 | index = 1 335 | for script in scripts: 336 | message += "\n{}) {}".format(index, script) 337 | index += 1 338 | update.message.reply_text(message, parse_mode='HTML') 339 | 340 | def add_user(bot, update, args): 341 | if str(update.message.chat_id) in dict_settings['allowed_id']: 342 | try: 343 | usernames = [ a['username'].lower() for a in users ] 344 | if args[0].lower() in usernames: 345 | update.message.reply_text("Sorry, username {} is already saved.".format(args[0]), parse_mode='HTML') 346 | else: 347 | users.append({ 348 | "username": args[0], 349 | "password": args[1], 350 | "proxy": None if len(args) < 3 else args[2] 351 | }) 352 | pickle.dump(users, open('users.pickle', 'wb')) 353 | update.message.reply_text("All done! {} correctly saved.".format(args[0]), parse_mode='HTML') 354 | except (IndexError, ValueError): 355 | update.message.reply_text('Usage: /add_user ') 356 | else: 357 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 358 | update.message.reply_text(message, parse_mode='Markdown') 359 | 360 | def delete_user(bot, update, args): 361 | if str(update.message.chat_id) in dict_settings['allowed_id']: 362 | try: 363 | usernames = [ a['username'].lower() for a in users ] 364 | if not args[0].lower() in usernames: 365 | update.message.reply_text("Sorry, username {} is not saved.".format(args[0]), parse_mode='HTML') 366 | else: 367 | for i in range(0, len(users)): 368 | if users[i]['username'].lower() == args[0].lower(): 369 | del users[i] 370 | break 371 | pickle.dump(users, open('users.pickle', 'wb')) 372 | update.message.reply_text("All done! {} correctly deleted.".format(args[0]), parse_mode='HTML') 373 | except (IndexError, ValueError): 374 | update.message.reply_text('Usage: /delete_user ') 375 | else: 376 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 377 | update.message.reply_text(message, parse_mode='Markdown') 378 | 379 | def print_users(bot, update): 380 | if str(update.message.chat_id) in dict_settings['allowed_id']: 381 | usernames = [ a['username'].lower() for a in users ] 382 | message = "You have {} accounts configured.".format(len(usernames)) 383 | index = 1 384 | for username in usernames: 385 | message += "\n{}) {}".format(index, username) 386 | index += 1 387 | update.message.reply_text(message, parse_mode='HTML') 388 | else: 389 | message = 'You have not the permission to use this bot.\nFor more details visit [Telegram-InstaPy-Scheduling](https://github.com/Tkd-Alex/Telegram-InstaPy-Scheduling)' 390 | update.message.reply_text(message, parse_mode='Markdown') 391 | 392 | def error(bot, update, error): 393 | logger.error('Update "%s" caused error "%s"' % (update, error)) 394 | 395 | @plac.annotations(setting_file=("Path of settings.json file", "option", "s", str)) 396 | def main(setting_file='settings.json'): 397 | global scripts 398 | global users 399 | global dict_settings 400 | global database 401 | 402 | try: 403 | cprint("Load setting file from: \"%s\"" % setting_file, "green" ) 404 | with open(setting_file) as f: 405 | settings_json = json.load(f) 406 | except (FileNotFoundError): 407 | cprint("[ERROR] %s is not defined!" % setting_file, "red" ) 408 | sys.exit(1) 409 | 410 | for key in dict_settings: 411 | result, value, message = utils.safe_load_settings(key, settings_json) 412 | if result is False: 413 | cprint("[ERROR] {}".format(message), "red") 414 | if key not in ["telegram_token", "instapy_folder", "allowed_id"]: 415 | cprint("[WARNING] Load default value of: {} : {}".format(key, dict_settings[key]), "yellow") 416 | else: 417 | sys.exit() 418 | else: 419 | dict_settings[key] = value 420 | cprint("[SUCCESS] {}".format(message), "green") 421 | 422 | if dict_settings['project_path'] != "./": 423 | sys.path.insert(0, dict_settings['project_path']) 424 | 425 | try: 426 | from scripts import Scripts 427 | except (ModuleNotFoundError): 428 | cprint("[ERROR] Require \"scripts.py\" file!", "red" ) 429 | sys.exit(1) 430 | 431 | scripts = Scripts().scripts 432 | 433 | try: 434 | users = pickle.load(open(dict_settings['project_path'] + dict_settings['users_path'], 'rb')) 435 | cprint("[SUCCESS] Load users list from: {}".format(dict_settings['users_path']), "green") 436 | except (FileNotFoundError, IOError): 437 | pickle.dump(users, open(dict_settings['project_path'] + dict_settings['users_path'], 'wb')) 438 | cprint("[WARNING] Init user list in: {}".format(dict_settings['users_path']), "yellow" ) 439 | 440 | database = TinyDB('./db.json') 441 | 442 | updater = Updater(dict_settings['telegram_token'], request_kwargs={'read_timeout': 20, 'connect_timeout': 20}) 443 | 444 | dp = updater.dispatcher 445 | 446 | dp.add_handler(CommandHandler("start", help)) 447 | dp.add_handler(CommandHandler("help", help)) 448 | 449 | dp.add_handler(CommandHandler("status", status_process, pass_args=True)) 450 | dp.add_handler(CommandHandler("logs", logs, pass_args=True)) 451 | 452 | dp.add_handler(CommandHandler("set", set_job, pass_args=True, pass_job_queue=True, pass_chat_data=True)) 453 | dp.add_handler(CommandHandler("now", now, pass_args=True)) 454 | dp.add_handler(CommandHandler("stop", stop, pass_args=True)) 455 | 456 | dp.add_handler(CommandHandler("unset", unset, pass_args=True, pass_chat_data=True)) 457 | dp.add_handler(CommandHandler("jobs", list_jobs, pass_chat_data=True)) 458 | dp.add_handler(CommandHandler("reload", reload_jobs, pass_job_queue=True, pass_chat_data=True)) 459 | 460 | dp.add_handler(CommandHandler("add_user", add_user, pass_args=True)) 461 | dp.add_handler(CommandHandler("delete_user", delete_user, pass_args=True)) 462 | dp.add_handler(CommandHandler("users", print_users)) 463 | 464 | dp.add_handler(CommandHandler("scripts", list_scripts)) 465 | 466 | dp.add_handler(CommandHandler("time", timenow)) 467 | 468 | dp.add_handler(CallbackQueryHandler(day_choose, pass_job_queue=True, pass_chat_data=True)) 469 | 470 | dp.add_error_handler(error) 471 | 472 | updater.start_polling(timeout=25) 473 | 474 | cprint("TELEGRAM-INSTAPY-SCHEDULING IS READY!", "green" ) 475 | 476 | updater.idle() 477 | 478 | sys.exit(0) 479 | 480 | if __name__ == '__main__': 481 | plac.call(main) 482 | -------------------------------------------------------------------------------- /process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import multiprocessing, datetime, json, time, os, sys 5 | from stringparse import parse_loglines 6 | 7 | # Colored terminal 8 | from termcolor import colored, cprint 9 | 10 | def reload_process(process, scripts): 11 | attribute = process.return_attribute() 12 | new_process = Process( 13 | attribute['instapy_path'], 14 | attribute['job_name'], 15 | attribute['script_name'], 16 | attribute['chat_id'], 17 | attribute['bot'], 18 | attribute['user']['username'], 19 | attribute['user']['password'], 20 | scripts, 21 | proxy=attribute['user']['proxy'] 22 | ) 23 | return new_process 24 | 25 | class Process (multiprocessing.Process): 26 | def __init__(self, instapy_path, job_name, script_name, chat_id, bot, username, password, scripts, proxy=None): 27 | multiprocessing.Process.__init__(self) 28 | self.instapy_path = instapy_path 29 | self.job_name = job_name 30 | self.script_name = script_name 31 | self.chat_id = chat_id 32 | self.bot = bot 33 | self.username = username 34 | self.password = password 35 | self.scripts = scripts 36 | self.proxy = proxy 37 | 38 | def return_attribute(self): 39 | return { 40 | "instapy_path": self.instapy_path, 41 | "job_name": self.job_name, 42 | "script_name": self.script_name, 43 | "scripts": self.scripts, 44 | "chat_id": self.chat_id , 45 | "bot": self.bot, 46 | "user": { 47 | "username": self.username, 48 | "password": self.password, 49 | "proxy": self.proxy 50 | } 51 | } 52 | 53 | # self.bot.send_message does not work if called by the parent. 54 | def end(self, forced=True): 55 | if forced is True: 56 | self.terminate() 57 | 58 | end = datetime.datetime.now().replace(microsecond=0) 59 | self.bot.send_message(self.chat_id, text='InstaPy Bot end at {}\nExecution time {}'.format(time.strftime("%X"), end-self.start)) 60 | log_file = '{}/logs/{}/general.log'.format(self.instapy_path, self.username) 61 | try: 62 | with open(log_file, "r") as f: 63 | lines = f.readlines() 64 | 65 | message = parse_loglines( lines[-20:], self.username ) 66 | self.bot.send_message(self.chat_id, text=message, parse_mode='HTML') 67 | except (FileNotFoundError): 68 | message_str = "[ERROR] %s is not Available!"%log_file 69 | cprint(message_str, "red" ) 70 | self.bot.send_message(self.chat_id, text="%s"%message_str, parse_mode='HTML') 71 | 72 | 73 | def run(self): 74 | sys.path.append(self.instapy_path) 75 | from instapy import InstaPy 76 | 77 | self.start = datetime.datetime.now().replace(microsecond=0) 78 | self.bot.send_message(self.chat_id, text='InstaPy Bot - {} start at {}'.format(self.job_name, time.strftime("%X"))) 79 | 80 | self.scripts[self.script_name](InstaPy, self.username, self.password, self.proxy) 81 | 82 | self.end(forced=False) 83 | 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asn1crypto==0.24.0 2 | certifi==2018.11.29 3 | cffi==1.11.5 4 | cryptography==2.5 5 | future==0.17.1 6 | plac==1.0.0 7 | pycparser==2.19 8 | python-telegram-bot==11.1.0 9 | six==1.12.0 10 | termcolor==1.1.0 11 | tinydb==3.12.2 -------------------------------------------------------------------------------- /requirements_instapy.txt: -------------------------------------------------------------------------------- 1 | certifi>=2018.10.15 2 | chardet>=3.0.4 3 | clarifai>=2.4.1 4 | configparser>=3.5.0 5 | EasyProcess>=0.2.3 6 | emoji>=0.5.1 7 | future>=0.17.1 8 | googleapis-common-protos>=1.5.5 9 | grpcio>=1.16.1 10 | idna>=2.7 11 | jsonschema>=2.6.0 12 | plyer>=1.3.1 13 | protobuf>=3.6.1 14 | PyVirtualDisplay>=0.2.1 15 | requests>=2.20.1 16 | selenium>=3.141.0 17 | six>=1.11.0 18 | urllib3>=1.24.1 19 | regex>=2018.11.22 20 | MeaningCloud-python>=1.1.1 21 | PyYAML>=3.13 22 | -------------------------------------------------------------------------------- /scripts.py.dist: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import random, time, traceback 5 | 6 | # Create your script here: 7 | def awesome(InstaPy, username, password, proxy=None): 8 | try: 9 | session = InstaPy(username=username, password=password) 10 | session.login() 11 | session.end() 12 | except: 13 | print(traceback.format_exc()) 14 | 15 | def biglikers(InstaPy, username, password, proxy=None): 16 | try: 17 | session = InstaPy(username=username, password=password) 18 | session.login() 19 | session.end() 20 | except: 21 | print(traceback.format_exc()) 22 | 23 | # !! Not delete the following code. 24 | class Scripts: 25 | def __init__(self): 26 | functions = [f for fname, f in sorted(globals().items()) if callable(f)] 27 | self.scripts = {} 28 | for function in functions: 29 | name = str(function.__name__).lower() 30 | if name != "scripts": 31 | self.scripts[name] = function 32 | -------------------------------------------------------------------------------- /settings.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "telegram_token": "xxxx", 3 | "instapy_folder": "/home/.../.../folder_instapy", 4 | "allowed_id": [ 5 | "123123", 6 | "342342" 7 | ] 8 | } -------------------------------------------------------------------------------- /stringparse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import datetime, re 5 | 6 | def parse_time(time): 7 | time = time.split(":") 8 | h = int( time[0] ) 9 | m = int( time[1] ) 10 | s = int( time[2] ) 11 | time = datetime.time(h, m, s) 12 | return time 13 | 14 | """ 15 | INFO [2018-12-05 19:22:48] [tkd_alex] Sessional Live Report: 16 | |> LIKED 62 images | ALREADY LIKED: 1 17 | |> COMMENTED on 0 images 18 | |> FOLLOWED 25 users | ALREADY FOLLOWED: 0 19 | |> UNFOLLOWED 0 users 20 | |> INAPPROPRIATE images: 344 21 | |> NOT VALID users: 142 22 | On session start was FOLLOWING 6708 users & had 17371 FOLLOWERS 23 | 24 | OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO 25 | INFO [2018-12-05 19:22:48] [tkd_alex] Session ended! 26 | oooooooooooooooooooooooooooooooooooooooooooooooooooo 27 | _____________________________________________________________ 28 | 29 | INFO [2018-12-16 18:28:29] [tkd_alex] --> Total people unfollowed : 397 30 | 31 | """ 32 | 33 | def username_stripper(line, username): 34 | line = line[line.find("[{}]".format(username)):] 35 | line = line.replace("[{}]".format(username), "") 36 | return line.strip() 37 | 38 | def clear_lines(message, username=None): 39 | lines = message.replace('-->', '').split('\n') # Remove arrow 40 | # Clear lines, remove space and INFO [2018-12-16 18:28:29] [tkd_alex] 41 | message = '\n'.join( ( line.strip() if username is None else username_stripper(line, username) ) for line in lines if line.strip() != "") 42 | return message 43 | 44 | def parse_loglines(lines, username=None): 45 | # Merge the string by new line char. Keep the text between 'sessional live report' and 'OOOOOO' 46 | message = '\n'.join(x for x in lines) 47 | if 'Session ended!' in message: 48 | message = message[message.find('Sessional Live Report'):message.find('OOOOOO')] 49 | lines = message.split('\n') 50 | message = '\n'.join(line.strip() for line in lines if line.strip() != "") 51 | for boldword in [ "LIKED", "COMMENTED", "FOLLOWED", "UNFOLLOWED", "INAPPROPRIATE", "NOT VALID" ]: 52 | message = message.replace("ALREADY {}".format(boldword), "ALREADY {}".format(boldword)) 53 | message = message.replace("|> {}".format(boldword), "|> {}".format(boldword)) 54 | return message 55 | else: 56 | message = clear_lines(message, username) 57 | return message.split('\n')[-1] # Return the last line of logs. -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from tinydb import Query, where 5 | 6 | def safe_load_settings(field, settings): 7 | if field in settings: 8 | return True, settings[field], "Load {} : {}".format(field, settings[field]) 9 | return False, None, "Unable to load {}".format(field) 10 | 11 | def return_elements(database, owner, entity="job"): 12 | query = Query() 13 | items = database.search((where('entity') == entity) & (where('owner') == owner)) 14 | return items 15 | 16 | def save_element(database, data, owner, entity="job"): 17 | data['entity'] = entity 18 | data['owner'] = str(owner) 19 | database.insert( data ) 20 | 21 | def delete_job(database, name, owner): 22 | database.remove((where('entity') == "job") & (where('owner') == owner) & (where('name') == name)) 23 | 24 | days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] 25 | 26 | def fix_days(days_args): 27 | if type(days_args[0]) == int: 28 | return days_args 29 | return [ days.index(day.strip()) for day in days_args.split(',') ] --------------------------------------------------------------------------------