pygpt-net 2.6.1__py3-none-any.whl → 2.6.2__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 (61) hide show
  1. pygpt_net/CHANGELOG.txt +4 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +15 -1
  4. pygpt_net/controller/chat/response.py +5 -3
  5. pygpt_net/controller/chat/stream.py +40 -2
  6. pygpt_net/controller/plugins/plugins.py +25 -0
  7. pygpt_net/controller/presets/editor.py +33 -88
  8. pygpt_net/controller/presets/experts.py +20 -1
  9. pygpt_net/controller/presets/presets.py +2 -2
  10. pygpt_net/controller/ui/mode.py +17 -66
  11. pygpt_net/core/agents/runner.py +15 -7
  12. pygpt_net/core/experts/experts.py +3 -3
  13. pygpt_net/data/config/config.json +3 -3
  14. pygpt_net/data/config/models.json +3 -3
  15. pygpt_net/data/locale/locale.de.ini +2 -0
  16. pygpt_net/data/locale/locale.en.ini +2 -0
  17. pygpt_net/data/locale/locale.es.ini +2 -0
  18. pygpt_net/data/locale/locale.fr.ini +2 -0
  19. pygpt_net/data/locale/locale.it.ini +2 -0
  20. pygpt_net/data/locale/locale.pl.ini +3 -1
  21. pygpt_net/data/locale/locale.uk.ini +2 -0
  22. pygpt_net/data/locale/locale.zh.ini +2 -0
  23. pygpt_net/plugin/base/plugin.py +35 -3
  24. pygpt_net/plugin/bitbucket/__init__.py +12 -0
  25. pygpt_net/plugin/bitbucket/config.py +267 -0
  26. pygpt_net/plugin/bitbucket/plugin.py +125 -0
  27. pygpt_net/plugin/bitbucket/worker.py +569 -0
  28. pygpt_net/plugin/facebook/__init__.py +12 -0
  29. pygpt_net/plugin/facebook/config.py +359 -0
  30. pygpt_net/plugin/facebook/plugin.py +114 -0
  31. pygpt_net/plugin/facebook/worker.py +698 -0
  32. pygpt_net/plugin/github/__init__.py +12 -0
  33. pygpt_net/plugin/github/config.py +441 -0
  34. pygpt_net/plugin/github/plugin.py +124 -0
  35. pygpt_net/plugin/github/worker.py +674 -0
  36. pygpt_net/plugin/google/__init__.py +12 -0
  37. pygpt_net/plugin/google/config.py +367 -0
  38. pygpt_net/plugin/google/plugin.py +126 -0
  39. pygpt_net/plugin/google/worker.py +826 -0
  40. pygpt_net/plugin/slack/__init__.py +12 -0
  41. pygpt_net/plugin/slack/config.py +349 -0
  42. pygpt_net/plugin/slack/plugin.py +116 -0
  43. pygpt_net/plugin/slack/worker.py +639 -0
  44. pygpt_net/plugin/telegram/__init__.py +12 -0
  45. pygpt_net/plugin/telegram/config.py +308 -0
  46. pygpt_net/plugin/telegram/plugin.py +118 -0
  47. pygpt_net/plugin/telegram/worker.py +563 -0
  48. pygpt_net/plugin/twitter/__init__.py +12 -0
  49. pygpt_net/plugin/twitter/config.py +491 -0
  50. pygpt_net/plugin/twitter/plugin.py +126 -0
  51. pygpt_net/plugin/twitter/worker.py +837 -0
  52. pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
  53. pygpt_net/ui/base/config_dialog.py +4 -0
  54. pygpt_net/ui/dialog/preset.py +34 -77
  55. pygpt_net/ui/layout/toolbox/presets.py +2 -2
  56. pygpt_net/ui/main.py +3 -1
  57. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.2.dist-info}/METADATA +145 -2
  58. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.2.dist-info}/RECORD +61 -33
  59. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.2.dist-info}/LICENSE +0 -0
  60. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.2.dist-info}/WHEEL +0 -0
  61. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,491 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.08.14 00:00:00 #
