├── requirements.txt ├── posts └── 25.11.18 14.00 │ ├── message.txt │ └── example.jpg ├── .gitignore ├── config.py ├── README.md ├── main.py └── post.py /requirements.txt: -------------------------------------------------------------------------------- 1 | vk-api==11.6.0 2 | -------------------------------------------------------------------------------- /posts/25.11.18 14.00/message.txt: -------------------------------------------------------------------------------- 1 | Test message -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | vk_config.v2.json 3 | .vscode 4 | -------------------------------------------------------------------------------- /posts/25.11.18 14.00/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MixSP/VK_AutoPoster/HEAD/posts/25.11.18 14.00/example.jpg -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # id вашего аккаунта 2 | USER_ID = '' 3 | 4 | # список с id групп, в которые будем размещать посты, перед id должен быть "-" 5 | # для размещения поста на стену пользователя, вбиваем id пользователя 6 | GROUP_IDS = [''] 7 | 8 | # id приложения, для нормальной работы лучше не менять 9 | APP_ID = 2685278 10 | 11 | # логин, лучше использовать номер телефона 12 | LOGIN = '' 13 | # пароль 14 | PASSWORD = '' 15 | 16 | # разрешения, для нормальной работы лучше не менять 17 | PERMISSIONS = 'friends,photos,messages,wall,offline,docs,groups,stats' 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VK_AutoPoster 2 | Отложенный постинг на стену вк 3 | 4 | ![PyPI - Python Version](https://img.shields.io/badge/python-3.5%2C%203.6%2C%203.7-orange.svg) ![stars](https://img.shields.io/github/stars/MixSP/VK_Autoposter.svg) 5 | 6 | *** 7 | Для одного проекта возникла потребность автопостинга записей на стену группы в вк. 8 | Встроенный в вк функционал отложенного постинга не подходил, т.к. заранее было неизвестно какой именно контент будет прикреплен к посту. 9 | Для этого и был написан этот скрипт 10 | *** 11 | 12 | ## Установка и настройка 13 | 1. Клонируйте репозиторий 14 | ``` 15 | git clone https://github.com/MixSP/VK_AutoPoster 16 | ``` 17 | 2. Установите требуемые зависимости 18 | ``` 19 | pip install -r requirements.txt 20 | ``` 21 | 3. Введите свои данные в файле [config.py](/config.py) 22 | 23 | 4. Сформируйте посты в папке [posts/](/posts/) 24 | 25 | ## Правила оформления постов
26 | Все посты создаются в папке [posts/](/posts/)
27 | Название папки с постом должно соответствовать времни отправки этого поста на стену вк в формате 28 | **\.\.\ \.\**

