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.
- marqetive/__init__.py +58 -59
- marqetive/core/__init__.py +1 -1
- marqetive/factory.py +380 -0
- marqetive/platforms/__init__.py +6 -6
- marqetive/platforms/base.py +36 -3
- marqetive/platforms/instagram/__init__.py +2 -4
- marqetive/platforms/instagram/client.py +8 -4
- marqetive/platforms/instagram/exceptions.py +1 -1
- marqetive/platforms/instagram/media.py +2 -2
- marqetive/platforms/linkedin/__init__.py +2 -4
- marqetive/platforms/linkedin/client.py +8 -4
- marqetive/platforms/linkedin/exceptions.py +1 -1
- marqetive/platforms/linkedin/media.py +4 -4
- marqetive/platforms/tiktok/__init__.py +2 -4
- marqetive/platforms/tiktok/client.py +324 -104
- marqetive/platforms/tiktok/exceptions.py +170 -66
- marqetive/platforms/tiktok/media.py +545 -159
- marqetive/platforms/twitter/__init__.py +2 -4
- marqetive/platforms/twitter/client.py +11 -53
- marqetive/platforms/twitter/exceptions.py +1 -1
- marqetive/platforms/twitter/media.py +4 -4
- marqetive/utils/__init__.py +3 -3
- marqetive/utils/file_handlers.py +1 -1
- marqetive/utils/oauth.py +2 -2
- marqetive/utils/token_validator.py +1 -1
- {marqetive_lib-0.1.2.dist-info → marqetive_lib-0.1.4.dist-info}/METADATA +1 -1
- marqetive_lib-0.1.4.dist-info/RECORD +35 -0
- marqetive/core/account_factory.py +0 -212
- marqetive/core/base_manager.py +0 -303
- marqetive/core/progress.py +0 -291
- marqetive/core/registry.py +0 -257
- marqetive/platforms/instagram/factory.py +0 -106
- marqetive/platforms/instagram/manager.py +0 -112
- marqetive/platforms/linkedin/factory.py +0 -130
- marqetive/platforms/linkedin/manager.py +0 -119
- marqetive/platforms/tiktok/factory.py +0 -188
- marqetive/platforms/tiktok/manager.py +0 -115
- marqetive/platforms/twitter/factory.py +0 -151
- marqetive/platforms/twitter/manager.py +0 -121
- marqetive/platforms/twitter/threads.py +0 -442
- marqetive/registry_init.py +0 -66
- marqetive_lib-0.1.2.dist-info/RECORD +0 -48
- {marqetive_lib-0.1.2.dist-info → marqetive_lib-0.1.4.dist-info}/WHEEL +0 -0
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
"""Twitter thread creation and management.
|
|
2
|
-
|
|
3
|
-
This module provides utilities for creating and managing Twitter threads
|
|
4
|
-
(sequences of connected tweets).
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
|
-
import tweepy
|
|
11
|
-
|
|
12
|
-
from src.marqetive.platforms.exceptions import PlatformError, ValidationError
|
|
13
|
-
from src.marqetive.platforms.models import Post, PostStatus
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass
|
|
17
|
-
class TweetData:
|
|
18
|
-
"""Data for a single tweet in a thread.
|
|
19
|
-
|
|
20
|
-
Attributes:
|
|
21
|
-
content: Tweet text content (max 280 characters).
|
|
22
|
-
media_ids: List of Twitter media IDs to attach.
|
|
23
|
-
alt_texts: Optional alt texts for media (same order as media_ids).
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
content: str
|
|
27
|
-
media_ids: list[str] | None = None
|
|
28
|
-
alt_texts: list[str] | None = None
|
|
29
|
-
|
|
30
|
-
def __post_init__(self) -> None:
|
|
31
|
-
"""Validate tweet data after initialization."""
|
|
32
|
-
# Validate content length
|
|
33
|
-
if not self.content:
|
|
34
|
-
raise ValidationError(
|
|
35
|
-
"Tweet content cannot be empty",
|
|
36
|
-
platform="twitter",
|
|
37
|
-
field="content",
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
if len(self.content) > 280:
|
|
41
|
-
raise ValidationError(
|
|
42
|
-
f"Tweet exceeds 280 characters ({len(self.content)} characters)",
|
|
43
|
-
platform="twitter",
|
|
44
|
-
field="content",
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
# Validate media count (Twitter allows max 4 images or 1 video per tweet)
|
|
48
|
-
if self.media_ids and len(self.media_ids) > 4:
|
|
49
|
-
raise ValidationError(
|
|
50
|
-
f"Too many media attachments ({len(self.media_ids)}). "
|
|
51
|
-
"Twitter allows maximum 4 images or 1 video per tweet.",
|
|
52
|
-
platform="twitter",
|
|
53
|
-
field="media_ids",
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
# Validate alt texts match media count
|
|
57
|
-
if self.alt_texts:
|
|
58
|
-
if not self.media_ids:
|
|
59
|
-
raise ValidationError(
|
|
60
|
-
"Cannot provide alt_texts without media_ids",
|
|
61
|
-
platform="twitter",
|
|
62
|
-
field="alt_texts",
|
|
63
|
-
)
|
|
64
|
-
if len(self.alt_texts) != len(self.media_ids):
|
|
65
|
-
raise ValidationError(
|
|
66
|
-
f"Number of alt_texts ({len(self.alt_texts)}) must match "
|
|
67
|
-
f"number of media_ids ({len(self.media_ids)})",
|
|
68
|
-
platform="twitter",
|
|
69
|
-
field="alt_texts",
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@dataclass
|
|
74
|
-
class ThreadResult:
|
|
75
|
-
"""Result of creating a Twitter thread.
|
|
76
|
-
|
|
77
|
-
Attributes:
|
|
78
|
-
thread_id: ID of the first tweet in the thread.
|
|
79
|
-
tweet_ids: List of all tweet IDs in order.
|
|
80
|
-
tweets: List of Post objects for each tweet.
|
|
81
|
-
total_tweets: Total number of tweets in the thread.
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
thread_id: str
|
|
85
|
-
tweet_ids: list[str]
|
|
86
|
-
tweets: list[Post]
|
|
87
|
-
|
|
88
|
-
@property
|
|
89
|
-
def total_tweets(self) -> int:
|
|
90
|
-
"""Get total number of tweets in thread."""
|
|
91
|
-
return len(self.tweet_ids)
|
|
92
|
-
|
|
93
|
-
def __str__(self) -> str:
|
|
94
|
-
"""String representation of thread result."""
|
|
95
|
-
return (
|
|
96
|
-
f"Thread(id={self.thread_id}, "
|
|
97
|
-
f"tweets={self.total_tweets}, "
|
|
98
|
-
f"ids={self.tweet_ids})"
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class TwitterThreadManager:
|
|
103
|
-
"""Manager for creating and managing Twitter threads.
|
|
104
|
-
|
|
105
|
-
Example:
|
|
106
|
-
>>> manager = TwitterThreadManager(tweepy_client)
|
|
107
|
-
>>> tweets = [
|
|
108
|
-
... TweetData("First tweet in thread"),
|
|
109
|
-
... TweetData("Second tweet with more info"),
|
|
110
|
-
... TweetData("Final tweet with conclusion"),
|
|
111
|
-
... ]
|
|
112
|
-
>>> result = await manager.create_thread(tweets)
|
|
113
|
-
>>> print(f"Created thread: {result.thread_id}")
|
|
114
|
-
"""
|
|
115
|
-
|
|
116
|
-
def __init__(self, tweepy_client: tweepy.Client) -> None:
|
|
117
|
-
"""Initialize thread manager.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
tweepy_client: Authenticated tweepy Client instance.
|
|
121
|
-
"""
|
|
122
|
-
self.client = tweepy_client
|
|
123
|
-
|
|
124
|
-
async def create_thread(
|
|
125
|
-
self,
|
|
126
|
-
tweets: list[TweetData],
|
|
127
|
-
*,
|
|
128
|
-
auto_number: bool = False,
|
|
129
|
-
number_format: str = "{index}/{total}",
|
|
130
|
-
) -> ThreadResult:
|
|
131
|
-
"""Create a Twitter thread from multiple tweets.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
tweets: List of TweetData objects representing the thread.
|
|
135
|
-
auto_number: If True, automatically add tweet numbers to content.
|
|
136
|
-
number_format: Format string for auto-numbering (supports {index}, {total}).
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
ThreadResult with thread ID and all tweet information.
|
|
140
|
-
|
|
141
|
-
Raises:
|
|
142
|
-
ValidationError: If thread data is invalid.
|
|
143
|
-
PlatformError: If thread creation fails.
|
|
144
|
-
|
|
145
|
-
Example:
|
|
146
|
-
>>> tweets = [
|
|
147
|
-
... TweetData("Part 1: Introduction"),
|
|
148
|
-
... TweetData("Part 2: Details"),
|
|
149
|
-
... TweetData("Part 3: Conclusion"),
|
|
150
|
-
... ]
|
|
151
|
-
>>> result = await manager.create_thread(tweets, auto_number=True)
|
|
152
|
-
"""
|
|
153
|
-
if not tweets:
|
|
154
|
-
raise ValidationError(
|
|
155
|
-
"Thread must contain at least one tweet",
|
|
156
|
-
platform="twitter",
|
|
157
|
-
field="tweets",
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
if len(tweets) > 25:
|
|
161
|
-
raise ValidationError(
|
|
162
|
-
f"Thread too long ({len(tweets)} tweets). "
|
|
163
|
-
"Consider splitting into multiple threads.",
|
|
164
|
-
platform="twitter",
|
|
165
|
-
field="tweets",
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
thread_id: str | None = None
|
|
169
|
-
tweet_ids: list[str] = []
|
|
170
|
-
post_objects: list[Post] = []
|
|
171
|
-
previous_tweet_id: str | None = None
|
|
172
|
-
|
|
173
|
-
try:
|
|
174
|
-
for index, tweet_data in enumerate(tweets, start=1):
|
|
175
|
-
# Prepare tweet content
|
|
176
|
-
content = tweet_data.content
|
|
177
|
-
|
|
178
|
-
# Add auto-numbering if requested
|
|
179
|
-
if auto_number and len(tweets) > 1:
|
|
180
|
-
number_prefix = number_format.format(
|
|
181
|
-
index=index,
|
|
182
|
-
total=len(tweets),
|
|
183
|
-
)
|
|
184
|
-
content = f"{number_prefix} {content}"
|
|
185
|
-
|
|
186
|
-
# Validate length after numbering
|
|
187
|
-
if len(content) > 280:
|
|
188
|
-
raise ValidationError(
|
|
189
|
-
f"Tweet {index} exceeds 280 characters after auto-numbering "
|
|
190
|
-
f"({len(content)} characters). Consider shorter content or "
|
|
191
|
-
"disable auto_number.",
|
|
192
|
-
platform="twitter",
|
|
193
|
-
field="content",
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
# Prepare tweet parameters
|
|
197
|
-
tweet_params: dict[str, Any] = {"text": content}
|
|
198
|
-
|
|
199
|
-
# Add reply-to for threading
|
|
200
|
-
if previous_tweet_id:
|
|
201
|
-
tweet_params["in_reply_to_tweet_id"] = previous_tweet_id
|
|
202
|
-
|
|
203
|
-
# Add media if provided
|
|
204
|
-
if tweet_data.media_ids:
|
|
205
|
-
tweet_params["media_ids"] = tweet_data.media_ids
|
|
206
|
-
|
|
207
|
-
# Create tweet
|
|
208
|
-
response = self.client.create_tweet(**tweet_params)
|
|
209
|
-
tweet_id = str(response.data["id"]) # type: ignore[index]
|
|
210
|
-
|
|
211
|
-
# Store IDs
|
|
212
|
-
if thread_id is None:
|
|
213
|
-
thread_id = tweet_id
|
|
214
|
-
tweet_ids.append(tweet_id)
|
|
215
|
-
previous_tweet_id = tweet_id
|
|
216
|
-
|
|
217
|
-
# Fetch full tweet details
|
|
218
|
-
tweet_response = self.client.get_tweet(
|
|
219
|
-
tweet_id,
|
|
220
|
-
tweet_fields=[
|
|
221
|
-
"created_at",
|
|
222
|
-
"public_metrics",
|
|
223
|
-
"attachments",
|
|
224
|
-
"author_id",
|
|
225
|
-
],
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
# Convert to Post object
|
|
229
|
-
tweet = tweet_response.data # type: ignore[attr-defined]
|
|
230
|
-
post = Post(
|
|
231
|
-
post_id=tweet_id,
|
|
232
|
-
platform="twitter",
|
|
233
|
-
content=content,
|
|
234
|
-
status=PostStatus.PUBLISHED,
|
|
235
|
-
created_at=tweet.created_at,
|
|
236
|
-
author_id=str(tweet.author_id) if tweet.author_id else None,
|
|
237
|
-
raw_data=tweet.data,
|
|
238
|
-
)
|
|
239
|
-
post_objects.append(post)
|
|
240
|
-
|
|
241
|
-
except tweepy.TweepyException as e:
|
|
242
|
-
# If thread creation fails partway through, we have partial thread
|
|
243
|
-
# Log the created tweets for cleanup if needed
|
|
244
|
-
if tweet_ids:
|
|
245
|
-
raise PlatformError(
|
|
246
|
-
f"Thread creation failed at tweet {len(tweet_ids) + 1}. "
|
|
247
|
-
f"Partial thread created with IDs: {tweet_ids}. "
|
|
248
|
-
f"Error: {e}",
|
|
249
|
-
platform="twitter",
|
|
250
|
-
) from e
|
|
251
|
-
raise PlatformError(
|
|
252
|
-
f"Failed to create thread: {e}",
|
|
253
|
-
platform="twitter",
|
|
254
|
-
) from e
|
|
255
|
-
|
|
256
|
-
if not thread_id:
|
|
257
|
-
raise PlatformError(
|
|
258
|
-
"Thread creation completed but no thread ID available",
|
|
259
|
-
platform="twitter",
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
return ThreadResult(
|
|
263
|
-
thread_id=thread_id,
|
|
264
|
-
tweet_ids=tweet_ids,
|
|
265
|
-
tweets=post_objects,
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
async def delete_thread(
|
|
269
|
-
self,
|
|
270
|
-
thread_id: str,
|
|
271
|
-
tweet_ids: list[str],
|
|
272
|
-
*,
|
|
273
|
-
continue_on_error: bool = True,
|
|
274
|
-
) -> dict[str, bool]:
|
|
275
|
-
"""Delete all tweets in a thread.
|
|
276
|
-
|
|
277
|
-
Args:
|
|
278
|
-
thread_id: ID of the first tweet (for logging).
|
|
279
|
-
tweet_ids: List of all tweet IDs to delete.
|
|
280
|
-
continue_on_error: If True, continue deleting even if some fail.
|
|
281
|
-
|
|
282
|
-
Returns:
|
|
283
|
-
Dictionary mapping tweet_id to deletion success status.
|
|
284
|
-
|
|
285
|
-
Example:
|
|
286
|
-
>>> result = await manager.delete_thread(
|
|
287
|
-
... thread_id="123",
|
|
288
|
-
... tweet_ids=["123", "456", "789"]
|
|
289
|
-
... )
|
|
290
|
-
>>> print(f"Deleted: {sum(result.values())} / {len(result)} tweets")
|
|
291
|
-
"""
|
|
292
|
-
results: dict[str, bool] = {}
|
|
293
|
-
errors: list[str] = []
|
|
294
|
-
|
|
295
|
-
# Delete in reverse order (newest to oldest)
|
|
296
|
-
for tweet_id in reversed(tweet_ids):
|
|
297
|
-
try:
|
|
298
|
-
self.client.delete_tweet(tweet_id)
|
|
299
|
-
results[tweet_id] = True
|
|
300
|
-
except tweepy.TweepyException as e:
|
|
301
|
-
results[tweet_id] = False
|
|
302
|
-
errors.append(f"Tweet {tweet_id}: {e}")
|
|
303
|
-
|
|
304
|
-
if not continue_on_error:
|
|
305
|
-
raise PlatformError(
|
|
306
|
-
f"Failed to delete tweet {tweet_id} in thread {thread_id}: {e}",
|
|
307
|
-
platform="twitter",
|
|
308
|
-
) from e
|
|
309
|
-
|
|
310
|
-
# If there were errors but continue_on_error=True, log them
|
|
311
|
-
if errors:
|
|
312
|
-
error_summary = "; ".join(errors)
|
|
313
|
-
raise PlatformError(
|
|
314
|
-
f"Some tweets in thread {thread_id} failed to delete: {error_summary}",
|
|
315
|
-
platform="twitter",
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
return results
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
def split_content_into_tweets(
|
|
322
|
-
content: str,
|
|
323
|
-
*,
|
|
324
|
-
max_length: int = 280,
|
|
325
|
-
split_on_sentences: bool = True,
|
|
326
|
-
add_continuation: bool = True,
|
|
327
|
-
continuation_suffix: str = "...",
|
|
328
|
-
) -> list[str]:
|
|
329
|
-
"""Split long content into multiple tweet-sized chunks.
|
|
330
|
-
|
|
331
|
-
Attempts to split on sentence boundaries when possible.
|
|
332
|
-
|
|
333
|
-
Args:
|
|
334
|
-
content: Long text content to split.
|
|
335
|
-
max_length: Maximum characters per tweet (default: 280).
|
|
336
|
-
split_on_sentences: Try to split on sentence boundaries.
|
|
337
|
-
add_continuation: Add continuation indicator to split tweets.
|
|
338
|
-
continuation_suffix: Suffix to add when splitting mid-content.
|
|
339
|
-
|
|
340
|
-
Returns:
|
|
341
|
-
List of tweet content strings.
|
|
342
|
-
|
|
343
|
-
Example:
|
|
344
|
-
>>> long_text = "This is a very long piece of content..." * 20
|
|
345
|
-
>>> tweets = split_content_into_tweets(long_text)
|
|
346
|
-
>>> print(f"Split into {len(tweets)} tweets")
|
|
347
|
-
"""
|
|
348
|
-
if len(content) <= max_length:
|
|
349
|
-
return [content]
|
|
350
|
-
|
|
351
|
-
tweets: list[str] = []
|
|
352
|
-
remaining = content
|
|
353
|
-
|
|
354
|
-
while remaining:
|
|
355
|
-
# Calculate available space
|
|
356
|
-
available = max_length
|
|
357
|
-
if add_continuation and len(remaining) > max_length:
|
|
358
|
-
available -= len(continuation_suffix)
|
|
359
|
-
|
|
360
|
-
if len(remaining) <= available:
|
|
361
|
-
# Last chunk
|
|
362
|
-
tweets.append(remaining)
|
|
363
|
-
break
|
|
364
|
-
|
|
365
|
-
# Find split point
|
|
366
|
-
split_point = available
|
|
367
|
-
|
|
368
|
-
if split_on_sentences:
|
|
369
|
-
# Try to split on sentence boundary
|
|
370
|
-
sentence_ends = [". ", "! ", "? ", ".\n", "!\n", "?\n"]
|
|
371
|
-
best_split = 0
|
|
372
|
-
|
|
373
|
-
for sent_end in sentence_ends:
|
|
374
|
-
pos = remaining[:available].rfind(sent_end)
|
|
375
|
-
if pos > best_split:
|
|
376
|
-
best_split = pos + len(sent_end)
|
|
377
|
-
|
|
378
|
-
if best_split > 0:
|
|
379
|
-
split_point = best_split
|
|
380
|
-
else:
|
|
381
|
-
# Try to split on word boundary
|
|
382
|
-
last_space = remaining[:available].rfind(" ")
|
|
383
|
-
if last_space > 0:
|
|
384
|
-
split_point = last_space + 1
|
|
385
|
-
|
|
386
|
-
# Extract chunk
|
|
387
|
-
chunk = remaining[:split_point].rstrip()
|
|
388
|
-
|
|
389
|
-
# Add continuation indicator if not at end
|
|
390
|
-
if add_continuation and len(remaining) > split_point:
|
|
391
|
-
chunk += continuation_suffix
|
|
392
|
-
|
|
393
|
-
tweets.append(chunk)
|
|
394
|
-
remaining = remaining[split_point:].lstrip()
|
|
395
|
-
|
|
396
|
-
return tweets
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
def create_numbered_thread(
|
|
400
|
-
content_parts: list[str],
|
|
401
|
-
*,
|
|
402
|
-
number_format: str = "{index}/{total}",
|
|
403
|
-
media_per_tweet: list[list[str]] | None = None,
|
|
404
|
-
) -> list[TweetData]:
|
|
405
|
-
"""Create a numbered thread from content parts.
|
|
406
|
-
|
|
407
|
-
Convenience function to create TweetData with auto-numbering.
|
|
408
|
-
|
|
409
|
-
Args:
|
|
410
|
-
content_parts: List of content strings, one per tweet.
|
|
411
|
-
number_format: Format string for numbering.
|
|
412
|
-
media_per_tweet: Optional list of media ID lists, one per tweet.
|
|
413
|
-
|
|
414
|
-
Returns:
|
|
415
|
-
List of TweetData objects ready for thread creation.
|
|
416
|
-
|
|
417
|
-
Example:
|
|
418
|
-
>>> parts = ["Introduction", "Main content", "Conclusion"]
|
|
419
|
-
>>> tweets = create_numbered_thread(parts)
|
|
420
|
-
>>> # Tweets will be: "1/3 Introduction", "2/3 Main content", "3/3 Conclusion"
|
|
421
|
-
"""
|
|
422
|
-
total = len(content_parts)
|
|
423
|
-
tweet_data_list: list[TweetData] = []
|
|
424
|
-
|
|
425
|
-
for index, content in enumerate(content_parts, start=1):
|
|
426
|
-
# Add number prefix
|
|
427
|
-
number_prefix = number_format.format(index=index, total=total)
|
|
428
|
-
numbered_content = f"{number_prefix} {content}"
|
|
429
|
-
|
|
430
|
-
# Get media for this tweet if provided
|
|
431
|
-
media_ids = None
|
|
432
|
-
if media_per_tweet and index - 1 < len(media_per_tweet):
|
|
433
|
-
media_ids = media_per_tweet[index - 1]
|
|
434
|
-
|
|
435
|
-
tweet_data_list.append(
|
|
436
|
-
TweetData(
|
|
437
|
-
content=numbered_content,
|
|
438
|
-
media_ids=media_ids,
|
|
439
|
-
)
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
return tweet_data_list
|
marqetive/registry_init.py
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"""Registry initialization for platform managers.
|
|
2
|
-
|
|
3
|
-
This module provides the initialization function that registers all available
|
|
4
|
-
platform managers with the global registry.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import logging
|
|
8
|
-
|
|
9
|
-
from src.marqetive.core.registry import register_platform
|
|
10
|
-
from src.marqetive.platforms.instagram.manager import InstagramPostManager
|
|
11
|
-
from src.marqetive.platforms.linkedin.manager import LinkedInPostManager
|
|
12
|
-
from src.marqetive.platforms.twitter.manager import TwitterPostManager
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
_initialized = False
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def initialize_platform_registry() -> None:
|
|
20
|
-
"""Initialize the platform registry with all available platforms.
|
|
21
|
-
|
|
22
|
-
This function registers all platform managers (Twitter, LinkedIn, Instagram)
|
|
23
|
-
with the global registry. It should be called once at application startup.
|
|
24
|
-
|
|
25
|
-
This function is idempotent - calling it multiple times is safe.
|
|
26
|
-
|
|
27
|
-
Example:
|
|
28
|
-
>>> from marqetive_lib import initialize_platform_registry
|
|
29
|
-
>>> initialize_platform_registry()
|
|
30
|
-
>>> # Now you can use get_manager_for_platform()
|
|
31
|
-
>>> from marqetive_lib import get_manager_for_platform
|
|
32
|
-
>>> manager = get_manager_for_platform("twitter")
|
|
33
|
-
"""
|
|
34
|
-
global _initialized
|
|
35
|
-
|
|
36
|
-
if _initialized:
|
|
37
|
-
logger.debug("Platform registry already initialized")
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
logger.info("Initializing platform registry...")
|
|
41
|
-
|
|
42
|
-
# Register all platform managers
|
|
43
|
-
register_platform("twitter", TwitterPostManager)
|
|
44
|
-
register_platform("linkedin", LinkedInPostManager)
|
|
45
|
-
register_platform("instagram", InstagramPostManager)
|
|
46
|
-
|
|
47
|
-
_initialized = True
|
|
48
|
-
logger.info("Platform registry initialized with 3 platforms")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def is_registry_initialized() -> bool:
|
|
52
|
-
"""Check if registry has been initialized.
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
True if initialized, False otherwise.
|
|
56
|
-
"""
|
|
57
|
-
return _initialized
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def reset_registry() -> None:
|
|
61
|
-
"""Reset the initialization flag.
|
|
62
|
-
|
|
63
|
-
This is mainly useful for testing. It allows re-initialization of the registry.
|
|
64
|
-
"""
|
|
65
|
-
global _initialized
|
|
66
|
-
_initialized = False
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
marqetive/__init__.py,sha256=XVnYmB817AILAnJYg1WVNnmrd1MvJns3QpPbfdjmwRE,3142
|
|
2
|
-
marqetive/core/__init__.py,sha256=5Wgcby-EQkTxrPVD4so69nBmZ66Ng9JGqBov4RQMT4A,117
|
|
3
|
-
marqetive/core/account_factory.py,sha256=9fsP6fzLYp3UGU59fkxwURgzn7eBxr8-WJt3aP-Z65U,7153
|
|
4
|
-
marqetive/core/base_manager.py,sha256=z28bV4p1FC7s9g_YkXIKW_qkv8bjF4tMoF-cGGksJuA,10047
|
|
5
|
-
marqetive/core/client.py,sha256=2_FoNpqaRglsWg10i5RTbyDg_kRQKhgWjYs6iDdFxLg,3210
|
|
6
|
-
marqetive/core/progress.py,sha256=5Vyksf8YfQUVRudwDEuegcxoc0js5qbHM2SFZ6VLxHM,8593
|
|
7
|
-
marqetive/core/registry.py,sha256=D6qlafpvemYfE9GouWcWZyltK0aNcFLSi_2M-WdTIdE,8084
|
|
8
|
-
marqetive/platforms/__init__.py,sha256=6QjetNWodf32pgGQsmLKs7W-v-pG2kKckb5JEPqIPNI,1351
|
|
9
|
-
marqetive/platforms/base.py,sha256=zmCda00Szq_LswRdsWPI5V88TysLH3FIZ0mKwH6b8dE,11957
|
|
10
|
-
marqetive/platforms/exceptions.py,sha256=Xyj0bzNiZm5VTErmzXgVW8T6IQnOpF92-HJiKPKjIio,7076
|
|
11
|
-
marqetive/platforms/instagram/__init__.py,sha256=9V0GPVm9HSTtDdV0bQEfuND8r7PEF2smOL3lb5BZevg,343
|
|
12
|
-
marqetive/platforms/instagram/client.py,sha256=PxVsHUW8SSpPe2Kdd5zGezD7LUvJj5tdyNj8In_ChxI,24955
|
|
13
|
-
marqetive/platforms/instagram/exceptions.py,sha256=zFG_CkstlZWG47zsFdnObOZviTDIx3Z8yVG9-x7GMVY,8987
|
|
14
|
-
marqetive/platforms/instagram/factory.py,sha256=082cF47zs9GvrnrFFaKmftbt_GmvcHZvSNIJsOQcupY,3553
|
|
15
|
-
marqetive/platforms/instagram/manager.py,sha256=RhV2QpLp9z2p-MCP3E-UzeYFCmuB0K8PIdDfwSaxdJI,3730
|
|
16
|
-
marqetive/platforms/instagram/media.py,sha256=tbFzOf670T8UDoCD9fVeS03Uzhp6HSUFrpPgJVgpdRE,21174
|
|
17
|
-
marqetive/platforms/linkedin/__init__.py,sha256=mcHNOKW5niq0MGBMuRpCJfEg2DkE3sCt0J0AqSZUPBs,333
|
|
18
|
-
marqetive/platforms/linkedin/client.py,sha256=d7YDd23VAqUMAHw8Oi9Swpq5RwhIIpl4TjbrBI9GkfE,23574
|
|
19
|
-
marqetive/platforms/linkedin/exceptions.py,sha256=64J02_99cUcUW5i5xHE_sa0_6V4ibW8PmCEz2aROIE4,9771
|
|
20
|
-
marqetive/platforms/linkedin/factory.py,sha256=8JNxAMC-lJcU1trnd0PFge0LQWQMiIVzZORJHwcBhmM,4536
|
|
21
|
-
marqetive/platforms/linkedin/manager.py,sha256=XB6HB6-MWsDz2Arij5GuyRH_j5xWkYb8sHCTlKFWzMg,4000
|
|
22
|
-
marqetive/platforms/linkedin/media.py,sha256=busrlTA3CisY43qDy9dmiAmbFVz8De4nDsA4CV9PeWk,16917
|
|
23
|
-
marqetive/platforms/models.py,sha256=W_yOuRWItWSn82n8vXRNN_ScdNkzY1De2qqXaVN2RGU,10974
|
|
24
|
-
marqetive/platforms/tiktok/__init__.py,sha256=_lfPj-5VfNaL4cE39Uiv8QOI1DtRnKiP5feKdPQ53zI,313
|
|
25
|
-
marqetive/platforms/tiktok/client.py,sha256=p0uhPCyjykMj8mx0y3oCEOTNwEM_obr1CW8iLz-8AMw,10715
|
|
26
|
-
marqetive/platforms/tiktok/exceptions.py,sha256=SKzmAEIfBf9qeYPXdOFKL2ARx7MK_h57fyohbg_86vY,5780
|
|
27
|
-
marqetive/platforms/tiktok/factory.py,sha256=aqzyWe42i0R3Tvr2ml2xEYeW2e3USqIkz5-o8O5ae64,6764
|
|
28
|
-
marqetive/platforms/tiktok/manager.py,sha256=0bIL26NCppN5PFk0DKPaH2zyrm4wwA-Mfm8KQwq97UY,4113
|
|
29
|
-
marqetive/platforms/tiktok/media.py,sha256=B3QoE05xlQ0OSZ_1y_MiNX8bJFwbyM4jDvM4w7A-HHA,10652
|
|
30
|
-
marqetive/platforms/twitter/__init__.py,sha256=TRVOejA-wa_tw50_YUwTYnhGyGuOK2IFpZWvVZP94HQ,325
|
|
31
|
-
marqetive/platforms/twitter/client.py,sha256=s5nLUc3wOSEBCNuFCqxoLqUypBI6eDK6fRC1iP5OnZU,20856
|
|
32
|
-
marqetive/platforms/twitter/exceptions.py,sha256=yRLLcodSouaFV076iwbxcWZzXUuoswAOXuL_-iPvLOk,8935
|
|
33
|
-
marqetive/platforms/twitter/factory.py,sha256=nBBl5aFcNCgIwf45PWywjpx_Qp0aOdbOHthvQX-BzI4,5286
|
|
34
|
-
marqetive/platforms/twitter/manager.py,sha256=NEqFllDdQ6nlUQMV1M5koX8DMURuvNsa39gKiTpm5A0,4108
|
|
35
|
-
marqetive/platforms/twitter/media.py,sha256=szkILg8geuH5wVbGXUBeHr9B-48j08njb-NlClPDcQQ,24984
|
|
36
|
-
marqetive/platforms/twitter/threads.py,sha256=F1msBRcmpAb_NKaZvdDn0oA_cQwwzFoXGeiUIdr7rrY,14772
|
|
37
|
-
marqetive/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
marqetive/registry_init.py,sha256=ozvGN_lWWx_vvYcU_ONA49EzwN4ymbrB1jtkRchYvAU,2051
|
|
39
|
-
marqetive/utils/__init__.py,sha256=qBn_DN-L2YTfuB_G-n_bHJjtuVRiyH3ftl9JZ0JO93M,989
|
|
40
|
-
marqetive/utils/file_handlers.py,sha256=IUdRZxr_jxAuwxUypUkUgKqiGDcIHNr1LhCb2Ne2a98,12572
|
|
41
|
-
marqetive/utils/helpers.py,sha256=8-ljhL47SremKcQO2GF8DIHOPODEv1rSioVNuSPCbec,2634
|
|
42
|
-
marqetive/utils/media.py,sha256=Rvxw9XKU65n-z4G1bEihG3wXZBmjSDZUqClfjGFrg6k,12013
|
|
43
|
-
marqetive/utils/oauth.py,sha256=B8GbzliQKEV5XpPshrKCHNlFl_arEOfZ10KKIUDKfQg,12113
|
|
44
|
-
marqetive/utils/retry.py,sha256=lAniJLMNWp9XsHrvU0XBNifpNEjfde4MGfd5hlFTPfA,7636
|
|
45
|
-
marqetive/utils/token_validator.py,sha256=asLMiEgT-BtqEpn_HDX15vJoSBcH7CGW0abervFOXxM,6707
|
|
46
|
-
marqetive_lib-0.1.2.dist-info/METADATA,sha256=xk2oWdG9QJlzw1Mu49BWngovHJ4izI2N0pwZFKNzP-4,7798
|
|
47
|
-
marqetive_lib-0.1.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
48
|
-
marqetive_lib-0.1.2.dist-info/RECORD,,
|
|
File without changes
|