10
+ # ================================================== #
11
+
12
+ from pygpt_net.plugin.base.config import BaseConfig, BasePlugin
13
+
14
+
15
+ class Config(BaseConfig):
16
+ def __init__(self, plugin: BasePlugin = None, *args, **kwargs):
17
+ super(Config, self).__init__(plugin)
18
+ self.plugin = plugin
19
+
20
+ def from_defaults(self, plugin: BasePlugin = None):
21
+ # Endpoints / HTTP
22
+ plugin.add_option(
23
+ "api_base",
24
+ type="text",
25
+ value="https://api.x.com",
26
+ label="API base",
27
+ description="Base API URL (default https://api.x.com).",
28
+ )
29
+ plugin.add_option(
30
+ "authorize_base",
31
+ type="text",
32
+ value="https://x.com",
33
+ label="Authorize base",
34
+ description="Base for OAuth authorize (default https://x.com).",
35
+ )
36
+ plugin.add_option(
37
+ "http_timeout",
38
+ type="int",
39
+ value=30,
40
+ label="HTTP timeout (s)",
41
+ description="Requests timeout in seconds.",
42
+ )
43
+
44
+ # OAuth2 PKCE
45
+ plugin.add_option(
46
+ "oauth2_client_id",
47
+ type="text",
48
+ value="",
49
+ label="OAuth2 Client ID",
50
+ description="Client ID from X Developer Portal.",
51
+ secret=True,
52
+ )
53
+ plugin.add_option(
54
+ "oauth2_client_secret",
55
+ type="text",
56
+ value="",
57
+ label="OAuth2 Client Secret (optional)",
58
+ description="Only for confidential clients (Basic auth on token endpoints).",
59
+ secret=True,
60
+ )
61
+ plugin.add_option(
62
+ "oauth2_confidential",
63
+ type="bool",
64
+ value=False,
65
+ label="Confidential client (use Basic auth)",
66
+ description="Enable if your App is confidential and you want to use Basic auth with token endpoints.",
67
+ )
68
+ plugin.add_option(
69
+ "oauth2_redirect_uri",
70
+ type="text",
71
+ value="http://127.0.0.1:8731/callback",
72
+ label="Redirect URI",
73
+ description="Must exactly match one of the callback URLs in your X App (including port).",
74
+ )
75
+ plugin.add_option(
76
+ "oauth2_scopes",
77
+ type="text",
78
+ value="tweet.read users.read like.read like.write tweet.write bookmark.read bookmark.write tweet.moderate.write offline.access",
79
+ label="Scopes",
80
+ description="Space-separated OAuth2 scopes for Authorization Code with PKCE.",
81
+ )
82
+ plugin.add_option(
83
+ "oauth2_code_verifier",
84
+ type="text",
85
+ value="",
86
+ label="(auto) code_verifier",
87
+ description="Generated by x_oauth_begin.",
88
+ secret=True,
89
+ )
90
+ plugin.add_option(
91
+ "oauth2_state",
92
+ type="text",
93
+ value="",
94
+ label="(auto) state",
95
+ description="Generated by x_oauth_begin.",
96
+ secret=True,
97
+ )
98
+ plugin.add_option(
99
+ "oauth2_access_token",
100
+ type="textarea",
101
+ value="",
102
+ label="(auto) Access token",
103
+ description="Stored user access token.",
104
+ secret=True,
105
+ )
106
+ plugin.add_option(
107
+ "oauth2_refresh_token",
108
+ type="textarea",
109
+ value="",
110
+ label="(auto) Refresh token",
111
+ description="Stored user refresh token.",
112
+ secret=True,
113
+ )
114
+ plugin.add_option(
115
+ "oauth2_expires_at",
116
+ type="text",
117
+ value="0",
118
+ label="(auto) Expires at (unix)",
119
+ description="Auto-calculated expiry time.",
120
+ secret=False,
121
+ )
122
+
123
+ # App-only bearer (optional for read-only)
124
+ plugin.add_option(
125
+ "bearer_token",
126
+ type="textarea",
127
+ value="",
128
+ label="App-only Bearer token (optional)",
129
+ description="Optional app-only bearer for read endpoints.",
130
+ secret=True,
131
+ )
132
+
133
+ # Convenience cache
134
+ plugin.add_option(
135
+ "user_id",
136
+ type="text",
137
+ value="",
138
+ label="(auto) User ID",
139
+ description="Cached after x_me or oauth exchange.",
140
+ )
141
+ plugin.add_option(
142
+ "username",
143
+ type="text",
144
+ value="",
145
+ label="(auto) Username",
146
+ description="Cached after x_me or oauth exchange.",
147
+ )
148
+ plugin.add_option(
149
+ "oauth_auto_begin",
150
+ type="bool",
151
+ value=True,
152
+ label="Auto-start OAuth when required",
153
+ description="If a command needs user token, begin PKCE flow automatically.",
154
+ )
155
+ plugin.add_option(
156
+ "oauth_open_browser",
157
+ type="bool",
158
+ value=True,
159
+ label="Open browser automatically",
160
+ description="Open authorize URL in default browser.",
161
+ )
162
+ plugin.add_option(
163
+ "oauth_local_server",
164
+ type="bool",
165
+ value=True,
166
+ label="Use local server for OAuth",
167
+ description="Start local HTTP server to capture redirect (requires redirect_uri = http://localhost[:port]/path).",
168
+ )
169
+ plugin.add_option(
170
+ "oauth_local_timeout",
171
+ type="int",
172
+ value=180,
173
+ label="OAuth local timeout (s)",
174
+ description="How long to wait for redirect with code.",
175
+ )
176
+ plugin.add_option(
177
+ "oauth_success_html",
178
+ type="textarea",
179
+ value="<html><body><h3>Authorization complete. You can close this window.</h3></body></html>",
180
+ label="Success HTML",
181
+ description="HTML shown on local callback success.",
182
+ )
183
+ plugin.add_option(
184
+ "oauth_fail_html",
185
+ type="textarea",
186
+ value="<html><body><h3>Authorization failed.</h3></body></html>",
187
+ label="Fail HTML",
188
+ description="HTML shown on local callback error.",
189
+ )
190
+ plugin.add_option(
191
+ "oauth_local_port",
192
+ type="int",
193
+ value=8731,
194
+ label="OAuth local port (0=auto)",
195
+ description="Local HTTP port for callback; use >1024. Must be registered in X App.",
196
+ )
197
+
198
+ plugin.add_option(
199
+ "oauth_allow_port_fallback",
200
+ type="bool",
201
+ value=True,
202
+ label="Allow fallback port if busy",
203
+ description="If preferred port is busy/forbidden, pick a free local port and rebuild the redirect URL.",
204
+ )
205
+
206
+ # ---------------- Commands ----------------
207
+
208
+ # Auth
209
+ plugin.add_cmd(
210
+ "x_oauth_begin",
211
+ instruction="Begin OAuth2 PKCE flow (returns authorize URL).",
212
+ params=[
213
+ {"name": "scopes", "type": "str", "required": False, "description": "Override scopes (space-separated)"},
214
+ {"name": "state", "type": "str", "required": False, "description": "Optional CSRF state"},
215
+ ],
216
+ enabled=True,
217
+ description="Auth: begin OAuth2",
218
+ tab="auth",
219
+ )
220
+ plugin.add_cmd(
221
+ "x_oauth_exchange",
222
+ instruction="Exchange authorization code for access/refresh tokens.",
223
+ params=[
224
+ {"name": "code", "type": "str", "required": True, "description": "Authorization code"},
225
+ {"name": "state", "type": "str", "required": False, "description": "State (if used)"},
226
+ ],
227
+ enabled=True,
228
+ description="Auth: exchange code",
229
+ tab="auth",
230
+ )
231
+ plugin.add_cmd(
232
+ "x_oauth_refresh",
233
+ instruction="Refresh access token using refresh_token.",
234
+ params=[],
235
+ enabled=True,
236
+ description="Auth: refresh token",
237
+ tab="auth",
238
+ )
239
+
240
+ # Users
241
+ plugin.add_cmd(
242
+ "x_me",
243
+ instruction="Get authorized user (requires user token).",
244
+ params=[
245
+ {"name": "user.fields", "type": "str", "required": False, "description": "e.g. created_at,verified,public_metrics"},
246
+ {"name": "expansions", "type": "str", "required": False, "description": "e.g. pinned_tweet_id"},
247
+ {"name": "tweet.fields", "type": "str", "required": False, "description": "fields of pinned tweet"},
248
+ ],
249
+ enabled=True,
250
+ description="Users: me",
251
+ tab="users",
252
+ )
253
+ plugin.add_cmd(
254
+ "x_user_by_username",
255
+ instruction="Lookup user by username.",
256
+ params=[
257
+ {"name": "username", "type": "str", "required": True, "description": "Handle without @"},
258
+ {"name": "user_fields", "type": "str", "required": False, "description": "Comma list"},
259
+ {"name": "expansions", "type": "str", "required": False, "description": "Comma list"},
260
+ {"name": "tweet_fields", "type": "str", "required": False, "description": "Comma list"},
261
+ ],
262
+ enabled=True,
263
+ description="Users: by username",
264
+ tab="users",
265
+ )
266
+ plugin.add_cmd(
267
+ "x_user_by_id",
268
+ instruction="Lookup user by ID.",
269
+ params=[
270
+ {"name": "id", "type": "str", "required": True, "description": "User ID"},
271
+ {"name": "user_fields", "type": "str", "required": False, "description": "Comma list"},
272
+ ],
273
+ enabled=True,
274
+ description="Users: by id",
275
+ tab="users",
276
+ )
277
+
278
+ # Timelines / Search
279
+ plugin.add_cmd(
280
+ "x_user_tweets",
281
+ instruction="User Tweet timeline.",
282
+ params=[
283
+ {"name": "id", "type": "str", "required": True, "description": "User ID"},
284
+ {"name": "max_results", "type": "int", "required": False, "description": "5..100 (default 20)"},
285
+ {"name": "since_id", "type": "str", "required": False, "description": "Since id"},
286
+ {"name": "until_id", "type": "str", "required": False, "description": "Until id"},
287
+ {"name": "exclude", "type": "list", "required": False, "description": "['replies','retweets']"},
288
+ {"name": "tweet_fields", "type": "str", "required": False, "description": "Comma list"},
289
+ {"name": "expansions", "type": "str", "required": False, "description": "Comma list"},
290
+ {"name": "media_fields", "type": "str", "required": False, "description": "Comma list"},
291
+ {"name": "pagination_token", "type": "str", "required": False, "description": "For next page"},
292
+ ],
293
+ enabled=True,
294
+ description="Timeline: user tweets",
295
+ tab="tweets",
296
+ )
297
+ plugin.add_cmd(
298
+ "x_search_recent",
299
+ instruction="Recent search (last 7 days).",
300
+ params=[
301
+ {"name": "query", "type": "str", "required": True, "description": "Query string with operators"},
302
+ {"name": "max_results", "type": "int", "required": False, "description": "10..100 (default 25)"},
303
+ {"name": "since_id", "type": "str", "required": False, "description": "Since id"},
304
+ {"name": "until_id", "type": "str", "required": False, "description": "Until id"},
305
+ {"name": "start_time", "type": "str", "required": False, "description": "RFC3339"},
306
+ {"name": "end_time", "type": "str", "required": False, "description": "RFC3339"},
307
+ {"name": "tweet_fields", "type": "str", "required": False, "description": "Comma list"},
308
+ {"name": "expansions", "type": "str", "required": False, "description": "Comma list"},
309
+ {"name": "media_fields", "type": "str", "required": False, "description": "Comma list"},
310
+ {"name": "next_token", "type": "str", "required": False, "description": "For next page"},
311
+ ],
312
+ enabled=True,
313
+ description="Search: recent",
314
+ tab="search",
315
+ )
316
+
317
+ # Tweet CRUD
318
+ plugin.add_cmd(
319
+ "x_tweet_create",
320
+ instruction="Create a Tweet/Post.",
321
+ params=[
322
+ {"name": "text", "type": "str", "required": False, "description": "Tweet text"},
323
+ {"name": "media_ids", "type": "list", "required": False, "description": "Uploaded media ids"},
324
+ {"name": "tagged_user_ids", "type": "list", "required": False, "description": "User IDs to tag"},
325
+ {"name": "quote_tweet_id", "type": "str", "required": False, "description": "Quote tweet id"},
326
+ {"name": "in_reply_to_tweet_id", "type": "str", "required": False, "description": "Reply to id"},
327
+ {"name": "exclude_reply_user_ids", "type": "list", "required": False, "description": "Exclude ids"},
328
+ {"name": "reply_settings", "type": "str", "required": False, "description": "everyone|following|mentionedUsers"},
329
+ {"name": "place_id", "type": "str", "required": False, "description": "Geo place id"},
330
+ {"name": "poll", "type": "dict", "required": False, "description": "duration_minutes, options"},
331
+ {"name": "card_uri", "type": "str", "required": False, "description": "Card URI"},
332
+ ],
333
+ enabled=True,
334
+ description="Tweets: create",
335
+ tab="tweets",
336
+ )
337
+ plugin.add_cmd(
338
+ "x_tweet_delete",
339
+ instruction="Delete Tweet by ID.",
340
+ params=[{"name": "id", "type": "str", "required": True, "description": "Tweet ID"}],
341
+ enabled=True,
342
+ description="Tweets: delete",
343
+ tab="tweets",
344
+ )
345
+ plugin.add_cmd(
346
+ "x_tweet_reply",
347
+ instruction="Reply to a Tweet.",
348
+ params=[
349
+ {"name": "in_reply_to_tweet_id", "type": "str", "required": True, "description": "Parent tweet id"},
350
+ {"name": "text", "type": "str", "required": False, "description": "Reply text"},
351
+ {"name": "media_ids", "type": "list", "required": False, "description": "Media ids"},
352
+ ],
353
+ enabled=True,
354
+ description="Tweets: reply",
355
+ tab="tweets",
356
+ )
357
+ plugin.add_cmd(
358
+ "x_tweet_quote",
359
+ instruction="Quote a Tweet.",
360
+ params=[
361
+ {"name": "quote_tweet_id", "type": "str", "required": True, "description": "Quoted tweet id"},
362
+ {"name": "text", "type": "str", "required": False, "description": "Quote text"},
363
+ {"name": "media_ids", "type": "list", "required": False, "description": "Media ids"},
364
+ ],
365
+ enabled=True,
366
+ description="Tweets: quote",
367
+ tab="tweets",
368
+ )
369
+
370
+ # Actions
371
+ plugin.add_cmd(
372
+ "x_like",
373
+ instruction="Like a Tweet.",
374
+ params=[
375
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Tweet id"},
376
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
377
+ ],
378
+ enabled=True,
379
+ description="Actions: like",
380
+ tab="actions",
381
+ )
382
+ plugin.add_cmd(
383
+ "x_unlike",
384
+ instruction="Unlike a Tweet.",
385
+ params=[
386
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Tweet id"},
387
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
388
+ ],
389
+ enabled=True,
390
+ description="Actions: unlike",
391
+ tab="actions",
392
+ )
393
+ plugin.add_cmd(
394
+ "x_retweet",
395
+ instruction="Retweet.",
396
+ params=[
397
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Tweet id"},
398
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
399
+ ],
400
+ enabled=True,
401
+ description="Actions: retweet",
402
+ tab="actions",
403
+ )
404
+ plugin.add_cmd(
405
+ "x_unretweet",
406
+ instruction="Undo retweet.",
407
+ params=[
408
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Original tweet id"},
409
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
410
+ ],
411
+ enabled=True,
412
+ description="Actions: unretweet",
413
+ tab="actions",
414
+ )
415
+ plugin.add_cmd(
416
+ "x_hide_reply",
417
+ instruction="Hide or unhide a reply to your Tweet.",
418
+ params=[
419
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Reply tweet id"},
420
+ {"name": "hidden", "type": "bool", "required": False, "description": "Default true"},
421
+ ],
422
+ enabled=True,
423
+ description="Actions: hide/unhide reply",
424
+ tab="actions",
425
+ )
426
+
427
+ # Bookmarks
428
+ plugin.add_cmd(
429
+ "x_bookmarks_list",
430
+ instruction="List my bookmarks.",
431
+ params=[
432
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
433
+ {"name": "max_results", "type": "int", "required": False, "description": "Default 50"},
434
+ {"name": "pagination_token", "type": "str", "required": False, "description": "Next page token"},
435
+ {"name": "tweet_fields", "type": "str", "required": False, "description": "Comma list"},
436
+ {"name": "expansions", "type": "str", "required": False, "description": "Comma list"},
437
+ {"name": "media_fields", "type": "str", "required": False, "description": "Comma list"},
438
+ ],
439
+ enabled=True,
440
+ description="Bookmarks: list",
441
+ tab="bookmarks",
442
+ )
443
+ plugin.add_cmd(
444
+ "x_bookmark_add",
445
+ instruction="Add bookmark.",
446
+ params=[
447
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Tweet id"},
448
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
449
+ ],
450
+ enabled=True,
451
+ description="Bookmarks: add",
452
+ tab="bookmarks",
453
+ )
454
+ plugin.add_cmd(
455
+ "x_bookmark_remove",
456
+ instruction="Remove bookmark.",
457
+ params=[
458
+ {"name": "tweet_id", "type": "str", "required": True, "description": "Tweet id"},
459
+ {"name": "user_id", "type": "str", "required": False, "description": "Defaults to me"},
460
+ ],
461
+ enabled=True,
462
+ description="Bookmarks: remove",
463
+ tab="bookmarks",
464
+ )
465
+
466
+ # Media
467
+ plugin.add_cmd(
468
+ "x_upload_media",
469
+ instruction="Upload media (INIT/APPEND/FINALIZE). Returns media_id.",
470
+ params=[
471
+ {"name": "path", "type": "str", "required": True, "description": "Local file path"},
472
+ {"name": "media_type", "type": "str", "required": False, "description": "Mime, auto-guessed"},
473
+ {"name": "media_category", "type": "str", "required": False, "description": "tweet_image|tweet_video|tweet_gif"},
474
+ {"name": "chunk_size", "type": "int", "required": False, "description": "Bytes, default 1MB"},
475
+ {"name": "wait_for_processing", "type": "bool", "required": False, "description": "Poll STATUS if needed"},
476
+ ],
477
+ enabled=True,
478
+ description="Media: upload",
479
+ tab="media",
480
+ )
481
+ plugin.add_cmd(
482
+ "x_media_set_alt_text",
483
+ instruction="Attach alt text (accessibility) to uploaded media.",
484
+ params=[
485
+ {"name": "media_id", "type": "str", "required": True, "description": "Media id from upload"},
486
+ {"name": "alt_text", "type": "str", "required": True, "description": "Up to 1000 chars"},
487
+ ],
488
+ enabled=True,
489
+ description="Media: set alt text",
490
+ tab="media",
491
+ )
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.07.14 00:00:00 #
10
+ # ================================================== #
11
+
12
+ import os
13
+
14
+ from pygpt_net.plugin.base.plugin import BasePlugin
15
+ from pygpt_net.core.events import Event
16
+ from pygpt_net.item.ctx import CtxItem
17
+
18
+ from .config import Config
19
+ from .worker import Worker
20
+
21
+
22
+ class Plugin(BasePlugin):
23
+ def __init__(self, *args, **kwargs):
24
+ super(Plugin, self).__init__(*args, **kwargs)
25
+ self.id = "twitter"
26
+ self.name = "Twitter/X"
27
+ self.description = "Interact with tweets and users, manage bookmarks and media, perform likes, retweets, and more."
28
+ self.prefix = "API"
29
+ self.order = 100
30
+ self.allowed_cmds = [
31
+ 'x_oauth_begin',
32
+ 'x_oauth_exchange',
33
+ 'x_oauth_refresh',
34
+ 'x_me',
35
+ 'x_user_by_username',
36
+ 'x_user_by_id',
37
+ 'x_user_tweets',
38
+ 'x_search_recent',
39
+ 'x_tweet_create',
40
+ 'x_tweet_delete',
41
+ 'x_tweet_reply',
42
+ 'x_tweet_quote',
43
+ 'x_like',
44
+ 'x_unlike',
45
+ 'x_retweet',
46
+ 'x_unretweet',
47
+ 'x_hide_reply',
48
+ 'x_bookmarks_list',
49
+ 'x_bookmark_add',
50
+ 'x_bookmark_remove',
51
+ 'x_upload_media',
52
+ 'x_media_set_alt_text'
53
+ ]
54
+ self.use_locale = False
55
+ self.worker = None
56
+ self.config = Config(self)
57
+ self.init_options()
58
+
59
+ def init_options(self):
60
+ """Initialize options"""
61
+ self.config.from_defaults(self)
62
+
63
+ def handle(self, event: Event, *args, **kwargs):
64
+ """
65
+ Handle dispatched event
66
+
67
+ :param event: event object
68
+ :param args: event args
69
+ :param kwargs: event kwargs
70
+ """
71
+ name = event.name
72
+ data = event.data
73
+ ctx = event.ctx
74
+
75
+ if name == Event.CMD_SYNTAX:
76
+ self.cmd_syntax(data)
77
+
78
+ elif name == Event.CMD_EXECUTE:
79
+ self.cmd(
80
+ ctx,
81
+ data['commands'],
82
+ )
83
+
84
+ def cmd_syntax(self, data: dict):
85
+ """
86
+ Event: CMD_SYNTAX
87
+
88
+ :param data: event data dict
89
+ """
90
+ for option in self.allowed_cmds:
91
+ if self.has_cmd(option):
92
+ data['cmd'].append(self.get_cmd(option)) # append command
93
+
94
+ def cmd(self, ctx: CtxItem, cmds: list):
95
+ """
96
+ Event: CMD_EXECUTE
97
+
98
+ :param ctx: CtxItem
99
+ :param cmds: commands dict
100
+ """
101
+ is_cmd = False
102
+ my_commands = []
103
+ for item in cmds:
104
+ if item["cmd"] in self.allowed_cmds:
105
+ my_commands.append(item)
106
+ is_cmd = True
107
+
108
+ if not is_cmd:
109
+ return
110
+
111
+ # set state: busy
112
+ self.cmd_prepare(ctx, my_commands)
113
+
114
+ try:
115
+ worker = Worker()
116
+ worker.from_defaults(self)
117
+ worker.cmds = my_commands
118
+ worker.ctx = ctx
119
+
120
+ if not self.is_async(ctx):
121
+ worker.run()
122
+ return
123
+ worker.run_async()
124
+
125
+ except Exception as e:
126
+ self.error(e)