marqetive-lib 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

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.
Files changed (43) hide show
  1. marqetive/__init__.py +58 -59
  2. marqetive/core/__init__.py +1 -1
  3. marqetive/factory.py +380 -0
  4. marqetive/platforms/__init__.py +6 -6
  5. marqetive/platforms/base.py +36 -3
  6. marqetive/platforms/instagram/__init__.py +2 -4
  7. marqetive/platforms/instagram/client.py +8 -4
  8. marqetive/platforms/instagram/exceptions.py +1 -1
  9. marqetive/platforms/instagram/media.py +2 -2
  10. marqetive/platforms/linkedin/__init__.py +2 -4
  11. marqetive/platforms/linkedin/client.py +8 -4
  12. marqetive/platforms/linkedin/exceptions.py +1 -1
  13. marqetive/platforms/linkedin/media.py +4 -4
  14. marqetive/platforms/tiktok/__init__.py +2 -4
  15. marqetive/platforms/tiktok/client.py +324 -104
  16. marqetive/platforms/tiktok/exceptions.py +170 -66
  17. marqetive/platforms/tiktok/media.py +545 -159
  18. marqetive/platforms/twitter/__init__.py +2 -4
  19. marqetive/platforms/twitter/client.py +11 -53
  20. marqetive/platforms/twitter/exceptions.py +1 -1
  21. marqetive/platforms/twitter/media.py +4 -4
  22. marqetive/utils/__init__.py +3 -3
  23. marqetive/utils/file_handlers.py +1 -1
  24. marqetive/utils/oauth.py +2 -2
  25. marqetive/utils/token_validator.py +1 -1
  26. {marqetive_lib-0.1.2.dist-info → marqetive_lib-0.1.4.dist-info}/METADATA +1 -1
  27. marqetive_lib-0.1.4.dist-info/RECORD +35 -0
  28. marqetive/core/account_factory.py +0 -212
  29. marqetive/core/base_manager.py +0 -303
  30. marqetive/core/progress.py +0 -291
  31. marqetive/core/registry.py +0 -257
  32. marqetive/platforms/instagram/factory.py +0 -106
  33. marqetive/platforms/instagram/manager.py +0 -112
  34. marqetive/platforms/linkedin/factory.py +0 -130
  35. marqetive/platforms/linkedin/manager.py +0 -119
  36. marqetive/platforms/tiktok/factory.py +0 -188
  37. marqetive/platforms/tiktok/manager.py +0 -115
  38. marqetive/platforms/twitter/factory.py +0 -151
  39. marqetive/platforms/twitter/manager.py +0 -121
  40. marqetive/platforms/twitter/threads.py +0 -442
  41. marqetive/registry_init.py +0 -66
  42. marqetive_lib-0.1.2.dist-info/RECORD +0 -48
  43. {marqetive_lib-0.1.2.dist-info → marqetive_lib-0.1.4.dist-info}/WHEEL +0 -0
@@ -1,7 +1,5 @@
1
1
  """Twitter/X platform integration."""
2
2
 
3
- from src.marqetive.platforms.twitter.client import TwitterClient
4
- from src.marqetive.platforms.twitter.factory import TwitterAccountFactory
5
- from src.marqetive.platforms.twitter.manager import TwitterPostManager
3
+ from marqetive.platforms.twitter.client import TwitterClient
6
4
 
7
- __all__ = ["TwitterClient", "TwitterAccountFactory", "TwitterPostManager"]
5
+ __all__ = ["TwitterClient"]
@@ -13,8 +13,8 @@ import httpx
13
13
  import tweepy
14
14
  from pydantic import HttpUrl
15
15
 
