├── 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 |  
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 |
--------------------------------------------------------------------------------