pygpt-net 2.6.1__py3-none-any.whl → 2.6.6__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 (131) hide show
  1. pygpt_net/CHANGELOG.txt +23 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +20 -1
  4. pygpt_net/config.py +55 -65
  5. pygpt_net/controller/__init__.py +5 -2
  6. pygpt_net/controller/calendar/note.py +101 -126
  7. pygpt_net/controller/chat/chat.py +38 -35
  8. pygpt_net/controller/chat/render.py +154 -214
  9. pygpt_net/controller/chat/response.py +5 -3
  10. pygpt_net/controller/chat/stream.py +92 -27
  11. pygpt_net/controller/config/config.py +39 -42
  12. pygpt_net/controller/config/field/checkbox.py +16 -12
  13. pygpt_net/controller/config/field/checkbox_list.py +36 -31
  14. pygpt_net/controller/config/field/cmd.py +51 -57
  15. pygpt_net/controller/config/field/combo.py +33 -16
  16. pygpt_net/controller/config/field/dictionary.py +48 -55
  17. pygpt_net/controller/config/field/input.py +50 -32
  18. pygpt_net/controller/config/field/slider.py +40 -45
  19. pygpt_net/controller/config/field/textarea.py +20 -6
  20. pygpt_net/controller/config/placeholder.py +110 -231
  21. pygpt_net/controller/ctx/common.py +48 -48
  22. pygpt_net/controller/ctx/ctx.py +91 -132
  23. pygpt_net/controller/lang/mapping.py +57 -95
  24. pygpt_net/controller/lang/plugins.py +64 -55
  25. pygpt_net/controller/lang/settings.py +39 -38
  26. pygpt_net/controller/layout/layout.py +176 -109
  27. pygpt_net/controller/mode/mode.py +88 -85
  28. pygpt_net/controller/model/model.py +73 -73
  29. pygpt_net/controller/plugins/plugins.py +209 -223
  30. pygpt_net/controller/plugins/presets.py +54 -55
  31. pygpt_net/controller/plugins/settings.py +54 -69
  32. pygpt_net/controller/presets/editor.py +33 -88
  33. pygpt_net/controller/presets/experts.py +20 -1
  34. pygpt_net/controller/presets/presets.py +293 -298
  35. pygpt_net/controller/settings/profile.py +16 -4
  36. pygpt_net/controller/theme/theme.py +72 -81
  37. pygpt_net/controller/ui/mode.py +118 -186
  38. pygpt_net/controller/ui/tabs.py +69 -90
  39. pygpt_net/controller/ui/ui.py +47 -56
  40. pygpt_net/controller/ui/vision.py +24 -23
  41. pygpt_net/core/agents/runner.py +15 -7
  42. pygpt_net/core/bridge/bridge.py +5 -5
  43. pygpt_net/core/command/command.py +149 -219
  44. pygpt_net/core/ctx/ctx.py +94 -146
  45. pygpt_net/core/debug/debug.py +48 -58
  46. pygpt_net/core/experts/experts.py +3 -3
  47. pygpt_net/core/models/models.py +74 -112
  48. pygpt_net/core/modes/modes.py +13 -21
  49. pygpt_net/core/plugins/plugins.py +154 -177
  50. pygpt_net/core/presets/presets.py +103 -176
  51. pygpt_net/core/render/web/body.py +217 -215
  52. pygpt_net/core/render/web/renderer.py +330 -474
  53. pygpt_net/core/text/utils.py +28 -44
  54. pygpt_net/core/tokens/tokens.py +104 -203
  55. pygpt_net/data/config/config.json +3 -3
  56. pygpt_net/data/config/models.json +3 -3
  57. pygpt_net/data/locale/locale.de.ini +2 -0
  58. pygpt_net/data/locale/locale.en.ini +2 -0
  59. pygpt_net/data/locale/locale.es.ini +2 -0
  60. pygpt_net/data/locale/locale.fr.ini +2 -0
  61. pygpt_net/data/locale/locale.it.ini +2 -0
  62. pygpt_net/data/locale/locale.pl.ini +3 -1
  63. pygpt_net/data/locale/locale.uk.ini +2 -0
  64. pygpt_net/data/locale/locale.zh.ini +2 -0
  65. pygpt_net/item/ctx.py +141 -139
  66. pygpt_net/plugin/agent/plugin.py +2 -1
  67. pygpt_net/plugin/audio_output/plugin.py +5 -2
  68. pygpt_net/plugin/base/plugin.py +101 -85
  69. pygpt_net/plugin/bitbucket/__init__.py +12 -0
  70. pygpt_net/plugin/bitbucket/config.py +267 -0
  71. pygpt_net/plugin/bitbucket/plugin.py +126 -0
  72. pygpt_net/plugin/bitbucket/worker.py +569 -0
  73. pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
  74. pygpt_net/plugin/cmd_custom/plugin.py +3 -2
  75. pygpt_net/plugin/cmd_files/plugin.py +3 -2
  76. pygpt_net/plugin/cmd_history/plugin.py +3 -2
  77. pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
  78. pygpt_net/plugin/cmd_serial/plugin.py +3 -2
  79. pygpt_net/plugin/cmd_system/plugin.py +3 -6
  80. pygpt_net/plugin/cmd_web/plugin.py +3 -2
  81. pygpt_net/plugin/experts/plugin.py +2 -2
  82. pygpt_net/plugin/facebook/__init__.py +12 -0
  83. pygpt_net/plugin/facebook/config.py +359 -0
  84. pygpt_net/plugin/facebook/plugin.py +113 -0
  85. pygpt_net/plugin/facebook/worker.py +698 -0
  86. pygpt_net/plugin/github/__init__.py +12 -0
  87. pygpt_net/plugin/github/config.py +441 -0
  88. pygpt_net/plugin/github/plugin.py +126 -0
  89. pygpt_net/plugin/github/worker.py +674 -0
  90. pygpt_net/plugin/google/__init__.py +12 -0
  91. pygpt_net/plugin/google/config.py +367 -0
  92. pygpt_net/plugin/google/plugin.py +126 -0
  93. pygpt_net/plugin/google/worker.py +826 -0
  94. pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
  95. pygpt_net/plugin/mailer/plugin.py +3 -5
  96. pygpt_net/plugin/openai_vision/plugin.py +3 -2
  97. pygpt_net/plugin/real_time/plugin.py +52 -60
  98. pygpt_net/plugin/slack/__init__.py +12 -0
  99. pygpt_net/plugin/slack/config.py +349 -0
  100. pygpt_net/plugin/slack/plugin.py +115 -0
  101. pygpt_net/plugin/slack/worker.py +639 -0
  102. pygpt_net/plugin/telegram/__init__.py +12 -0
  103. pygpt_net/plugin/telegram/config.py +308 -0
  104. pygpt_net/plugin/telegram/plugin.py +117 -0
  105. pygpt_net/plugin/telegram/worker.py +563 -0
  106. pygpt_net/plugin/twitter/__init__.py +12 -0
  107. pygpt_net/plugin/twitter/config.py +491 -0
  108. pygpt_net/plugin/twitter/plugin.py +125 -0
  109. pygpt_net/plugin/twitter/worker.py +837 -0
  110. pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
  111. pygpt_net/tools/code_interpreter/tool.py +0 -1
  112. pygpt_net/tools/translator/tool.py +1 -1
  113. pygpt_net/ui/base/config_dialog.py +86 -100
  114. pygpt_net/ui/base/context_menu.py +48 -46
  115. pygpt_net/ui/dialog/preset.py +34 -77
  116. pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
  117. pygpt_net/ui/layout/toolbox/presets.py +41 -41
  118. pygpt_net/ui/main.py +49 -31
  119. pygpt_net/ui/tray.py +61 -60
  120. pygpt_net/ui/widget/calendar/select.py +86 -70
  121. pygpt_net/ui/widget/lists/attachment.py +86 -44
  122. pygpt_net/ui/widget/lists/base_list_combo.py +85 -33
  123. pygpt_net/ui/widget/lists/context.py +135 -188
  124. pygpt_net/ui/widget/lists/preset.py +59 -61
  125. pygpt_net/ui/widget/textarea/web.py +161 -48
  126. pygpt_net/utils.py +8 -1
  127. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +164 -2
  128. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +131 -103
  129. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
  130. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
  131. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.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,125 @@
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.15 23:00:00 #
10
+ # ================================================== #
11
+
12
+ from pygpt_net.plugin.base.plugin import BasePlugin
13
+ from pygpt_net.core.events import Event
14
+ from pygpt_net.item.ctx import CtxItem
15
+
16
+ from .config import Config
17
+
18
+
19
+ class Plugin(BasePlugin):
20
+ def __init__(self, *args, **kwargs):
21
+ super(Plugin, self).__init__(*args, **kwargs)
22
+ self.id = "twitter"
23
+ self.name = "Twitter/X"
24
+ self.description = "Interact with tweets and users, manage bookmarks and media, perform likes, retweets, and more."
25
+ self.prefix = "API"
26
+ self.order = 100
27
+ self.allowed_cmds = [
28
+ 'x_oauth_begin',
29
+ 'x_oauth_exchange',
30
+ 'x_oauth_refresh',
31
+ 'x_me',
32
+ 'x_user_by_username',
33
+ 'x_user_by_id',
34
+ 'x_user_tweets',
35
+ 'x_search_recent',
36
+ 'x_tweet_create',
37
+ 'x_tweet_delete',
38
+ 'x_tweet_reply',
39
+ 'x_tweet_quote',
40
+ 'x_like',
41
+ 'x_unlike',
42
+ 'x_retweet',
43
+ 'x_unretweet',
44
+ 'x_hide_reply',
45
+ 'x_bookmarks_list',
46
+ 'x_bookmark_add',
47
+ 'x_bookmark_remove',
48
+ 'x_upload_media',
49
+ 'x_media_set_alt_text'
50
+ ]
51
+ self.use_locale = False
52
+ self.worker = None
53
+ self.config = Config(self)
54
+ self.init_options()
55
+
56
+ def init_options(self):
57
+ """Initialize options"""
58
+ self.config.from_defaults(self)
59
+
60
+ def handle(self, event: Event, *args, **kwargs):
61
+ """
62
+ Handle dispatched event
63
+
64
+ :param event: event object
65
+ :param args: event args
66
+ :param kwargs: event kwargs
67
+ """
68
+ name = event.name
69
+ data = event.data
70
+ ctx = event.ctx
71
+
72
+ if name == Event.CMD_SYNTAX:
73
+ self.cmd_syntax(data)
74
+
75
+ elif name == Event.CMD_EXECUTE:
76
+ self.cmd(
77
+ ctx,
78
+ data['commands'],
79
+ )
80
+
81
+ def cmd_syntax(self, data: dict):
82
+ """
83
+ Event: CMD_SYNTAX
84
+
85
+ :param data: event data dict
86
+ """
87
+ for option in self.allowed_cmds:
88
+ if self.has_cmd(option):
89
+ data['cmd'].append(self.get_cmd(option)) # append command
90
+
91
+ def cmd(self, ctx: CtxItem, cmds: list):
92
+ """
93
+ Event: CMD_EXECUTE
94
+
95
+ :param ctx: CtxItem
96
+ :param cmds: commands dict
97
+ """
98
+ from .worker import Worker
99
+
100
+ is_cmd = False
101
+ my_commands = []
102
+ for item in cmds:
103
+ if item["cmd"] in self.allowed_cmds:
104
+ my_commands.append(item)
105
+ is_cmd = True
106
+
107
+ if not is_cmd:
108
+ return
109
+
110
+ # set state: busy
111
+ self.cmd_prepare(ctx, my_commands)
112
+
113
+ try:
114
+ worker = Worker()
115
+ worker.from_defaults(self)
116
+ worker.cmds = my_commands
117
+ worker.ctx = ctx
118
+
119
+ if not self.is_async(ctx):
120
+ worker.run()
121
+ return
122
+ worker.run_async()
123
+
124
+ except Exception as e:
125
+ self.error(e)