├── __init__.py ├── requirements.txt ├── src ├── myexceptions.py ├── imagesite.py ├── browser.py ├── __init__.py ├── publisher.py ├── database.py ├── flooder.py ├── user.py ├── imagecache.py ├── subscriptions.py ├── messages.py └── spy.py ├── Dockerfile ├── docker-compose.yml ├── tests ├── __init__.py ├── test_imagecache.py ├── test_subscriptions.py └── test_spy.py ├── README.md ├── data.json ├── LICENSE ├── .gitignore ├── example.py └── bot.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.20.0 2 | pyTelegramBotAPI==2.1.7 3 | pymongo==3.4.0 4 | python-decouple==3.0 5 | gevent==1.2.1 6 | greenlet==0.4.11 -------------------------------------------------------------------------------- /src/myexceptions.py: -------------------------------------------------------------------------------- 1 | class GroupNotFoundException(Exception): 2 | pass 3 | 4 | 5 | class UserNotFoundException(Exception): 6 | pass 7 | 8 | 9 | class AlreadyExistsOnDatabaseException(Exception): 10 | pass 11 | 12 | 13 | class ChatIdOrTextCannotBeEmpty(Exception): 14 | pass 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frolvlad/alpine-python3 2 | 3 | RUN apk --update add python3 python3-dev 4 | RUN apk --update add --virtual build-dependencies build-base && rm -f /var/cache/apk/* 5 | RUN adduser -S bot 6 | RUN mkdir /bot 7 | WORKDIR /bot 8 | COPY . /bot/spylist 9 | WORKDIR /bot/spylist 10 | RUN pip install -r requirements.txt 11 | USER bot -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | bot: 2 | container_name: spy_bot 3 | build: . 4 | command: /usr/bin/python3 bot.py 5 | restart: always 6 | volumes: 7 | - .:/bot/spylist 8 | env_file: .env 9 | user: bot 10 | links: 11 | - database 12 | log_opt: 13 | max-size: "50k" 14 | 15 | database: 16 | container_name: spy_bot_db 17 | image: "mongo" 18 | ports: 19 | - "127.0.0.1:27017:27017" 20 | volumes: 21 | - mongodata:/data 22 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | from src.database import MongoSetup, CollectionFactory 2 | 3 | MONGO_URI = 'mongodb://localhost:27017/data' 4 | 5 | 6 | # Database settings 7 | def create_database_collection(database_name, collection_name): 8 | mongo_client = MongoSetup(MONGO_URI, database_name) 9 | collection_factory = CollectionFactory(mongo_client) 10 | collection = collection_factory.create(collection_name) 11 | # Clear the collection before start the test 12 | collection.drop() 13 | 14 | return collection 15 | -------------------------------------------------------------------------------- /src/imagesite.py: -------------------------------------------------------------------------------- 1 | from src.user import User 2 | from src.browser import Browser 3 | 4 | 5 | class ImageSite(): 6 | BASE_URL = 'https://www.instagram.com/{}' 7 | 8 | def __init__(self, browser=None): 9 | self.browser = browser 10 | # Default browser to simplify 11 | if not self.browser: 12 | self.browser = Browser() 13 | 14 | def get_user(self, username): 15 | content = self._get_user_content(username) 16 | return User(username, content) 17 | 18 | def _get_user_content(self, username): 19 | url = ImageSite.BASE_URL.format(username) 20 | self.browser.set_url(url) 21 | return self.browser.get_page_content() 22 | -------------------------------------------------------------------------------- /src/browser.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import shutil 3 | 4 | 5 | class Browser(): 6 | """Handle HTTP requests""" 7 | 8 | def __init__(self, url=''): 9 | self.url = url 10 | 11 | def set_url(self, url): 12 | self.url = url 13 | 14 | def get(self): 15 | return requests.get(self.url) 16 | 17 | # Return HTML text content 18 | def get_page_content(self): 19 | page = self.get() 20 | return page.text 21 | 22 | # Download content from URL and save with the given filename 23 | def save_as(self, filename): 24 | response = requests.get(self.url, stream=True) 25 | with open(filename, 'wb') as out_file: 26 | shutil.copyfileobj(response.raw, out_file) 27 | del response 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spy Manager bot 2 | 3 | Create and manager groups of users from ImageSite getting the latest (12 till now) posted photos from each user. 4 | 5 | 6 | ## Environment file 7 | 8 | Create a `.env` file on the project root folder: 9 | 10 | ```sh 11 | API_TOKEN=MY_TELEGRAM_API 12 | ADMIN_ID=YOUR_ID 13 | ``` 14 | 15 | ## Run 16 | 17 | `docker-compose build && docker-compose up` 18 | 19 | ## Code example without bot: 20 | 21 | ### Install dependencies 22 | 23 | `pip install -r requeriments.txt` 24 | 25 | ### Application 26 | 27 | **MongoDB Server** 28 | 29 | To store the data into a separated container: 30 | 31 | `docker volume create --name mongodata` 32 | 33 | Runing: 34 | 35 | `docker run -d -p 27017:27017 --name mongod -v mongodata:/data mongo mongod` 36 | 37 | `python example.py` -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | from .myexceptions import AlreadyExistsOnDatabaseException 3 | 4 | TELEGRAM_URL = 'https://api.telegram.org/bot{token}/{method}?chat_id={chat_id}&text={text}' 5 | 6 | 7 | class Manager(): 8 | """ Manage objects adding, removing and getting from database """ 9 | 10 | def __init__(self, collection): 11 | self.collection = collection 12 | 13 | def add(self, username): 14 | try: 15 | self.collection.insert_one(self.newObj) 16 | except pymongo.errors.DuplicateKeyError: 17 | raise AlreadyExistsOnDatabaseException 18 | 19 | def remove(self, username): 20 | self.collection.find_one_and_delete({"username": username}) 21 | 22 | def all(self): 23 | cursor = self.collection.find() 24 | return [document for document in cursor] 25 | 26 | def get(self, username): 27 | pass 28 | -------------------------------------------------------------------------------- /src/publisher.py: -------------------------------------------------------------------------------- 1 | from .browser import Browser 2 | from . myexceptions import ChatIdOrTextCannotBeEmpty 3 | from . import TELEGRAM_URL 4 | 5 | 6 | class Publisher(): 7 | 8 | def __init__(self, api_token): 9 | self.api_token = api_token 10 | self.browser = Browser() 11 | self.chat_id = '' 12 | self.text_to_send = '' 13 | 14 | def _create_url(self): 15 | return TELEGRAM_URL.format( 16 | token=self.api_token, 17 | method='sendMessage', 18 | chat_id=self.chat_id, 19 | text=self.text_to_send 20 | ) 21 | 22 | def send_to(self, chat_id): 23 | self.chat_id = chat_id 24 | 25 | def send(self, text): 26 | self.text_to_send = text 27 | 28 | if not self.chat_id or not self.text_to_send: 29 | raise ChatIdOrTextCannotBeEmpty 30 | 31 | url = self._create_url() 32 | self.browser.set_url(url) 33 | self.browser.get() 34 | -------------------------------------------------------------------------------- /src/database.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | 3 | 4 | class MongoSetup(): 5 | def __init__(self, uri, databaseName): 6 | self.uri = uri 7 | self.databaseName = databaseName 8 | self._create_client() 9 | 10 | def use_collection(self, collectionName): 11 | self.collection = self._db.get_collection(name=collectionName) 12 | 13 | def _create_client(self): 14 | self.client = pymongo.MongoClient(self.uri) 15 | self._db = self.client[self.databaseName] 16 | 17 | def create_index(self, field, unique=True): 18 | self.collection.create_index( 19 | [(field, pymongo.ASCENDING)], unique=unique 20 | ) 21 | 22 | 23 | class CollectionFactory(): 24 | def __init__(self, mongo_client): 25 | self.mongo_client = mongo_client 26 | 27 | def create(self, collectionName, index=True): 28 | self.mongo_client.use_collection(collectionName) 29 | if index: 30 | self.mongo_client.create_index(field='username') 31 | 32 | return self.mongo_client.collection 33 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | # Spies 2 | { 3 | "spies": [{ 4 | "username": "", 5 | "groups": [{ 6 | "name": "", 7 | "users": ["", ""] 8 | }, { 9 | "name": "", 10 | "users": ["", ""] 11 | }], 12 | "created": "", 13 | "chat_id": "" 14 | }, { 15 | "username": "", 16 | "groups": [{ 17 | "name": "", 18 | "users": ["", ""], 19 | "images": [""] 20 | }, { 21 | "name": "", 22 | "users": ["", ""] 23 | }], 24 | "created": "", 25 | "chat_id": "" 26 | }] 27 | } 28 | 29 | # Images cache 30 | { 31 | "pictures": [{ 32 | "username": "pinheirofellipe", 33 | "images": ["imgUrl1", "imgUrl2"] 34 | }, { 35 | "username": "mazulo_", 36 | "images": ["imgUrl1", "imgUrl2"] 37 | }, { 38 | "username": "ciclano", 39 | "images": ["imgUrl1", "imgUrl2"] 40 | }] 41 | } 42 | 43 | # Pub/sub 44 | { 45 | "subscriptions": [{ 46 | "username": "pinheirofellipe", 47 | "subscribers": [{ 48 | "spy": "spy1", 49 | "group": "g1", 50 | "chat_id": "" 51 | }, 52 | { 53 | "username": "mazulo_", 54 | "subscribers": [{ 55 | "spy": "spy1", 56 | "group": "g1", 57 | "chat_id": "" 58 | }] 59 | }] 60 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Fellipe Pinheiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_imagecache.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, '../spymanager') 3 | sys.path.insert(0, '../') 4 | 5 | from tests import create_database_collection 6 | from src.imagecache import UserImageCacheManager 7 | 8 | 9 | # Database settings 10 | DATABASE_NAME = 'spies_database' 11 | COLLECTION_NAME = 'images_cache' 12 | images_cache_collection = create_database_collection(DATABASE_NAME, COLLECTION_NAME) 13 | 14 | # User to test 15 | USERNAME = 'pinheirofellipe' 16 | 17 | user_image_manager = UserImageCacheManager(images_cache_collection) 18 | 19 | # Clear before tests 20 | user_image_manager.remove(USERNAME) 21 | 22 | user_image_manager.add(USERNAME) 23 | 24 | all_user_images = user_image_manager.all() 25 | 26 | assert len(all_user_images) == 1 27 | 28 | user = user_image_manager.get(USERNAME) 29 | 30 | assert user.username == USERNAME 31 | 32 | images = [ 33 | 'someURL1', 'someURL2', 'someURL3' 34 | ] 35 | 36 | user.add_images(images) 37 | print(user) 38 | assert len(user.images) == 3 39 | 40 | image_to_remove = 'someURL1' 41 | 42 | user.remove_image(image_to_remove) 43 | 44 | assert len(user.images) == 2 45 | 46 | print('Well done!') 47 | -------------------------------------------------------------------------------- /.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /tests/test_subscriptions.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, '../spymanager') 3 | sys.path.insert(0, '../') 4 | 5 | from tests import create_database_collection 6 | from src.subscriptions import SubscriptionsManager 7 | 8 | 9 | # Database settings 10 | DATABASE_NAME = 'spies_database' 11 | COLLECTION_NAME = 'subscriptions' 12 | subscriptions_collection = create_database_collection(DATABASE_NAME, COLLECTION_NAME) 13 | 14 | subscriptions_manager = SubscriptionsManager(subscriptions_collection) 15 | 16 | # User to test 17 | USERNAME = 'pinheirofellipe' 18 | 19 | # Clear before tests 20 | subscriptions_manager.remove(USERNAME) 21 | 22 | subscriptions_manager.add(USERNAME) 23 | 24 | all_subscritions = subscriptions_manager.all() 25 | 26 | assert len(all_subscritions) == 1 27 | 28 | user = subscriptions_manager.get(USERNAME) 29 | 30 | assert user.username == USERNAME 31 | 32 | assert user.exists() is True 33 | 34 | subscribers = [ 35 | { 36 | "spy": "spy1", 37 | "group": "g1", 38 | "chat_id": 123456 39 | }, { 40 | "spy": "spy2", 41 | "group": "g1", 42 | "chat_id": 654321 43 | } 44 | ] 45 | 46 | user.add_subscribers(subscribers) 47 | 48 | assert len(user.subscribers) == 2 49 | 50 | subscriber_to_remove = { 51 | "spy": "spy1", 52 | "group": "g1", 53 | "chat_id": 123456 54 | } 55 | 56 | user.remove_subscriber(subscriber_to_remove) 57 | 58 | assert len(user.subscribers) == 1 59 | 60 | print('Well done!') 61 | -------------------------------------------------------------------------------- /src/flooder.py: -------------------------------------------------------------------------------- 1 | import gevent 2 | from gevent import monkey, pool 3 | 4 | 5 | monkey.patch_all() 6 | 7 | 8 | class Flooder(): 9 | def __init__(self, site, publisher, image_cache_handler): 10 | self.site = site 11 | self.publisher = publisher 12 | self.image_cache_handler = image_cache_handler 13 | 14 | self.cache_handlers = [] 15 | 16 | def _push_to(self, chat_id, messages): 17 | self.publisher.send_to(chat_id) 18 | for message in messages: 19 | self.publisher.send(message) 20 | 21 | def _pull_from(self, subscription): 22 | user = self.site.get_user(subscription['username']) 23 | 24 | self.image_cache_handler.get_or_create(username=user.username) 25 | new_images = self.image_cache_handler.get_the_news(user.images) 26 | 27 | # This need run after send all images, because bulk is raising an 28 | # InvalidOperation Exception: Bulk operations can only be executed once 29 | self.image_cache_handler.add_the_images(new_images) 30 | 31 | chat_ids = [s['chat_id'] for s in subscription['subscribers']] 32 | 33 | p = pool.Pool(5) 34 | for _id in chat_ids: 35 | p.spawn(self._push_to, _id, new_images) 36 | p.join() 37 | 38 | def flood_to(self, subscriptions): 39 | jobs = [ 40 | gevent.spawn(self._pull_from, subscription) 41 | for subscription in subscriptions 42 | ] 43 | gevent.wait(jobs) 44 | -------------------------------------------------------------------------------- /tests/test_spy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, '../spymanager') 3 | sys.path.insert(0, '../') 4 | 5 | from tests import create_database_collection 6 | from src.spy import SpyManager 7 | 8 | # Database settings 9 | DATABASE_NAME = 'spies_database' 10 | COLLECTION_NAME = 'subscriptions' 11 | subscriptions_collection = create_database_collection(DATABASE_NAME, COLLECTION_NAME) 12 | 13 | # User to test 14 | USERNAME = 'pinheirofellipe' 15 | CHAT_ID = 123456 16 | 17 | # Spy actions 18 | spy_manager = SpyManager(subscriptions_collection) 19 | 20 | # Remove if it's exists 21 | spy_manager.remove(USERNAME) 22 | 23 | # Adding bot user 24 | spy_manager.add(USERNAME, CHAT_ID) 25 | 26 | all_spies = spy_manager.all() 27 | 28 | assert len(all_spies) == 1 29 | 30 | # Get created spy 31 | spy = spy_manager.get(USERNAME) 32 | 33 | assert spy.username == USERNAME 34 | 35 | assert spy.exists() is True 36 | 37 | # Adding groups 38 | new_group = 'devs' 39 | spy.add_group(new_group) 40 | 41 | new_group = 'sports' 42 | spy.add_group(new_group) 43 | 44 | assert len(spy.groups) == 2 45 | 46 | # Adding user to group 47 | member_mazulo = 'mazulo_' 48 | member_pinheiro = 'pinheirofellipe' 49 | group_to_add = 'devs' 50 | 51 | spy.add_members_to_group([member_mazulo, member_pinheiro], group_to_add) 52 | 53 | assert len(spy.members_from_group('devs')) == 2 54 | 55 | # Remove group 56 | spy.remove_group('sports') 57 | 58 | assert len(spy.groups) == 1 59 | 60 | # Removing member 61 | spy.remove_member_from_group('mazulo_', 'devs') 62 | 63 | assert len(spy.members_from_group('devs')) == 1 64 | 65 | print('Well done!') 66 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | from src.database import MongoSetup, CollectionFactory 2 | from src.spy import SpyManager 3 | from src.imagesite import ImageSite 4 | from src.myexceptions import AlreadyExistsOnDatabaseException 5 | 6 | # Database settings 7 | MONGO_URI = 'mongodb://localhost:27017/data' 8 | DATABASE_NAME = 'spies_database' 9 | COLLECTION_NAME = 'spies' 10 | 11 | # User to test 12 | USERNAME = 'pinheirofellipe' 13 | CHAT_ID = 123465 14 | 15 | mongo_client = MongoSetup(MONGO_URI, DATABASE_NAME) 16 | collection_factory = CollectionFactory(mongo_client) 17 | 18 | spies_collection = collection_factory.create(COLLECTION_NAME) 19 | 20 | # Spy actions 21 | spy_manager = SpyManager(spies_collection) 22 | 23 | # Remove if it's exists 24 | spy_manager.remove(USERNAME) 25 | 26 | # Adding bot user 27 | try: 28 | spy_manager.add(USERNAME, CHAT_ID) 29 | except AlreadyExistsOnDatabaseException as e: 30 | print('Spy {} already exists!'.format(USERNAME)) 31 | 32 | # Get created spy 33 | spy = spy_manager.get(USERNAME) 34 | 35 | # Adding groups 36 | new_group = 'devs' 37 | spy.add_group(new_group) 38 | 39 | new_group = 'sports' 40 | spy.add_group(new_group) 41 | 42 | # Adding user to group 43 | member_mazulo = 'mazulo_' 44 | member_pinheiro = 'pinheirofellipe' 45 | group_to_add = 'devs' 46 | 47 | spy.add_members_to_group([member_mazulo, member_pinheiro], group_to_add) 48 | 49 | # Remove group 50 | spy.remove_group('sports') 51 | 52 | # Printing 53 | print('\nMy groups: {}'.format(spy.groups)) 54 | 55 | # Removing member 56 | print("\n Removing mazulo_ from devs") 57 | spy.remove_member_from_group('mazulo_', 'devs') 58 | 59 | print('\nMy groups: {}'.format(spy.groups)) 60 | 61 | site = ImageSite() 62 | 63 | for group in spy.groups: 64 | print('\n\n\t\tMembers from {} group\n\n'.format(group['name'])) 65 | 66 | for member_username in group['users']: 67 | print('\n\tImages from {}\n\n'.format(member_username)) 68 | user = site.get_user(member_username) 69 | user.print_images_urls() 70 | -------------------------------------------------------------------------------- /src/user.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from src.myexceptions import UserNotFoundException 4 | 5 | 6 | class User(): 7 | """ SiteImage user model """ 8 | 9 | def __init__(self, username, html_page): 10 | self.username = username 11 | self._data = self._extract_data_from_html(html_page) 12 | 13 | self._images = self._get_images(self._data) 14 | self._get_user_informations(self._data) 15 | 16 | def __repr__(self): 17 | return '{}'.format(self.username) 18 | 19 | @property 20 | def images(self): 21 | return [self._images[image]['src'] for image in self._images] 22 | 23 | def _extract_data_from_html(self, html_content): 24 | json_text = self._extract_json_data_from_html(html_content) 25 | # clean text removing these junk strings 26 | first = '' 28 | 29 | json_text = json_text.replace(first, "") 30 | json_text = json_text.replace(last, "") 31 | 32 | data = json.loads(json_text) 33 | 34 | try: 35 | user = data['entry_data']['ProfilePage'][0]['user'] 36 | except KeyError: 37 | raise UserNotFoundException("{} not found!".format(self.username)) 38 | 39 | return user 40 | 41 | def _extract_json_data_from_html(self, html_page): 42 | json_text = '' 43 | lines = html_page.split('\n') 44 | for line in lines: 45 | if 'window._sharedData =' in line: 46 | json_text = line 47 | break 48 | return json_text 49 | 50 | def _get_images(self, user_data): 51 | nodes = user_data['media']['nodes'] 52 | 53 | images = {} 54 | 55 | for node in nodes: 56 | images[node['code']] = { 57 | 'src': node.get('display_src'), 58 | 'caption': node.get('caption') 59 | } 60 | return images 61 | 62 | def _get_user_informations(self, user_data): 63 | self.id = user_data['id'] 64 | self.name = user_data['full_name'] 65 | # Following back only works if who is requesting is logged 66 | self.is_following_back = user_data['follows_viewer'] 67 | self.is_private = user_data['follows_viewer'] 68 | 69 | def print_images_urls(self): 70 | for image in self._images: 71 | print(self._images[image]['src']) 72 | -------------------------------------------------------------------------------- /src/imagecache.py: -------------------------------------------------------------------------------- 1 | from pymongo import UpdateOne 2 | from . import Manager 3 | from .myexceptions import AlreadyExistsOnDatabaseException 4 | 5 | 6 | class UserImageCacheManager(Manager): 7 | def add(self, username): 8 | self.newObj = { 9 | "username": username, 10 | "images": [] 11 | } 12 | super().add(username) 13 | 14 | def get(self, username): 15 | return UserImageCache(username=username, collection=self.collection) 16 | 17 | 18 | class UserImageCache(): 19 | 20 | def __init__(self, username, collection): 21 | self.username = username 22 | self.images_cache = collection 23 | self._user = self.images_cache.find_one({"username": self.username}) 24 | 25 | @property 26 | def images(self): 27 | return self._user['images'] 28 | 29 | def add_images(self, images): 30 | if type(images) != list: 31 | images = [images] 32 | 33 | new_images = [image for image in images if image not in self.images] 34 | 35 | if new_images: 36 | requests = self._add_images(new_images) 37 | self.images_cache.bulk_write(requests) 38 | else: 39 | print('Dont have new images') 40 | 41 | def _add_images(self, new_images): 42 | requests = [] 43 | for image in new_images: 44 | requests.append( 45 | UpdateOne( 46 | {'username': self.username}, 47 | {'$push': {'images': image}} 48 | ) 49 | ) 50 | return requests 51 | 52 | def remove_image(self, image): 53 | if not self.isImageExists(image): 54 | print('Image {} does not exist!'.format(image)) 55 | return 56 | 57 | self.images_cache.find_one_and_update( 58 | {'username': self.username}, 59 | {'$pull': { 60 | 'images': { 61 | '$in': [image] 62 | } 63 | }} 64 | ) 65 | 66 | def isImageExists(self, image): 67 | return image in self.images 68 | 69 | 70 | class ImageCacheHandler(): 71 | def __init__(self, image_cache_manager): 72 | self.image_cache_manager = image_cache_manager 73 | self.image_cache = None 74 | 75 | def get_or_create(self, username): 76 | try: 77 | self.image_cache_manager.add(username) 78 | except AlreadyExistsOnDatabaseException: 79 | # It is not a problem, go on! 80 | pass 81 | finally: 82 | self.image_cache = self.image_cache_manager.get(username) 83 | 84 | def get_the_news(self, user_images): 85 | new_images = [ 86 | i for i in user_images 87 | if not self.image_cache.isImageExists(i) 88 | ] 89 | return new_images 90 | 91 | def add_the_images(self, user_images): 92 | self.image_cache.add_images(user_images) 93 | -------------------------------------------------------------------------------- /src/subscriptions.py: -------------------------------------------------------------------------------- 1 | from pymongo.errors import InvalidOperation 2 | from . import Manager 3 | 4 | 5 | class SubscriptionsManager(Manager): 6 | def add(self, username): 7 | self.newObj = { 8 | "username": username, 9 | "subscribers": [] 10 | } 11 | super().add(username) 12 | 13 | def get(self, username): 14 | return Subscription(username=username, collection=self.collection) 15 | 16 | def filterByGroup(self, spy_user, group_name): 17 | subscriptions = self.collection.find( 18 | {'$and': [ 19 | {'subscribers.spy': spy_user.username}, 20 | {'subscribers.group': group_name} 21 | ]}) 22 | return subscriptions 23 | 24 | 25 | class Subscription(): 26 | 27 | def __init__(self, username, collection): 28 | self.username = username 29 | self.subscriptions = collection 30 | 31 | self.bulk = self.subscriptions.initialize_unordered_bulk_op() 32 | 33 | @property 34 | def _user(self): 35 | return self.subscriptions.find_one({"username": self.username}) 36 | 37 | @property 38 | def subscribers(self): 39 | return self._user['subscribers'] 40 | 41 | def exists(self): 42 | return self._user is not None 43 | 44 | def add_subscribers(self, subscribers): 45 | if type(subscribers) != list: 46 | subscribers = [subscribers] 47 | 48 | for subscriber in subscribers: 49 | if not self._is_subscriber_exists(subscriber): 50 | self.bulk.find( 51 | {'username': self.username} 52 | ).update({'$push': {'subscribers': subscriber}}) 53 | 54 | else: 55 | msg = 'Subscriber {} already exists with group {}!' 56 | print(msg.format(subscriber['spy'], subscriber['group'])) 57 | try: 58 | self.bulk.execute() 59 | except InvalidOperation as e: 60 | print(e) 61 | 62 | def remove_subscriber(self, subscriber): 63 | if not self._is_subscriber_exists(subscriber): 64 | msg = 'Subscriber {} with group {} does not exist!' 65 | print(msg.format(subscriber['spy'], subscriber['group'])) 66 | return 67 | 68 | self.subscriptions.find_one_and_update( 69 | {'$and': [ 70 | {'username': self.username}, 71 | {'subscribers.spy': subscriber['spy']}, 72 | {'subscribers.group': subscriber['group']} 73 | ]}, 74 | {'$pull': {'subscribers': {'$in': [subscriber]}}} 75 | ) 76 | 77 | def _is_subscriber_exists(self, subscriber_find): 78 | subscriber = self.subscriptions.find_one( 79 | {'$and': [ 80 | {'username': self.username}, 81 | {'subscribers.spy': subscriber_find['spy']}, 82 | {'subscribers.group': subscriber_find['group']} 83 | ]}) 84 | 85 | return subscriber is not None 86 | -------------------------------------------------------------------------------- /src/messages.py: -------------------------------------------------------------------------------- 1 | WHOAMI = """ 2 | User: @{0.username} 3 | ID: {0.id} 4 | Name: {0.first_name} {0.last_name} 5 | """ 6 | 7 | REGISTER_FIRST = """ 8 | To initiate, register first! 9 | 10 | /register 11 | """ 12 | 13 | ALREADY_REGISTER = """ 14 | I can see that you're already register {0}. 15 | 16 | Type /help and enjoy! 17 | """ 18 | REGISTERED = """ 19 | You was registered. 20 | 21 | Now you can create a group and add some users to it. 22 | """ 23 | 24 | ABOUT = """ 25 | Hi {0}, I'm Spies Manager Bot and I can help you to be a spy too! 26 | 27 | With me you can create groups and add users, follow them, download their pictures and be aware with everything that happens. 28 | 29 | Join our secret society! 30 | 31 | Enter /help to get all commands. 32 | """ 33 | 34 | UNREGISTERED = """ 35 | You was unregistered! 36 | """ 37 | 38 | NEW_GROUP_ADDED = """ 39 | *{0}* group created! 40 | """ 41 | 42 | GROUP_REMOVED = """ 43 | *{0}* group was removed! 44 | """ 45 | 46 | REGISTERED_GROUPS = """ 47 | Your groups: 48 | """ 49 | 50 | MEMBERS_GROUPS = """ 51 | Members from *{0}* group: 52 | """ 53 | 54 | NEW_USER_ADDED_TO_GROUP = """ 55 | The user(s) @{0} was added to *{1}* group! 56 | """ 57 | 58 | USER_REMOVED_FROM_GROUP = """ 59 | The user @{0} was removed from *{1}* group! 60 | """ 61 | 62 | HELP = """ 63 | /start Welcome message 64 | /help Get all commands 65 | /links Some links 66 | /about Know about us 67 | 68 | - Spy commands: 69 | /register Register as a spy 70 | /unregister Unregister as a spy 71 | /whoami Informations about the spy 72 | 73 | - Group commands: 74 | /addgroup Create a new group 75 | /rmgroup Remove an existing group 76 | /groups List all groups name 77 | /updategroup Get the new photos from a group 78 | 79 | - User commands: 80 | /adduser Add multiple users to a group 81 | (USE COMMA TO ADD MORE THAN ONE USERS) 82 | /rmuser Remove a user from a group 83 | 84 | - Shortcuts 85 | Already know the group name and wanna get the new photos? 86 | Use: $GROUP_NAME 87 | 88 | Wanna get images from a user? 89 | Use: @USERNAME 90 | """ 91 | 92 | LINKS = """\ 93 | Telegram: 94 | https://telegram.me/@SpyList_bot 95 | 96 | GitHub: 97 | https://github.com/delete/spymanager 98 | """ 99 | 100 | START = """ 101 | Hey {0}, I'm the SpyManager bot. 102 | 103 | I will help you to manage your users' groups. 104 | 105 | Enter /help to get all commands. 106 | """ 107 | 108 | TELEGRAM_NOME_USUARIO_AJUDA = """ 109 | 110 | Você ainda não definiu o seu nome de usuário no Telegram. 111 | Para defini-lo, vá na opção "Configurações" que fica no menu inicial do Telegram e modifique "Nome de usuário". 112 | 113 | """ 114 | TELEGRAM_ULTIMO_NOME_AJUDA = """ 115 | 116 | Você ainda não definiu o seu sobrenome no Telegram. 117 | Para defini-lo, vá na opção "Configurações" que fica no menu inicial do Telegram, e modifique "sobrenome". 118 | 119 | """ 120 | 121 | BOT_PRIVADO = "Talk to me on private mode. "\ 122 | "Send a message to @SpyList_bot and let's talk!" 123 | -------------------------------------------------------------------------------- /src/spy.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from pymongo.errors import InvalidOperation 3 | from . import Manager 4 | from .myexceptions import AlreadyExistsOnDatabaseException 5 | 6 | 7 | class SpyManager(Manager): 8 | def add(self, username, chat_id): 9 | self.newObj = { 10 | "username": username, 11 | "groups": [], 12 | "created": datetime.datetime.utcnow(), 13 | "chat_id": chat_id 14 | } 15 | super().add(username) 16 | 17 | def get(self, username): 18 | return Spy(username=username, collection=self.collection) 19 | 20 | 21 | class Spy(): 22 | """ Spy model that wrappers spy mongo object """ 23 | 24 | def __init__(self, username, collection): 25 | self.username = username 26 | self.spies = collection 27 | 28 | self.bulk = self.spies.initialize_unordered_bulk_op() 29 | 30 | @property 31 | def _spy(self): 32 | return self.spies.find_one({"username": self.username}) 33 | 34 | @property 35 | def groups(self): 36 | return self._spy['groups'] 37 | 38 | @property 39 | def chat_id(self): 40 | return self._spy['chat_id'] 41 | 42 | @property 43 | def groups_names(self): 44 | return [group['name'] for group in self.groups] 45 | 46 | def members_from_group(self, group_name): 47 | found_group = None 48 | for group in self.groups: 49 | if group['name'] == group_name: 50 | found_group = group 51 | break 52 | return found_group['users'] 53 | 54 | def exists(self): 55 | return self._spy is not None 56 | 57 | def add_group(self, group_name): 58 | newGroup = { 59 | "name": group_name, 60 | "users": [] 61 | } 62 | 63 | if newGroup['name'] not in self.groups_names: 64 | self.spies.find_one_and_update( 65 | {'username': self.username}, 66 | {'$push': {'groups': newGroup}} 67 | ) 68 | else: 69 | raise AlreadyExistsOnDatabaseException 70 | 71 | def remove_group(self, group_name): 72 | if not self._isGroupExists(group_name): 73 | print('Group {} does not exist!'.format(group_name)) 74 | return 75 | 76 | self.spies.find_one_and_update( 77 | {'$and': [ 78 | {'username': self.username}, 79 | {'groups.name': group_name} 80 | ]}, 81 | {'$pull': {'groups': {"name": group_name}}} 82 | ) 83 | 84 | def add_members_to_group(self, members_username, group_name): 85 | found_group = None 86 | 87 | if type(members_username) != list: 88 | members_username = [members_username] 89 | 90 | for group in self.groups: 91 | if group['name'] == group_name: 92 | found_group = group 93 | break 94 | 95 | if found_group: 96 | new_members = [ 97 | member for member in members_username 98 | if member not in found_group['users'] 99 | ] 100 | for member in new_members: 101 | self.bulk.find({'$and': [ 102 | {'username': self.username}, 103 | {'groups.name': group_name} 104 | ]}).update({'$push': {'groups.$.users': member}}) 105 | 106 | try: 107 | self.bulk.execute() 108 | except InvalidOperation as e: 109 | print(e) 110 | else: 111 | print('Group {} does not exist!'.format(group_name)) 112 | 113 | def remove_member_from_group(self, member_username, group_name): 114 | if not self._isGroupExists(group_name): 115 | print('Group {} does not exist!'.format(group_name)) 116 | return 117 | 118 | self.spies.find_one_and_update( 119 | {'$and': [ 120 | {'username': self.username}, 121 | {'groups.name': group_name} 122 | ]}, 123 | {'$pull': {'groups.$.users': {'$in': [member_username]}}} 124 | ) 125 | 126 | def _isGroupExists(self, group_name): 127 | return group_name in self.groups_names 128 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import telebot 2 | from telebot import types 3 | import logging 4 | from datetime import datetime, timedelta 5 | from decouple import config 6 | 7 | from src.messages import * 8 | from src.spy import SpyManager 9 | from src.database import MongoSetup, CollectionFactory 10 | from src.imagesite import ImageSite 11 | from src.subscriptions import SubscriptionsManager 12 | from src.myexceptions import AlreadyExistsOnDatabaseException 13 | from src.imagecache import UserImageCacheManager, ImageCacheHandler 14 | from src.flooder import Flooder 15 | from src.publisher import Publisher 16 | 17 | # Read API_KEY from .env file 18 | API_TOKEN = config('API_TOKEN') 19 | ADMIN_ID = config('ADMIN_ID') 20 | 21 | bot = telebot.TeleBot(API_TOKEN, threaded=True) 22 | 23 | # Database settings 24 | MONGO_URI = 'mongodb://database:27017/data' 25 | DATABASE_NAME = 'spies_database' 26 | 27 | mongo_client = MongoSetup(MONGO_URI, DATABASE_NAME) 28 | collection_factory = CollectionFactory(mongo_client) 29 | 30 | # Spies collection 31 | spies_collection = collection_factory.create('spies') 32 | spy_manager = SpyManager(spies_collection) 33 | 34 | # Subscription collection 35 | subscriptions_collection = collection_factory.create('subscriptions') 36 | subscriptions_manager = SubscriptionsManager(subscriptions_collection) 37 | 38 | # Images cache collections 39 | images_cache_collection = collection_factory.create('images_cache') 40 | image_cache_manager = UserImageCacheManager(images_cache_collection) 41 | image_cache_handler = ImageCacheHandler(image_cache_manager) 42 | 43 | site = ImageSite() 44 | publisher = Publisher(API_TOKEN) 45 | flood = Flooder(site, publisher, image_cache_handler) 46 | 47 | 48 | # Time that last advice was posted on group, to avoid spam 49 | last_advice = None 50 | 51 | 52 | # ##### Helper functios 53 | 54 | def isGroup(message): 55 | return message.chat.type in ["group", "supergroup"] 56 | 57 | 58 | def destiny(message): 59 | return message.chat.id 60 | 61 | 62 | def bot_answer(mensagem, answer, parse_mode='Markdown'): 63 | chat_id = destiny(mensagem) 64 | bot.send_message(chat_id, answer, parse_mode) 65 | 66 | 67 | def nome(message): 68 | name = message.from_user.first_name 69 | if not name: 70 | return message.from_user.username 71 | return name 72 | 73 | 74 | def anti_spam_on_group(message): 75 | global last_advice 76 | if isGroup(message): 77 | if not last_advice or datetime.now() - last_advice > timedelta(minutes=15): 78 | bot_answer(message, BOT_PRIVADO) 79 | last_advice = datetime.now() 80 | return True 81 | else: 82 | return False 83 | 84 | 85 | def get_spy(spy_username): 86 | spy = spy_manager.get(spy_username) 87 | if spy.exists(): 88 | return spy 89 | 90 | return None 91 | 92 | 93 | def create_subscriber_from_group(spy_user, group_name): 94 | return { 95 | 'spy': spy_user.username, 96 | 'group': group_name, 97 | 'chat_id': spy_user.chat_id 98 | } 99 | 100 | 101 | def isAllowed(message, spy_user): 102 | if not spy_user: 103 | bot_answer(message, REGISTER_FIRST) 104 | return False 105 | return True 106 | 107 | 108 | def isAdmin(message): 109 | user_id = message.from_user.id 110 | return int(user_id) == int(ADMIN_ID) 111 | 112 | 113 | def question(message, question_message, callback): 114 | if anti_spam_on_group(message): 115 | return 116 | 117 | username = message.from_user.username 118 | spy_user = get_spy(username) 119 | if not isAllowed(message, spy_user): 120 | return 121 | 122 | markup = types.ForceReply(selective=True) 123 | replied_message = bot.send_message( 124 | message.chat.id, question_message, reply_markup=markup 125 | ) 126 | 127 | bot.register_next_step_handler(replied_message, callback) 128 | 129 | # ##### Telegram user functions 130 | 131 | 132 | @bot.message_handler(commands=['whoami']) 133 | def send_whoami(message): 134 | if anti_spam_on_group(message): 135 | return 136 | bot_answer(message, WHOAMI.format(message.from_user)) 137 | 138 | 139 | @bot.message_handler(commands=['register']) 140 | def register_spy(message): 141 | if anti_spam_on_group(message): 142 | return 143 | 144 | username = message.from_user.username 145 | chat_id = message.chat.id 146 | try: 147 | spy_manager.add(username, chat_id) 148 | bot_answer(message, REGISTERED) 149 | except AlreadyExistsOnDatabaseException: 150 | bot_answer(message, ALREADY_REGISTER.format(username)) 151 | 152 | 153 | @bot.message_handler(commands=['unregister']) 154 | def unregister_spy(message): 155 | if anti_spam_on_group(message): 156 | return 157 | 158 | username = message.from_user.username 159 | spy_manager.remove(username) 160 | 161 | bot_answer(message, UNREGISTERED) 162 | 163 | 164 | # ##### Group functions 165 | 166 | @bot.message_handler(commands=['addgroup']) 167 | def add_group(message): 168 | def add_user_to_group(replied_message): 169 | group_name = replied_message.text.lower() 170 | spy_user = get_spy(replied_message.from_user.username) 171 | 172 | if not group_name: 173 | bot_answer(message, 'You forgot the group name!') 174 | return 175 | 176 | try: 177 | spy_user.add_group(group_name=group_name) 178 | bot_answer(replied_message, NEW_GROUP_ADDED.format(group_name)) 179 | except AlreadyExistsOnDatabaseException: 180 | bot_answer( 181 | replied_message, 'Group {} already exists!'.format(group_name) 182 | ) 183 | 184 | question_message = "What's the group name?" 185 | callback = add_user_to_group 186 | 187 | question(message, question_message, callback) 188 | 189 | 190 | @bot.message_handler(commands=['rmgroup']) 191 | def remove_group(message): 192 | def _remove_group(replied_message): 193 | group_name = replied_message.text.lower() 194 | spy_user = get_spy(replied_message.from_user.username) 195 | 196 | # Must remove all members first, to unsubscriber them 197 | members_username = spy_user.members_from_group(group_name) 198 | 199 | for member_username in members_username: 200 | remove_user_from_group(spy_user, member_username, group_name) 201 | 202 | spy_user.remove_group(group_name=group_name) 203 | 204 | bot_answer(message, GROUP_REMOVED.format(group_name)) 205 | 206 | question_message = "What's the group name?" 207 | callback = _remove_group 208 | 209 | question(message, question_message, callback) 210 | 211 | 212 | @bot.message_handler(commands=['groups']) 213 | def list_groups(message): 214 | if anti_spam_on_group(message): 215 | return 216 | 217 | username = message.from_user.username 218 | spy_user = get_spy(username) 219 | if not isAllowed(message, spy_user): 220 | return 221 | 222 | if len(spy_user.groups_names) == 0: 223 | bot_answer(message, 'There is no group to spy yet!') 224 | return 225 | 226 | bot_answer(message, 'You are spying these groups:') 227 | for group in spy_user.groups_names: 228 | bot_answer(message, group) 229 | 230 | 231 | @bot.message_handler(commands=['groupusers']) 232 | def list_group_members(message): 233 | def members_from_group(replied_message): 234 | group_name = replied_message.text.lower() 235 | spy_user = get_spy(replied_message.from_user.username) 236 | 237 | if not group_name: 238 | bot_answer(message, 'You forgot the group name!') 239 | return 240 | 241 | members_username = spy_user.members_from_group(group_name=group_name) 242 | bot_answer(message, 'Users from {} group:'.format(group_name)) 243 | for member in members_username: 244 | bot_answer(message, member) 245 | 246 | question_message = "What's the group name?" 247 | callback = members_from_group 248 | 249 | question(message, question_message, callback) 250 | 251 | 252 | @bot.message_handler(commands=['updategroup']) 253 | def list_groups_to_update_images(message): 254 | if anti_spam_on_group(message): 255 | return 256 | 257 | username = message.from_user.username 258 | spy_user = get_spy(username) 259 | if not isAllowed(message, spy_user): 260 | return 261 | 262 | markup = types.ReplyKeyboardMarkup(row_width=4, one_time_keyboard=True) 263 | 264 | groups_names = [] 265 | 266 | if len(spy_user.groups_names) == 0: 267 | bot_answer(message, 'There is no group created yet!') 268 | return 269 | 270 | for group in spy_user.groups_names: 271 | groups_names.append(types.KeyboardButton('${}'.format(group))) 272 | 273 | markup.add(*groups_names) 274 | chat_id = destiny(message) 275 | bot.send_message(chat_id, REGISTERED_GROUPS, reply_markup=markup) 276 | 277 | 278 | @bot.message_handler(regexp="^\$.*") 279 | def update_images_from_group(message): 280 | if anti_spam_on_group(message): 281 | return 282 | 283 | username = message.from_user.username 284 | spy_user = get_spy(username) 285 | if not isAllowed(message, spy_user): 286 | return 287 | 288 | who = 'spy' 289 | if isAdmin(message): 290 | who = 'boss' 291 | 292 | group_name = message.text.split('$')[1] 293 | 294 | bot_answer( 295 | message, 'Wait a minute {}... this can take some time.'.format(who) 296 | ) 297 | 298 | subscriptions = subscriptions_manager.filterByGroup(spy_user, group_name) 299 | flood.flood_to(subscriptions) 300 | 301 | bot_answer(message, 'Everything was sent {}!'.format(who)) 302 | 303 | 304 | # ##### Users functions 305 | group_name = '' 306 | 307 | 308 | @bot.message_handler(commands=['adduser']) 309 | def add_user(message): 310 | def add_users_to_group(replied_message): 311 | members_usernames = replied_message.text.split(',') 312 | spy_user = get_spy(replied_message.from_user.username) 313 | 314 | spy_user.add_members_to_group( 315 | members_username=members_usernames, group_name=group_name 316 | ) 317 | 318 | subscriber = create_subscriber_from_group(spy_user, group_name) 319 | 320 | for member in members_usernames: 321 | try: 322 | subscriptions_manager.add(member) 323 | except AlreadyExistsOnDatabaseException: 324 | continue 325 | 326 | for member in members_usernames: 327 | s = subscriptions_manager.get(member) 328 | s.add_subscribers(subscriber) 329 | 330 | if members_usernames: 331 | bot_answer( 332 | message, 333 | NEW_USER_ADDED_TO_GROUP.format( 334 | ' @'.join(members_usernames), 335 | group_name 336 | ) 337 | ) 338 | 339 | def get_group_name(replied_message): 340 | global group_name 341 | group_name = replied_message.text.lower() 342 | 343 | question_message = "Who do you wanna spy on?" 344 | callback = add_users_to_group 345 | 346 | question(message, question_message, callback) 347 | 348 | question_message = "What's the GROUP name?" 349 | callback = get_group_name 350 | 351 | question(message, question_message, callback) 352 | 353 | 354 | @bot.message_handler(commands=['rmuser']) 355 | def remove_user(message): 356 | def _remove_user(replied_message): 357 | member_username = replied_message.text.split(',') 358 | spy_user = get_spy(replied_message.from_user.username) 359 | 360 | remove_user_from_group(spy_user, member_username, group_name) 361 | 362 | bot_answer( 363 | message, 364 | USER_REMOVED_FROM_GROUP.format(member_username, group_name) 365 | ) 366 | 367 | def get_group_name(replied_message): 368 | global group_name 369 | group_name = replied_message.text.lower() 370 | 371 | question_message = "Who do you wanna remove?" 372 | callback = _remove_user 373 | 374 | question(message, question_message, callback) 375 | 376 | question_message = "What's the GROUP name?" 377 | callback = get_group_name 378 | 379 | question(message, question_message, callback) 380 | 381 | 382 | def remove_user_from_group(spy_user, member_username, group_name): 383 | spy_user.remove_member_from_group( 384 | member_username=member_username, group_name=group_name 385 | ) 386 | 387 | subscriber = create_subscriber_from_group(spy_user, group_name) 388 | 389 | s = subscriptions_manager.get(member_username) 390 | s.remove_subscriber(subscriber) 391 | 392 | # Must remove the subscription, to do not get his images any longer 393 | if not s.subscribers: 394 | subscriptions_manager.remove(member_username) 395 | 396 | 397 | # ##### Send functions 398 | 399 | @bot.message_handler(commands=['start']) 400 | def send_welcome(message): 401 | if anti_spam_on_group(message): 402 | return 403 | bot_answer(message, ABOUT.format(nome(message))) 404 | 405 | username = message.from_user.username 406 | spy_user = get_spy(username) 407 | 408 | if spy_user: 409 | bot_answer(message, ALREADY_REGISTER.format(username)) 410 | 411 | 412 | @bot.message_handler(commands=['help', 'ajuda']) 413 | def send_help(message): 414 | if anti_spam_on_group(message): 415 | return 416 | bot_answer(message, HELP) 417 | 418 | 419 | @bot.message_handler(commands=['link', 'links']) 420 | def send_link(message): 421 | if anti_spam_on_group(message): 422 | return 423 | bot_answer(message, LINKS) 424 | 425 | 426 | @bot.message_handler(commands=['updateall']) 427 | def update_all_images(message): 428 | if anti_spam_on_group(message): 429 | return 430 | 431 | if not isAdmin(message): 432 | bot_answer(message, 'You are not the big boss!') 433 | return 434 | 435 | bot_answer(message, 'Wait a minute boss... this can take some time.') 436 | 437 | subscriptions = subscriptions_manager.all() 438 | flood.flood_to(subscriptions) 439 | 440 | bot_answer(message, 'Everything was sent boss!') 441 | 442 | 443 | @bot.message_handler(regexp="^\@.*") 444 | def send_user_photos(message): 445 | if anti_spam_on_group(message): 446 | return 447 | 448 | username = message.from_user.username 449 | spy_user = get_spy(username) 450 | if not isAllowed(message, spy_user): 451 | return 452 | 453 | username = message.text.split('@')[1] 454 | 455 | image_site = ImageSite() 456 | user = image_site.get_user(username) 457 | 458 | for image in user.images: 459 | bot_answer(message, image) 460 | 461 | 462 | _logger = telebot.logger 463 | telebot.logger.setLevel(logging.DEBUG) 464 | 465 | bot.polling(none_stop=True) 466 | --------------------------------------------------------------------------------