xselfbot 4.0.0__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.
xselfbot/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """
2
+ =========
3
+ XSelfBot
4
+ =========
5
+
6
+ A Python library for interacting with x.com: posting, reading, searching and
7
+ streaming over a cookie session, with no API key.
8
+
9
+ https://github.com/Tky567/xselfbot
10
+ """
11
+
12
+ __version__ = '4.0.0'
13
+
14
+ import asyncio
15
+ import os
16
+
17
+ if os.name == 'nt':
18
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
19
+
20
+ from ._captcha import Capsolver
21
+ from .bookmark import BookmarkFolder
22
+ from .errors import *
23
+ from .utils import build_query
24
+ from .client.client import Client
25
+ from .community import Community, CommunityCreator, CommunityMember, CommunityRule
26
+ from .geo import Place
27
+ from .group import Group, GroupMessage
28
+ from .list import List
29
+ from .message import Message
30
+ from .notification import Notification
31
+ from .trend import Trend
32
+ from .tweet import CommunityNote, Poll, ScheduledTweet, Tweet
33
+ from .content_disclosure import build_content_disclosure, applied_disclosures
34
+ from .user import User
@@ -0,0 +1,2 @@
1
+ from .base import CaptchaSolver
2
+ from .capsolver import Capsolver
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import TYPE_CHECKING, NamedTuple
5
+
6
+ from bs4 import BeautifulSoup
7
+ from httpx import Response
8
+ from ..constants import DOMAIN
9
+
10
+ if TYPE_CHECKING:
11
+ from ..client.client import Client
12
+
13
+
14
+ class UnlockHTML(NamedTuple):
15
+ authenticity_token: str
16
+ assignment_token: str
17
+ needs_unlock: bool
18
+ start_button: bool
19
+ finish_button: bool
20
+ delete_button: bool
21
+ blob: str
22
+
23
+
24
+ class CaptchaSolver:
25
+ client: Client
26
+ max_attempts: int
27
+
28
+ CAPTCHA_URL = f'https://{DOMAIN}/account/access'
29
+ CAPTCHA_SITE_KEY = '0152B4EB-D2DC-460A-89A1-629838B529C9'
30
+
31
+ async def get_unlock_html(self) -> tuple[Response, UnlockHTML]:
32
+ headers = {
33
+ 'X-Twitter-Client-Language': 'en-US',
34
+ 'User-Agent': self.client._user_agent,
35
+ 'Upgrade-Insecure-Requests': '1'
36
+ }
37
+ _, response = await self.client.get(
38
+ self.CAPTCHA_URL, headers=headers
39
+ )
40
+ return response, parse_unlock_html(response.text)
41
+
42
+ async def ui_metrix(self) -> str:
43
+ js, _ = await self.client.get(
44
+ f'https://{DOMAIN}/i/js_inst?c_name=ui_metrics'
45
+ )
46
+ return re.findall(r'return ({.*?});', js, re.DOTALL)[0]
47
+
48
+ async def confirm_unlock(
49
+ self,
50
+ authenticity_token: str,
51
+ assignment_token: str,
52
+ verification_string: str = None,
53
+ ui_metrics: bool = False
54
+ ) -> tuple[Response, UnlockHTML]:
55
+ data = {
56
+ 'authenticity_token': authenticity_token,
57
+ 'assignment_token': assignment_token,
58
+ 'lang': 'en',
59
+ 'flow': '',
60
+ }
61
+ params = {}
62
+ if verification_string:
63
+ data['verification_string'] = verification_string
64
+ data['language_code'] = 'en'
65
+ params['lang'] = 'en'
66
+ if ui_metrics:
67
+ data['ui_metrics'] = await self.client._ui_metrix()
68
+ headers = {
69
+ 'Content-Type': 'application/x-www-form-urlencoded',
70
+ 'Upgrade-Insecure-Requests': '1',
71
+ 'Referer': self.CAPTCHA_URL
72
+ }
73
+ _, response = await self.client.post(
74
+ self.CAPTCHA_URL, params=params, data=data, headers=headers
75
+ )
76
+ return response, parse_unlock_html(response.text)
77
+
78
+
79
+ def parse_unlock_html(html: str) -> UnlockHTML:
80
+ soup = BeautifulSoup(html, 'lxml')
81
+
82
+ authenticity_token = None
83
+ authenticity_token_element = soup.find(
84
+ 'input', {'name': 'authenticity_token'}
85
+ )
86
+ if authenticity_token_element is not None:
87
+ authenticity_token: str = authenticity_token_element.get('value')
88
+
89
+ assignment_token = None
90
+ assignment_token_element = soup.find('input', {'name': 'assignment_token'})
91
+ if assignment_token_element is not None:
92
+ assignment_token = assignment_token_element.get('value')
93
+
94
+ verification_string = soup.find('input', id='verification_string')
95
+ needs_unlock = bool(verification_string)
96
+ start_button = bool(soup.find('input', value='Start'))
97
+ finish_button = bool(soup.find('input', value='Continue to X'))
98
+ delete_button = bool(soup.find('input', value='Delete'))
99
+
100
+ iframe = soup.find(id='arkose_iframe')
101
+ blob = re.findall(r'data=(.+)', iframe['src'])[0] if iframe else None
102
+
103
+ return UnlockHTML(
104
+ authenticity_token,
105
+ assignment_token,
106
+ needs_unlock,
107
+ start_button,
108
+ finish_button,
109
+ delete_button,
110
+ blob
111
+ )
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ from time import sleep
4
+
5
+ import httpx
6
+
7
+ from .base import CaptchaSolver
8
+
9
+
10
+ class Capsolver(CaptchaSolver):
11
+ """
12
+ You can automatically unlock the account by passing the `captcha_solver`
13
+ argument when initialising the :class:`.Client`.
14
+
15
+ First, visit https://capsolver.com and obtain your Capsolver API key.
16
+ Next, pass the Capsolver instance to the client as shown in the example.
17
+
18
+ .. code-block:: python
19
+
20
+ from xselfbot import Capsolver, Client
21
+ solver = Capsolver(
22
+ api_key='your_api_key',
23
+ max_attempts=10
24
+ )
25
+ client = Client(captcha_solver=solver)
26
+
27
+ Parameters
28
+ ----------
29
+ api_key : :class:`str`
30
+ Capsolver API key.
31
+ max_attempts : :class:`int`, default=3
32
+ The maximum number of attempts to solve the captcha.
33
+ get_result_interval : :class:`float`, default=1.0
34
+
35
+ use_blob_data : :class:`bool`, default=False
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ api_key: str,
41
+ max_attempts: int = 3,
42
+ get_result_interval: float = 1.0,
43
+ use_blob_data: bool = False
44
+ ) -> None:
45
+ self.api_key = api_key
46
+ self.get_result_interval = get_result_interval
47
+ self.max_attempts = max_attempts
48
+ self.use_blob_data = use_blob_data
49
+
50
+ def create_task(self, task_data: dict) -> dict:
51
+ data = {
52
+ 'clientKey': self.api_key,
53
+ 'task': task_data
54
+ }
55
+ response = httpx.post(
56
+ 'https://api.capsolver.com/createTask',
57
+ json=data,
58
+ headers={'content-type': 'application/json'}
59
+ ).json()
60
+ return response
61
+
62
+ def get_task_result(self, task_id: str) -> dict:
63
+ data = {
64
+ 'clientKey': self.api_key,
65
+ 'taskId': task_id
66
+ }
67
+ response = httpx.post(
68
+ 'https://api.capsolver.com/getTaskResult',
69
+ json=data,
70
+ headers={'content-type': 'application/json'}
71
+ ).json()
72
+ return response
73
+
74
+ def solve_funcaptcha(self, blob: str) -> dict:
75
+ if self.client.proxy is None:
76
+ captcha_type = 'FunCaptchaTaskProxyLess'
77
+ else:
78
+ captcha_type = 'FunCaptchaTask'
79
+
80
+ task_data = {
81
+ 'type': captcha_type,
82
+ 'websiteURL': 'https://iframe.arkoselabs.com',
83
+ 'websitePublicKey': self.CAPTCHA_SITE_KEY,
84
+ 'funcaptchaApiJSSubdomain': 'https://client-api.arkoselabs.com',
85
+ 'proxy': self.client.proxy
86
+ }
87
+ if self.use_blob_data:
88
+ task_data['data'] = '{"blob":"%s"}' % blob
89
+ task_data['userAgent'] = self.client._user_agent
90
+ task = self.create_task(task_data)
91
+ while True:
92
+ sleep(self.get_result_interval)
93
+ result = self.get_task_result(task['taskId'])
94
+ if result['status'] in ('ready', 'failed'):
95
+ return result
xselfbot/bookmark.py ADDED
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from httpx import Response
7
+
8
+ from .client.client import Client
9
+ from .tweet import Tweet
10
+ from .utils import Result
11
+
12
+
13
+ class BookmarkFolder:
14
+ """
15
+ Attributes
16
+ ----------
17
+ id : :class:`str`
18
+ The ID of the folder.
19
+ name : :class:`str`
20
+ The name of the folder
21
+ media : :class:`str`
22
+ Icon image data.
23
+ """
24
+ def __init__(self, client: Client, data: dict) -> None:
25
+ self._client = client
26
+
27
+ self.id: str = data['id']
28
+ self.name: str = data['name']
29
+ self.media: dict = data['media']
30
+
31
+ async def get_tweets(self, cursor: str | None = None) -> Result[Tweet]:
32
+ """
33
+ Retrieves tweets from the folder.
34
+ """
35
+ return await self._client.get_bookmarks(
36
+ cursor=cursor, folder_id=self.id
37
+ )
38
+
39
+ async def edit(self, name: str) -> BookmarkFolder:
40
+ """
41
+ Edits the folder.
42
+ """
43
+ return await self._client.edit_bookmark_folder(self.id, name)
44
+
45
+ async def delete(self) -> Response:
46
+ """
47
+ Deletes the folder.
48
+ """
49
+ return await self._client.delete_bookmark_folder(self.id)
50
+
51
+ async def add(self, tweet_id: str) -> Response:
52
+ """
53
+ Adds a tweet to the folder.
54
+ """
55
+ return await self._client.bookmark_tweet(tweet_id, self.id)
56
+
57
+ def __eq__(self, __value: object) -> bool:
58
+ return isinstance(__value, BookmarkFolder) and self.id == __value.id
59
+
60
+ def __ne__(self, __value: object) -> bool:
61
+ return not self == __value
62
+
63
+ def __repr__(self) -> str:
64
+ return f'<BookmarkFolder id="{self.id}">'