29 | На данный момент скрипт поддерживает отправку сообщения (.txt), фотографий (.jpeg, .jpg, .png) и гифок (.gif) 30 | 31 | ## Запуск 32 | Запустите из консоли файл [main.py](/main.py) 33 | ``` 34 | python main.py 35 | ``` 36 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from time import sleep 3 | import os 4 | import sys 5 | 6 | import vk_api 7 | 8 | from post import make_posts 9 | from config import * 10 | 11 | 12 | def make_time(): 13 | return datetime.datetime.now().strftime("%H:%M:%S") 14 | 15 | 16 | def two_factor(): 17 | code = input('Enter 2FA code: ') 18 | remember_device = True 19 | return code, remember_device 20 | 21 | 22 | def main(): 23 | vk_session = vk_api.VkApi(LOGIN, PASSWORD, 24 | auth_handler=two_factor, 25 | app_id=APP_ID, 26 | scope=PERMISSIONS, 27 | config_filename='vk_config.v2.json') 28 | vk_session.auth() 29 | 30 | posts_path = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'posts') 31 | print('Posts: ') 32 | posts = make_posts(posts_path) 33 | print('=' * 40) 34 | 35 | for post in posts: 36 | td = ( post.time - datetime.datetime.now() ).total_seconds() 37 | if td < 0: 38 | print(post.time, '- wrong post time') 39 | print('-' * 40) 40 | continue 41 | 42 | else: 43 | while True: 44 | td = ( post.time - datetime.datetime.now() ).total_seconds() 45 | if td < 60: 46 | try: 47 | for GROUP_ID in GROUP_IDS: 48 | print('[{}] Uploading files as attachments...'.format(make_time())) 49 | post.upload_content(vk_session, USER_ID, GROUP_ID) 50 | 51 | print('[{}] Posting to {}...'.format(make_time(), GROUP_ID)) 52 | post.post(vk_session, GROUP_ID) 53 | 54 | print('[{}] Success!'.format(make_time())) 55 | sleep(0.34) 56 | 57 | except Exception as err: 58 | print('[{}] {} - Error {}'.format(make_time(), post.time, err)) 59 | 60 | break 61 | 62 | else: 63 | print('[{}] sleep {} min'.format(make_time(), round((td - 40) / 60, 1))) 64 | sleep(td - 40) 65 | 66 | print('-' * 40) 67 | 68 | 69 | if __name__ == '__main__': 70 | main() 71 | -------------------------------------------------------------------------------- /post.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | import vk_api 5 | 6 | 7 | class ImportContentError(Exception): pass 8 | 9 | 10 | class Post(): 11 | def __init__(self, time, path): 12 | self.time = time 13 | self.__path = path 14 | self.__photos = [] 15 | self.__docs = [] 16 | self.__message = '' 17 | self.__message_file = None 18 | self.__attachments = '' 19 | 20 | @property 21 | def time(self): 22 | return self.__time 23 | 24 | @time.setter 25 | def time(self, time): 26 | assert isinstance(time, datetime.datetime), "Invalid post time" 27 | self.__time = time 28 | 29 | def __len__(self): 30 | return len(self.__photos) + len(self.__docs) 31 | 32 | def __eq__(self, other): 33 | return self.__time == other.__time 34 | 35 | def __lt__(self, other): 36 | return self.__time < other.__time 37 | 38 | def import_content(self): 39 | files = os.listdir(self.__path) 40 | 41 | if files: 42 | for file_ in files: 43 | if file_.endswith('.jpg') or file_.endswith('.png') or file_.endswith('.jpeg'): 44 | self.__photos.append(file_) 45 | elif file_.endswith('.gif'): 46 | self.__docs.append(file_) 47 | elif file_.endswith('.txt') and self.__message_file is None: 48 | self.__message_file = file_ 49 | else: 50 | raise ImportContentError('Folder "{}" is empty'.format(os.path.basename(self.__path))) 51 | 52 | def upload_content(self, vk_session, user_id, group_id): 53 | upload = vk_api.VkUpload(vk_session) 54 | 55 | if self.__photos: 56 | photos_path = [os.path.join(self.__path, photo) for photo in self.__photos] 57 | photos = upload.photo_wall(photos_path, user_id, group_id) 58 | atcms = ['photo' + str(photo['owner_id']) + '_' + str(photo['id']) for photo in photos] 59 | atcms = ','.join(atcms) 60 | self.__attachments = ','.join( (self.__attachments, atcms) ) 61 | 62 | if self.__docs: 63 | for doc in self.__docs: 64 | path = os.path.join(self.__path, doc) 65 | dct = upload.document_wall(path, doc[:-4]) 66 | atcmt = 'doc' + str(dct[0]['owner_id']) + '_' + str(dct[0]['id']) 67 | self.__attachments = ','.join( (self.__attachments, atcmt) ) 68 | 69 | if self.__message_file: 70 | with open(os.path.join(self.__path, self.__message_file), 'rb') as f: 71 | for line in f: 72 | self.__message += line.decode('utf-8') 73 | 74 | def post(self, vk_session, owner_id): 75 | vk = vk_session.get_api() 76 | response = vk.wall.post(owner_id=owner_id, 77 | from_group=1, 78 | message=self.__message, 79 | attachments=self.__attachments) 80 | return response 81 | 82 | 83 | def get_time(folder): 84 | try: 85 | dt = datetime.datetime.strptime(folder, "%d.%m.%y %H.%M") 86 | return dt 87 | 88 | except: 89 | raise ValueError('Can not convert "{}" folder to post time'.format(folder)) 90 | 91 | 92 | def make_posts(posts_path): 93 | posts = [] 94 | for p in os.listdir(posts_path): 95 | if p == '.DS_Store': 96 | continue 97 | 98 | try: 99 | post = Post( get_time(p), os.path.join(posts_path, p) ) 100 | post.import_content() 101 | print( 'post in {} - {} objects'.format(post.time, len(post)) ) 102 | posts.append(post) 103 | except Exception as err: 104 | print('Error ({})'.format(err)) 105 | 106 | return posts 107 | --------------------------------------------------------------------------------