16
- from src.marqetive.platforms.base import SocialMediaPlatform
17
- from src.marqetive.platforms.exceptions import (
16
+ from marqetive.platforms.base import ProgressCallback, SocialMediaPlatform
17
+ from marqetive.platforms.exceptions import (
18
18
  MediaUploadError,
19
19
  PlatformAuthError,
20
20
  PlatformError,
@@ -22,7 +22,7 @@ from src.marqetive.platforms.exceptions import (
22
22
  RateLimitError,
23
23
  ValidationError,
24
24
  )
25
- from src.marqetive.platforms.models import (
25
+ from marqetive.platforms.models import (
26
26
  AuthCredentials,
27
27
  Comment,
28
28
  CommentStatus,
@@ -33,12 +33,7 @@ from src.marqetive.platforms.models import (
33
33
  PostStatus,
34
34
  PostUpdateRequest,
35
35
  )
36
- from src.marqetive.platforms.twitter.media import TwitterMediaManager
37
- from src.marqetive.platforms.twitter.threads import (
38
- ThreadResult,
39
- TweetData,
40
- TwitterThreadManager,
41
- )
36
+ from marqetive.platforms.twitter.media import TwitterMediaManager
42
37
 
43
38
 
44
39
  class TwitterClient(SocialMediaPlatform):
@@ -72,12 +67,15 @@ class TwitterClient(SocialMediaPlatform):
72
67
  self,
73
68
  credentials: AuthCredentials,
74
69
  timeout: float = 30.0,
70
+ progress_callback: ProgressCallback | None = None,
75
71
  ) -> None:
76
72
  """Initialize Twitter client.
77
73
 
78
74
  Args:
79
75
  credentials: Twitter authentication credentials
80
76
  timeout: Request timeout in seconds
77
+ progress_callback: Optional callback for progress updates during
78
+ long-running operations like media uploads.
81
79
 
82
80
  Raises:
83
81
  PlatformAuthError: If credentials are invalid
@@ -88,15 +86,15 @@ class TwitterClient(SocialMediaPlatform):
88
86
  credentials=credentials,
89
87
  base_url=base_url,
90
88
  timeout=timeout,
89
+ progress_callback=progress_callback,
91
90
  )
92
91
 
93
92
  # Initialize tweepy client
94
93
  self._tweepy_client: tweepy.Client | None = None
95
94
  self._setup_tweepy_client()
96
95
 
97
- # Initialize media and thread managers
96
+ # Initialize media manager
98
97
  self._media_manager: TwitterMediaManager | None = None
99
- self._thread_manager: TwitterThreadManager | None = None
100
98
 
101
99
  def _setup_tweepy_client(self) -> None:
102
100
  """Setup tweepy Client with credentials."""
@@ -107,7 +105,7 @@ class TwitterClient(SocialMediaPlatform):
107
105
  )
108
106
 
109
107
  async def _setup_managers(self) -> None:
110
- """Setup media and thread managers."""
108
+ """Setup media manager."""
111
109
  if not self._tweepy_client:
112
110
  return
113
111
 
@@ -117,15 +115,11 @@ class TwitterClient(SocialMediaPlatform):
117
115
  timeout=self.timeout,
118
116
  )
119
117
 
120
- # Initialize thread manager
121
- self._thread_manager = TwitterThreadManager(self._tweepy_client)
122
-
123
118
  async def _cleanup_managers(self) -> None:
124
- """Cleanup media and thread managers."""
119
+ """Cleanup media manager."""
125
120
  if self._media_manager:
126
121
  await self._media_manager.__aexit__(None, None, None)
127
122
  self._media_manager = None
128
- self._thread_manager = None
129
123
 
130
124
  async def __aenter__(self) -> "TwitterClient":
131
125
  """Async context manager entry."""
@@ -138,42 +132,6 @@ class TwitterClient(SocialMediaPlatform):
138
132
  await self._cleanup_managers()
139
133
  await super().__aexit__(exc_type, exc_val, exc_tb)
140
134
 
141
- # ==================== Thread Methods ====================
142
-
143
- async def create_thread(
144
- self,
145
- tweets: list[TweetData],
146
- *,
147
- auto_number: bool = False,
148
- number_format: str = "{index}/{total}",
149
- ) -> ThreadResult:
150
- """Create a Twitter thread from multiple tweets."""
151
- if not self._thread_manager:
152
- raise RuntimeError("Client must be used as async context manager")
153
-
154
- return await self._thread_manager.create_thread(
155
- tweets,
156
- auto_number=auto_number,
157
- number_format=number_format,
158
- )
159
-
160
- async def delete_thread(
161
- self,
162
- thread_id: str,
163
- tweet_ids: list[str],
164
- *,
165
- continue_on_error: bool = True,
166
- ) -> dict[str, bool]:
167
- """Delete all tweets in a thread."""
168
- if not self._thread_manager:
169
- raise RuntimeError("Client must be used as async context manager")
170
-
171
- return await self._thread_manager.delete_thread(
172
- thread_id,
173
- tweet_ids,
174
- continue_on_error=continue_on_error,
175
- )
176
-
177
135
  # ==================== Authentication Methods ====================
178
136
 
179
137
  async def authenticate(self) -> AuthCredentials:
@@ -9,7 +9,7 @@ This module provides comprehensive error handling for Twitter API errors includi
9
9
 
10
10
  from typing import Any
11
11
 
12
- from src.marqetive.platforms.exceptions import (
12
+ from marqetive.platforms.exceptions import (
13
13
  MediaUploadError,
14
14
  PlatformAuthError,
15
15
  PlatformError,
@@ -18,17 +18,17 @@ from typing import Any, Literal
18
18
 
19
19
  import httpx
20
20
 
21
- from src.marqetive.platforms.exceptions import (
21
+ from marqetive.platforms.exceptions import (
22
22
  InvalidFileTypeError,
23
23
  MediaUploadError,
24
24
  )
25
- from src.marqetive.utils.file_handlers import download_file
26
- from src.marqetive.utils.media import (
25
+ from marqetive.utils.file_handlers import download_file
26
+ from marqetive.utils.media import (
27
27
  detect_mime_type,
28
28
  format_file_size,
29
29
  get_chunk_count,
30
30
  )
31
- from src.marqetive.utils.retry import STANDARD_BACKOFF, retry_async
31
+ from marqetive.utils.retry import STANDARD_BACKOFF, retry_async
32
32
 
33
33
  logger = logging.getLogger(__name__)
34
34
 
@@ -1,6 +1,6 @@
1
1
  """Utility functions for MarqetiveLib."""
2
2
 
3
- from src.marqetive.utils.file_handlers import (
3
+ from marqetive.utils.file_handlers import (
4
4
  TempFileManager,
5
5
  download_file,
6
6
  download_to_memory,
@@ -9,8 +9,8 @@ from src.marqetive.utils.file_handlers import (
9
9
  stream_file_upload,
10
10
  write_file_bytes,
11
11
  )
12
- from src.marqetive.utils.helpers import format_response, parse_query_params
13
- from src.marqetive.utils.media import (
12
+ from marqetive.utils.helpers import format_response, parse_query_params
13
+ from marqetive.utils.media import (
14
14
  MediaValidator,
15
15
  chunk_file,
16
16
  detect_mime_type,
@@ -17,7 +17,7 @@ from typing import Any
17
17
  import aiofiles
18
18
  import httpx
19
19
 
20
- from src.marqetive.utils.media import detect_mime_type, format_file_size
20
+ from marqetive.utils.media import detect_mime_type, format_file_size
21
21
 
22
22
 
23
23
  class DownloadProgress:
marqetive/utils/oauth.py CHANGED
@@ -10,8 +10,8 @@ from typing import Any
10
10
 
11
11
  import httpx
12
12
 
13
- from src.marqetive.platforms.exceptions import PlatformAuthError
14
- from src.marqetive.platforms.models import AuthCredentials
13
+ from marqetive.platforms.exceptions import PlatformAuthError
14
+ from marqetive.platforms.models import AuthCredentials
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -8,7 +8,7 @@ import re
8
8
  from datetime import datetime, timedelta
9
9
  from typing import Any
10
10
 
11
- from src.marqetive.platforms.models import AuthCredentials
11
+ from marqetive.platforms.models import AuthCredentials
12
12
 
13
13
 
14
14
  def is_token_expired(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: marqetive-lib
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Modern Python utilities for web APIs
5
5
  Keywords: api,utilities,web,http,marqetive
6
6
  Requires-Python: >=3.12
@@ -0,0 +1,35 @@
1
+ marqetive/__init__.py,sha256=LaGrC2HSkbpJBj9mKWuVOflTSN1upTzHowZm6Aw0nOY,2923
2
+ marqetive/core/__init__.py,sha256=qOC6JhGjWOpj9_7umtc6VGH16ZJZE6iNDhA6iDZm8kg,113
3
+ marqetive/core/client.py,sha256=2_FoNpqaRglsWg10i5RTbyDg_kRQKhgWjYs6iDdFxLg,3210
4
+ marqetive/factory.py,sha256=W-8O7eowx_M5ykmL9ZEnjZVoMqNKq8rsDWpyU6X7mxg,14107
5
+ marqetive/platforms/__init__.py,sha256=jMzSg8fI5nCO20aTNaoF6cQ6RIZoxzVhEDvq3ZpAbXc,1327
6
+ marqetive/platforms/base.py,sha256=o0bY9tcV350uGYOHpo3SbSYI61gvx6Ll9KyHaSYFXko,13258
7
+ marqetive/platforms/exceptions.py,sha256=Xyj0bzNiZm5VTErmzXgVW8T6IQnOpF92-HJiKPKjIio,7076
8
+ marqetive/platforms/instagram/__init__.py,sha256=7ClfTovAcCHac2DzKS7z1MFuZpy9lcwet7YP5d6MPeY,135
9
+ marqetive/platforms/instagram/client.py,sha256=Y7jnIDoIRnzwP3QtubwKd8aRi89zrxr5ZLgCqS43xwI,25202
10
+ marqetive/platforms/instagram/exceptions.py,sha256=zwTc90VbG8uz_6O5bKw68Pp53SoevLQUqPiXj4ZgThU,8983
11
+ marqetive/platforms/instagram/media.py,sha256=JDRFB66UlhSjAf0PFR3raGqYxtNTSd5yIZ_2xTGmZRk,21166
12
+ marqetive/platforms/linkedin/__init__.py,sha256=zCnokoPYs56iA1sBSYIlaZW2J50L3CbnQpJSaOLrzP8,131
13
+ marqetive/platforms/linkedin/client.py,sha256=GSWUq66KkerS4H9Vh8j3F6KrMzrlz1DYokbERNyrrCc,23821
14
+ marqetive/platforms/linkedin/exceptions.py,sha256=WHjMZ-XJnKgT6uzzi7LXqFt4lD7nSCjzMzaqtOQA3hM,9767
15
+ marqetive/platforms/linkedin/media.py,sha256=360OT1sdBuJRjuL3wJ-T1irAX5gHmcf4AwAU__cvwwA,16901
16
+ marqetive/platforms/models.py,sha256=W_yOuRWItWSn82n8vXRNN_ScdNkzY1De2qqXaVN2RGU,10974
17
+ marqetive/platforms/tiktok/__init__.py,sha256=BQtxdECd2bW9_vV9W-MY4A1rdXi_xurGWWmzTjTUpMM,123
18
+ marqetive/platforms/tiktok/client.py,sha256=UkljwzrSybIgm4opT5ItT_EXdGx3OGbV4ig8yPoaCbM,17041
19
+ marqetive/platforms/tiktok/exceptions.py,sha256=yOjkZgHyo_t9Chrhws_2n-2sbiDDkCUBv5fdW2nfiHI,10129
20
+ marqetive/platforms/tiktok/media.py,sha256=pAT9AVZX7ZnAnhgoameFLszKDyX16nY43we_CHc5z9s,23771
21
+ marqetive/platforms/twitter/__init__.py,sha256=AA5BELRvZyl2WE_7-puSEWArxZjaXcTJ_i8NGOWrv6k,129
22
+ marqetive/platforms/twitter/client.py,sha256=vZ9wvFPaTOuBD-L48RD1UH6MCPZoF2EHRvFWe0m_cfc,19642
23
+ marqetive/platforms/twitter/exceptions.py,sha256=aKrjUZL07KWQ8hatj5-U7UfnSTl-n8DnTgGGigYCrIY,8931
24
+ marqetive/platforms/twitter/media.py,sha256=16atvKwSukvK_YC_gIm2qsl3pmidWEBAwIfJBPG9HQo,24968
25
+ marqetive/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ marqetive/utils/__init__.py,sha256=bSrNajbxYBSKQayrPviLz8JeGjplnyK8y_NGDtgb7yQ,977
27
+ marqetive/utils/file_handlers.py,sha256=4TP5kmWofNTSZmlS683CM1UYP83WvRd_NubMbqtXv-g,12568
28
+ marqetive/utils/helpers.py,sha256=8-ljhL47SremKcQO2GF8DIHOPODEv1rSioVNuSPCbec,2634
29
+ marqetive/utils/media.py,sha256=Rvxw9XKU65n-z4G1bEihG3wXZBmjSDZUqClfjGFrg6k,12013
30
+ marqetive/utils/oauth.py,sha256=NjHh3o5iKlXMJmzSFJHq-pn5yn82DvmpB7DMTJT0ht8,12105
31
+ marqetive/utils/retry.py,sha256=lAniJLMNWp9XsHrvU0XBNifpNEjfde4MGfd5hlFTPfA,7636
32
+ marqetive/utils/token_validator.py,sha256=pNimFr_FNJLcYGV5oADhoBjflKfEbLQ3Epwnwg_nTbg,6703
33
+ marqetive_lib-0.1.4.dist-info/METADATA,sha256=KawBEFdT6w-WUGCF5ulti5nfi6cWer0oOqP-DFXDDaM,7798
34
+ marqetive_lib-0.1.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
35
+ marqetive_lib-0.1.4.dist-info/RECORD,,
@@ -1,212 +0,0 @@
1
- """Base account factory for managing platform credentials and client creation.
2
-
3
- This module provides an abstract base class for platform-specific account
4
- factories that handle credential management, token refresh, and client creation.
5
- """
6
-
7
- import logging
8
- from abc import ABC, abstractmethod
9
- from collections.abc import Callable
10
- from typing import Any
11
-
12
- from src.marqetive.platforms.exceptions import PlatformAuthError
13
- from src.marqetive.platforms.models import AccountStatus, AuthCredentials
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class BaseAccountFactory(ABC):
19
- """Abstract base class for platform account factories.
20
-
21
- Account factories manage the lifecycle of platform credentials including:
22
- - Token expiry checking and automatic refresh
23
- - Account status management
24
- - Platform-specific client creation
25
-
26
- Subclasses must implement:
27
- - refresh_token(): Platform-specific token refresh logic
28
- - create_client(): Create platform-specific API client
29
- - validate_credentials(): Check if credentials are valid
30
-
31
- Example:
32
- >>> class TwitterAccountFactory(BaseAccountFactory):
33
- ... async def refresh_token(self, credentials):
34
- ... # Implement Twitter OAuth token refresh
35
- ... pass
36
- ...
37
- ... async def create_client(self, credentials):
38
- ... return TwitterClient(credentials=credentials)
39
- ...
40
- ... async def validate_credentials(self, credentials):
41
- ... # Check if credentials work
42
- ... pass
43
- """
44
-
45
- def __init__(
46
- self,
47
- on_status_update: Callable[[str, AccountStatus], None] | None = None,
48
- ) -> None:
49
- """Initialize the account factory.
50
-
51
- Args:
52
- on_status_update: Optional callback when account status changes.
53
- Called with (user_id, new_status).
54
- """
55
- self.on_status_update = on_status_update
56
-
57
- @property
58
- @abstractmethod
59
- def platform_name(self) -> str:
60
- """Get the name of the platform this factory manages.
61
-
62
- Returns:
63
- Platform name (e.g., "twitter", "linkedin").
64
- """
65
- pass
66
-
67
- @abstractmethod
68
- async def refresh_token(self, credentials: AuthCredentials) -> AuthCredentials:
69
- """Refresh the OAuth access token.
70
-
71
- Args:
72
- credentials: Current credentials with expired token.
73
-
74
- Returns:
75
- Updated credentials with new token.
76
-
77
- Raises:
78
- PlatformAuthError: If token refresh fails.
79
- """
80
- pass
81
-
82
- @abstractmethod
83
- async def create_client(self, credentials: AuthCredentials) -> Any:
84
- """Create a platform-specific API client.
85
-
86
- Args:
87
- credentials: Valid credentials for the platform.
88
-
89
- Returns:
90
- Platform-specific API client instance.
91
-
92
- Raises:
93
- PlatformAuthError: If credentials are invalid.
94
- """
95
- pass
96
-
97
- @abstractmethod
98
- async def validate_credentials(self, credentials: AuthCredentials) -> bool:
99
- """Validate that credentials are working.
100
-
101
- Args:
102
- credentials: Credentials to validate.
103
-
104
- Returns:
105
- True if credentials are valid, False otherwise.
106
- """
107
- pass
108
-
109
- async def get_credentials(
110
- self,
111
- credentials: AuthCredentials,
112
- auto_refresh: bool = True,
113
- ) -> AuthCredentials:
114
- """Get credentials, refreshing if necessary.
115
-
116
- This method checks if credentials are expired and automatically
117
- refreshes them if auto_refresh is True.
118
-
119
- Args:
120
- credentials: Current credentials.
121
- auto_refresh: Whether to automatically refresh expired tokens.
122
-
123
- Returns:
124
- Valid credentials (refreshed if necessary).
125
-
126
- Raises:
127
- PlatformAuthError: If refresh fails or credentials are invalid.
128
- """
129
- # Check if token needs refresh
130
- if credentials.needs_refresh():
131
- if not auto_refresh:
132
- logger.warning(
133
- f"Credentials for {credentials.platform} are expired "
134
- "but auto_refresh is disabled"
135
- )
136
- return credentials
137
-
138
- logger.info(
139
- f"Token expired for {credentials.platform}, attempting refresh..."
140
- )
141
- try:
142
- refreshed_creds = await self.refresh_token(credentials)
143
- refreshed_creds.mark_valid()
144
-
145
- # Notify status update
146
- if self.on_status_update and refreshed_creds.user_id:
147
- self.on_status_update(refreshed_creds.user_id, AccountStatus.VALID)
148
-
149
- logger.info(f"Successfully refreshed token for {credentials.platform}")
150
- return refreshed_creds
151
-
152
- except PlatformAuthError as e:
153
- # Determine if this is an OAuth error requiring reconnection
154
- if "oauth" in str(e).lower() or "authorization" in str(e).lower():
155
- credentials.mark_reconnection_required()
156
- if self.on_status_update and credentials.user_id:
157
- self.on_status_update(
158
- credentials.user_id, AccountStatus.RECONNECTION_REQUIRED
159
- )
160
- logger.error(
161
- f"OAuth error refreshing token for {credentials.platform}: {e}"
162
- )
163
- else:
164
- credentials.mark_error()
165
- if self.on_status_update and credentials.user_id:
166
- self.on_status_update(credentials.user_id, AccountStatus.ERROR)
167
- logger.error(
168
- f"Error refreshing token for {credentials.platform}: {e}"
169
- )
170
- raise
171
-
172
- return credentials
173
-
174
- async def create_authenticated_client(
175
- self,
176
- credentials: AuthCredentials,
177
- auto_refresh: bool = True,
178
- ) -> Any:
179
- """Create an authenticated client, refreshing credentials if needed.
180
-
181
- This is the main method to use for getting a ready-to-use client.
182
-
183
- Args:
184
- credentials: Platform credentials.
185
- auto_refresh: Whether to automatically refresh expired tokens.
186
-
187
- Returns:
188
- Authenticated platform client.
189
-
190
- Raises:
191
- PlatformAuthError: If authentication fails.
192
- """
193
- # Get valid credentials (refresh if needed)
194
- valid_creds = await self.get_credentials(credentials, auto_refresh=auto_refresh)
195
-
196
- # Create client
197
- client = await self.create_client(valid_creds)
198
-
199
- return client
200
-
201
- def _update_status(self, user_id: str | None, status: AccountStatus) -> None:
202
- """Internal method to update account status.
203
-
204
- Args:
205
- user_id: User ID for the account.
206
- status: New status.
207
- """
208
- if self.on_status_update and user_id:
209
- try:
210
- self.on_status_update(user_id, status)
211
- except Exception as e:
212
- logger.error(f"Error calling status update callback: {e}")