pyvko 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyvko-0.0.1/PKG-INFO +21 -0
- pyvko-0.0.1/README.md +0 -0
- pyvko-0.0.1/pyproject.toml +48 -0
- pyvko-0.0.1/setup.cfg +4 -0
- pyvko-0.0.1/setup.py +16 -0
- pyvko-0.0.1/src/pyvko/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/api_based.py +57 -0
- pyvko-0.0.1/src/pyvko/aspects/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/aspects/albums.py +139 -0
- pyvko-0.0.1/src/pyvko/aspects/comments.py +143 -0
- pyvko-0.0.1/src/pyvko/aspects/events.py +216 -0
- pyvko-0.0.1/src/pyvko/aspects/groups.py +95 -0
- pyvko-0.0.1/src/pyvko/aspects/likes.py +64 -0
- pyvko-0.0.1/src/pyvko/aspects/posts.py +230 -0
- pyvko-0.0.1/src/pyvko/aspects/reposts.py +45 -0
- pyvko-0.0.1/src/pyvko/aspects/utils.py +26 -0
- pyvko-0.0.1/src/pyvko/attachment/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/attachment/attachment.py +21 -0
- pyvko-0.0.1/src/pyvko/attachment/attachment_parser.py +77 -0
- pyvko-0.0.1/src/pyvko/attachment/photo.py +76 -0
- pyvko-0.0.1/src/pyvko/config/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/config/config.py +20 -0
- pyvko-0.0.1/src/pyvko/entities/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/entities/user.py +71 -0
- pyvko-0.0.1/src/pyvko/pyvko_main.py +108 -0
- pyvko-0.0.1/src/pyvko/pyvko_runner.py +164 -0
- pyvko-0.0.1/src/pyvko/shared/__init__.py +0 -0
- pyvko-0.0.1/src/pyvko/shared/photos_uploader.py +146 -0
- pyvko-0.0.1/src/pyvko/shared/utils.py +74 -0
- pyvko-0.0.1/src/pyvko.egg-info/PKG-INFO +21 -0
- pyvko-0.0.1/src/pyvko.egg-info/SOURCES.txt +32 -0
- pyvko-0.0.1/src/pyvko.egg-info/dependency_links.txt +1 -0
- pyvko-0.0.1/src/pyvko.egg-info/requires.txt +9 -0
- pyvko-0.0.1/src/pyvko.egg-info/top_level.txt +1 -0
pyvko-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyvko
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: VK API typed wrapper library
|
|
5
|
+
Home-page:
|
|
6
|
+
Author: Igor Djachenko
|
|
7
|
+
Author-email: i.s.djachenko@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/djachenko/pyvko
|
|
10
|
+
Project-URL: Repository, https://github.com/djachenko/pyvko
|
|
11
|
+
Project-URL: Issues, https://github.com/djachenko/pyvko/issues
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pytest; extra == "test"
|
|
16
|
+
Requires-Dist: ruff; extra == "test"
|
|
17
|
+
Requires-Dist: mypy; extra == "test"
|
|
18
|
+
Provides-Extra: release
|
|
19
|
+
Requires-Dist: build; extra == "release"
|
|
20
|
+
Requires-Dist: python-semantic-release; extra == "release"
|
|
21
|
+
Dynamic: author-email
|
pyvko-0.0.1/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyvko"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "VK API typed wrapper library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Igor Djachenko" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
test = [
|
|
16
|
+
"pytest",
|
|
17
|
+
"ruff",
|
|
18
|
+
"mypy",
|
|
19
|
+
]
|
|
20
|
+
release = [
|
|
21
|
+
"build",
|
|
22
|
+
"python-semantic-release",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/djachenko/pyvko"
|
|
27
|
+
Repository = "https://github.com/djachenko/pyvko"
|
|
28
|
+
Issues = "https://github.com/djachenko/pyvko/issues"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["src"]
|
|
32
|
+
|
|
33
|
+
[[tool.mypy.overrides]]
|
|
34
|
+
module = "vk_api.*"
|
|
35
|
+
ignore_missing_imports = true
|
|
36
|
+
|
|
37
|
+
[[tool.mypy.overrides]]
|
|
38
|
+
module = "pyvko.pyvko_runner"
|
|
39
|
+
ignore_errors = true
|
|
40
|
+
|
|
41
|
+
[tool.semantic_release]
|
|
42
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
43
|
+
branch = "master"
|
|
44
|
+
build_command = "pip install build && python -m build"
|
|
45
|
+
changelog_file = "CHANGELOG.md"
|
|
46
|
+
commit_message = "chore(release): v{version} [no ci]"
|
|
47
|
+
major_on_zero = false
|
|
48
|
+
allow_zero_version = true
|
pyvko-0.0.1/setup.cfg
ADDED
pyvko-0.0.1/setup.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='pyvko',
|
|
5
|
+
version='0.1.11',
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
install_requires=[
|
|
8
|
+
'vk_api',
|
|
9
|
+
'requests',
|
|
10
|
+
],
|
|
11
|
+
url='',
|
|
12
|
+
license='MIT',
|
|
13
|
+
author='Harry Djachenko',
|
|
14
|
+
author_email='i.s.djachenko@gmail.com',
|
|
15
|
+
description=''
|
|
16
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
from vk_api import VkApi
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RequestRoot:
|
|
8
|
+
def get_request(self, parameters: Dict | None = None):
|
|
9
|
+
return parameters or {}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ApiMixin(RequestRoot, ABC):
|
|
13
|
+
@property
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def new_api(self) -> VkApi:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def api(self) -> Any:
|
|
20
|
+
assert False, f"{type(self).__name__} still uses unmigrated api — switch to new_api"
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_request(self, parameters: Dict | None = None) -> Dict:
|
|
24
|
+
return super().get_request(parameters)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ApiBased(RequestRoot):
|
|
28
|
+
__VERSION = 5.199
|
|
29
|
+
|
|
30
|
+
def __init__(self, api: Any) -> None:
|
|
31
|
+
super().__init__()
|
|
32
|
+
|
|
33
|
+
self.__api = api
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def new_api(self) -> Any:
|
|
37
|
+
return self.__api
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def api(self) -> Any:
|
|
41
|
+
assert False, f"{type(self).__name__} still uses unmigrated api — switch to new_api"
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def __get_default_object():
|
|
45
|
+
return {
|
|
46
|
+
# "v": ApiBased.__VERSION,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def get_request(self, parameters: Dict | None = None) -> Dict:
|
|
50
|
+
if parameters is None:
|
|
51
|
+
parameters = {}
|
|
52
|
+
|
|
53
|
+
assert "v" not in parameters
|
|
54
|
+
|
|
55
|
+
request = ApiBased.__get_default_object() | parameters | super().get_request()
|
|
56
|
+
|
|
57
|
+
return request
|
|
File without changes
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from pyvko.api_based import ApiMixin, ApiBased
|
|
7
|
+
from pyvko.attachment.attachment import Attachment
|
|
8
|
+
from pyvko.attachment.photo import Photo
|
|
9
|
+
from pyvko.shared.photos_uploader import AlbumPhotoUploader
|
|
10
|
+
from pyvko.shared.utils import get_all
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Album(ApiBased, Attachment):
|
|
14
|
+
def __init__(self, api: Any, api_object: dict) -> None:
|
|
15
|
+
super().__init__(api)
|
|
16
|
+
|
|
17
|
+
self.__name = api_object["title"]
|
|
18
|
+
self.__id = api_object["id"]
|
|
19
|
+
self.__owner_id = api_object["owner_id"]
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def name(self) -> str:
|
|
23
|
+
return self.__name
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def id(self) -> int:
|
|
27
|
+
return self.__id
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def url(self) -> str:
|
|
31
|
+
return f"https://vk.com/album{self.owner_id}_{self.id}"
|
|
32
|
+
|
|
33
|
+
def get_photos(self) -> List[Photo]:
|
|
34
|
+
parameters = self.get_request()
|
|
35
|
+
|
|
36
|
+
photos_descriptions = get_all(parameters, self.api.photos.get)
|
|
37
|
+
|
|
38
|
+
photos = [Photo.from_photo_object(self.api, photo_object) for photo_object in photos_descriptions]
|
|
39
|
+
|
|
40
|
+
return photos
|
|
41
|
+
|
|
42
|
+
def get_request(self, parameters: Dict | None = None) -> dict:
|
|
43
|
+
if parameters is None:
|
|
44
|
+
parameters = {}
|
|
45
|
+
|
|
46
|
+
parameters |= {
|
|
47
|
+
"owner_id": self.__owner_id,
|
|
48
|
+
"album_id": self.__id
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return super().get_request(parameters)
|
|
52
|
+
|
|
53
|
+
def set_cover(self, cover: Photo):
|
|
54
|
+
request = self.get_request({
|
|
55
|
+
"photo_id": cover.id
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
self.api.photos.makeCover(**request)
|
|
59
|
+
|
|
60
|
+
def add_photo(self, path: Path) -> Photo:
|
|
61
|
+
uploader = AlbumPhotoUploader(self.api, self.id, -self.__owner_id)
|
|
62
|
+
|
|
63
|
+
return uploader.upload(path)
|
|
64
|
+
|
|
65
|
+
# region Attachment
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def type(self) -> str:
|
|
69
|
+
return "album"
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def owner_id(self) -> int:
|
|
73
|
+
return self.__owner_id
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def media_id(self) -> int:
|
|
77
|
+
return self.id
|
|
78
|
+
|
|
79
|
+
# endregion Attachment
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Albums(ApiMixin, ABC):
|
|
83
|
+
@property
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def id(self) -> int:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def __get_albums(self, parameters: Dict | None = None) -> List[Album]:
|
|
89
|
+
request = self.__get_owned_request(parameters)
|
|
90
|
+
|
|
91
|
+
result = self.api.photos.getAlbums(**request)
|
|
92
|
+
|
|
93
|
+
albums = [Album(self.api, album_object) for album_object in result["items"]]
|
|
94
|
+
|
|
95
|
+
return albums
|
|
96
|
+
|
|
97
|
+
def get_all_albums(self) -> List[Album]:
|
|
98
|
+
return self.__get_albums()
|
|
99
|
+
|
|
100
|
+
def get_album_by_id(self, album_id: int) -> Album | None:
|
|
101
|
+
albums_list = self.__get_albums({
|
|
102
|
+
"album_ids": [album_id]
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
if len(albums_list) == 1:
|
|
106
|
+
return albums_list[0]
|
|
107
|
+
elif len(albums_list) == 0:
|
|
108
|
+
return None
|
|
109
|
+
else:
|
|
110
|
+
assert False
|
|
111
|
+
|
|
112
|
+
def create_album(self, name: str) -> Album:
|
|
113
|
+
parameters = {
|
|
114
|
+
"title": name,
|
|
115
|
+
"group_id": abs(self.id),
|
|
116
|
+
"upload_by_admins_only": 1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
parameters = self.get_request(parameters)
|
|
120
|
+
|
|
121
|
+
response = self.api.photos.createAlbum(**parameters)
|
|
122
|
+
|
|
123
|
+
created_album = Album(self.api, response)
|
|
124
|
+
|
|
125
|
+
return created_album
|
|
126
|
+
|
|
127
|
+
def __get_owned_request(self, parameters: Dict | None = None) -> dict:
|
|
128
|
+
if parameters is None:
|
|
129
|
+
parameters = {}
|
|
130
|
+
else:
|
|
131
|
+
parameters = parameters.copy()
|
|
132
|
+
|
|
133
|
+
assert "owner_id" not in parameters
|
|
134
|
+
|
|
135
|
+
parameters.update({
|
|
136
|
+
"owner_id": self.id
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return self.get_request(parameters)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
|
|
6
|
+
from pyvko.api_based import ApiMixin, ApiBased
|
|
7
|
+
from pyvko.attachment.attachment import Attachment
|
|
8
|
+
from pyvko.attachment.attachment_parser import AttachmentParser
|
|
9
|
+
from pyvko.aspects.likes import Likes
|
|
10
|
+
from pyvko.shared.utils import get_all
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class CommentModel:
|
|
15
|
+
text: str
|
|
16
|
+
from_group: int = 0
|
|
17
|
+
|
|
18
|
+
attachments: List[Attachment] | None = None
|
|
19
|
+
|
|
20
|
+
def to_request(self):
|
|
21
|
+
request = {}
|
|
22
|
+
|
|
23
|
+
if self.text:
|
|
24
|
+
request["message"] = self.text
|
|
25
|
+
|
|
26
|
+
if self.from_group != 0:
|
|
27
|
+
request["from_group"] = self.from_group
|
|
28
|
+
|
|
29
|
+
if self.attachments:
|
|
30
|
+
request["attachments"] = ",".join([a.to_attach() for a in self.attachments])
|
|
31
|
+
|
|
32
|
+
return request
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Comment(ApiBased, Likes):
|
|
36
|
+
@property
|
|
37
|
+
def item_id(self) -> int:
|
|
38
|
+
return self.id
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def like_object_type(self) -> str:
|
|
42
|
+
return "comment"
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def owner_id(self) -> int:
|
|
46
|
+
return self.__owner_id
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def id(self):
|
|
50
|
+
return self.__id
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def text(self) -> str:
|
|
54
|
+
return self.__text
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def date(self) -> datetime:
|
|
58
|
+
return self.__date
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
api: Any,
|
|
63
|
+
comment_id: int,
|
|
64
|
+
owner_id: int,
|
|
65
|
+
date: datetime,
|
|
66
|
+
text: str,
|
|
67
|
+
attachments: List[Attachment] | None
|
|
68
|
+
) -> None:
|
|
69
|
+
super().__init__(api)
|
|
70
|
+
|
|
71
|
+
self.__id = comment_id
|
|
72
|
+
self.__owner_id = owner_id
|
|
73
|
+
self.__date = date
|
|
74
|
+
self.__text = text
|
|
75
|
+
self.__attachments = attachments
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_api_object(cls, api_object: Dict, api: Any) -> 'Comment':
|
|
79
|
+
if "attachments" in api_object:
|
|
80
|
+
parser = AttachmentParser.shared()
|
|
81
|
+
|
|
82
|
+
attachments = [parser.parse_object(o, api) for o in api_object["attachments"]]
|
|
83
|
+
else:
|
|
84
|
+
attachments = None
|
|
85
|
+
|
|
86
|
+
return Comment(
|
|
87
|
+
api=api,
|
|
88
|
+
comment_id=api_object["id"],
|
|
89
|
+
owner_id=api_object["owner_id"],
|
|
90
|
+
date=datetime.fromtimestamp(api_object["date"]),
|
|
91
|
+
text=api_object["text"],
|
|
92
|
+
attachments=attachments
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Comments(ApiMixin, ABC):
|
|
97
|
+
@property
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def post_id(self) -> int:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
@abstractmethod
|
|
104
|
+
def owner_id(self) -> int:
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
def add_comment(self, comment: CommentModel) -> Comment:
|
|
108
|
+
add_comment_request = self.get_request(comment.to_request() | {
|
|
109
|
+
"post_id": self.post_id,
|
|
110
|
+
"owner_id": self.owner_id,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
add_comment_response = self.api.wall.createComment(**add_comment_request)
|
|
114
|
+
|
|
115
|
+
comment_id = add_comment_response["comment_id"]
|
|
116
|
+
|
|
117
|
+
return self.get_comment(comment_id)
|
|
118
|
+
|
|
119
|
+
def get_comments(self) -> List[Comment]:
|
|
120
|
+
request = self.get_request({
|
|
121
|
+
"post_id": self.post_id,
|
|
122
|
+
"owner_id": self.owner_id,
|
|
123
|
+
# "count": 10,
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
comments_objects = list(get_all(request, self.api.wall.getComments, count_key="current_level_count"))
|
|
127
|
+
|
|
128
|
+
comments = [Comment.from_api_object(comments_object, self.api) for comments_object in comments_objects]
|
|
129
|
+
|
|
130
|
+
return comments
|
|
131
|
+
|
|
132
|
+
def get_comment(self, comment_id: int) -> Comment:
|
|
133
|
+
request = self.get_request({
|
|
134
|
+
"comment_id": comment_id,
|
|
135
|
+
"owner_id": self.owner_id,
|
|
136
|
+
# "count": 10,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
comment_object = self.api.wall.getComment(**request)
|
|
140
|
+
|
|
141
|
+
comment = Comment.from_api_object(comment_object["items"][0], self.api)
|
|
142
|
+
|
|
143
|
+
return comment
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from functools import cache
|
|
5
|
+
from typing import Optional, List, Dict, Tuple, Any
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
from vk_api.exceptions import ApiError
|
|
9
|
+
|
|
10
|
+
from pyvko.api_based import ApiMixin, ApiBased
|
|
11
|
+
from pyvko.aspects.albums import Albums
|
|
12
|
+
from pyvko.aspects.posts import Posts
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Event(ApiBased, Posts, Albums):
|
|
16
|
+
class Category(Enum):
|
|
17
|
+
CIRCUS = 1120
|
|
18
|
+
CONCERT = 1107
|
|
19
|
+
PARTY = 1103
|
|
20
|
+
EXHIBITION = 1104
|
|
21
|
+
|
|
22
|
+
class Section(Enum):
|
|
23
|
+
WALL = "wall"
|
|
24
|
+
DISCUSSION = "topics"
|
|
25
|
+
PHOTOS = "photos"
|
|
26
|
+
VIDEOS = "video"
|
|
27
|
+
MUSIC = "audio"
|
|
28
|
+
FILES = "docs"
|
|
29
|
+
WIKI = "wiki"
|
|
30
|
+
ARTICLES = "articles"
|
|
31
|
+
NARRATIVES = "narratives"
|
|
32
|
+
MESSAGES = "messages"
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
@cache
|
|
36
|
+
def __section_index_mapping() -> List[Tuple['Event.Section', int]]:
|
|
37
|
+
return [
|
|
38
|
+
(Event.Section.PHOTOS, 1),
|
|
39
|
+
(Event.Section.VIDEOS, 4),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_index(cls, index: int) -> Optional['Event.Section']:
|
|
44
|
+
|
|
45
|
+
for section, section_index in Event.Section.__section_index_mapping():
|
|
46
|
+
if index == section_index:
|
|
47
|
+
return section
|
|
48
|
+
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
def to_index(self) -> Optional[int]:
|
|
52
|
+
for section, section_index in Event.Section.__section_index_mapping():
|
|
53
|
+
if section == self:
|
|
54
|
+
return section_index
|
|
55
|
+
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
class SectionState(Enum):
|
|
59
|
+
NOT_AVAILABLE = -1
|
|
60
|
+
DISABLED = 0
|
|
61
|
+
OPEN = 1
|
|
62
|
+
ENABLED = 1
|
|
63
|
+
LIMITED = 2
|
|
64
|
+
RESTRICTED = 3
|
|
65
|
+
|
|
66
|
+
def __init__(self, api: Any, event_object: Dict, settings_object: Dict | None) -> None:
|
|
67
|
+
super().__init__(api)
|
|
68
|
+
|
|
69
|
+
self.__id: int = -event_object["id"]
|
|
70
|
+
self.name = event_object["name"]
|
|
71
|
+
self.start_date: datetime = datetime.fromtimestamp(event_object["start_date"])
|
|
72
|
+
self.end_date: datetime | None
|
|
73
|
+
|
|
74
|
+
if "finish_date" in event_object:
|
|
75
|
+
self.end_date = datetime.fromtimestamp(event_object["finish_date"])
|
|
76
|
+
else:
|
|
77
|
+
self.end_date = None
|
|
78
|
+
|
|
79
|
+
self.is_closed = bool(event_object["is_closed"])
|
|
80
|
+
self.url = event_object["screen_name"]
|
|
81
|
+
|
|
82
|
+
self.event_category: Event.Category | None = None
|
|
83
|
+
self.__sections: Dict[Event.Section, Event.SectionState] = {}
|
|
84
|
+
self.main_section: Event.Section | None = None
|
|
85
|
+
self.secondary_section: Event.Section | None = None
|
|
86
|
+
self.organiser: int | None = None
|
|
87
|
+
|
|
88
|
+
if settings_object is not None:
|
|
89
|
+
category_value = settings_object["public_category"]
|
|
90
|
+
|
|
91
|
+
if category_value != 0:
|
|
92
|
+
self.event_category = Event.Category(category_value)
|
|
93
|
+
|
|
94
|
+
self.__sections = {
|
|
95
|
+
s: Event.SectionState(settings_object[s.value]) for s in Event.Section
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
self.main_section = Event.Section.from_index(settings_object["main_section"])
|
|
99
|
+
self.secondary_section = Event.Section.from_index(settings_object["secondary_section"])
|
|
100
|
+
self.organiser = settings_object.get("event_object_id")
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def id(self) -> int:
|
|
104
|
+
return self.__id
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def browser_url(self):
|
|
108
|
+
return f"http://vk.com/event{abs(self.id)}"
|
|
109
|
+
|
|
110
|
+
def section_state(self, section: Section) -> Optional[SectionState]:
|
|
111
|
+
return self.__sections.get(section)
|
|
112
|
+
|
|
113
|
+
def set_section_state(self, section: Section, new_state: SectionState):
|
|
114
|
+
assert new_state != Event.SectionState.NOT_AVAILABLE
|
|
115
|
+
assert self.__sections[section] != Event.SectionState.NOT_AVAILABLE
|
|
116
|
+
|
|
117
|
+
self.__sections[section] = new_state
|
|
118
|
+
|
|
119
|
+
def save(self):
|
|
120
|
+
params = {
|
|
121
|
+
"group_id": abs(self.__id),
|
|
122
|
+
"access": int(self.is_closed),
|
|
123
|
+
"event_start_date": int(self.start_date.timestamp()),
|
|
124
|
+
"title": self.name,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for section, state in self.__sections.items():
|
|
128
|
+
if state == Event.SectionState.NOT_AVAILABLE:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
params[section.value] = state.value
|
|
132
|
+
|
|
133
|
+
if self.end_date is not None:
|
|
134
|
+
params["event_finish_date"] = int(self.end_date.timestamp())
|
|
135
|
+
|
|
136
|
+
if self.event_category is not None:
|
|
137
|
+
params["public_category"] = self.event_category.value
|
|
138
|
+
|
|
139
|
+
if self.main_section is not None:
|
|
140
|
+
params["main_section"] = self.main_section.to_index()
|
|
141
|
+
|
|
142
|
+
if self.secondary_section is not None:
|
|
143
|
+
params["secondary_section"] = self.secondary_section.to_index()
|
|
144
|
+
|
|
145
|
+
if self.organiser is not None:
|
|
146
|
+
params["event_group_id"] = abs(self.organiser)
|
|
147
|
+
|
|
148
|
+
request = self.get_request(params)
|
|
149
|
+
|
|
150
|
+
self.new_api.groups.edit(**request)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class Events(ApiMixin, ABC):
|
|
154
|
+
def create_event(self, title: str) -> Event:
|
|
155
|
+
request = self.get_request({
|
|
156
|
+
"title": title,
|
|
157
|
+
"type": "event",
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
response = self.new_api.groups.create(**request)
|
|
161
|
+
|
|
162
|
+
event = self.get_event(response["id"])
|
|
163
|
+
|
|
164
|
+
assert event is not None
|
|
165
|
+
|
|
166
|
+
return event
|
|
167
|
+
|
|
168
|
+
def get_event(self, url: str | int) -> Event | None:
|
|
169
|
+
url = str(url)
|
|
170
|
+
|
|
171
|
+
parse_result = urlparse(url)
|
|
172
|
+
|
|
173
|
+
qualifier = parse_result.path \
|
|
174
|
+
.split("/")[-1] \
|
|
175
|
+
.removeprefix("event") \
|
|
176
|
+
.removeprefix("-")
|
|
177
|
+
|
|
178
|
+
group_request = self.get_request({
|
|
179
|
+
"group_id": qualifier,
|
|
180
|
+
"fields": [
|
|
181
|
+
"start_date",
|
|
182
|
+
"finish_date",
|
|
183
|
+
]
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
event_request = {
|
|
187
|
+
"fields": [
|
|
188
|
+
"start_date",
|
|
189
|
+
"finish_date",
|
|
190
|
+
"main_section",
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
event_request.update(group_request)
|
|
195
|
+
|
|
196
|
+
event_response = self.new_api.groups.getById(**event_request)
|
|
197
|
+
|
|
198
|
+
event_object = event_response["groups"][0]
|
|
199
|
+
|
|
200
|
+
if event_object["type"] not in ["event", ]:
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
group_request["group_id"] = event_object["id"]
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
settings_response = self.new_api.groups.getSettings(**group_request)
|
|
207
|
+
except ApiError as err:
|
|
208
|
+
# 15 — ACCESS_DENIED in VK API
|
|
209
|
+
if err.code not in [3, 15]:
|
|
210
|
+
raise
|
|
211
|
+
|
|
212
|
+
settings_response = None
|
|
213
|
+
|
|
214
|
+
event = Event(self.new_api, event_object=event_object, settings_object=settings_response)
|
|
215
|
+
|
|
216
|
+
return event
|