tweepy-self 0.1.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.1
2
+ Name: tweepy-self
3
+ Version: 1.0.0
4
+ Summary: Twitter (selfbot) for Python!
5
+ Author: Alen
6
+ Author-email: alen.kimov@gmail.com
7
+ Requires-Python: >=3.11,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Requires-Dist: beautifulsoup4 (>=4,<5)
12
+ Requires-Dist: better-proxy (==0.5.0)
13
+ Requires-Dist: curl_cffi (==0.6.0b9)
14
+ Requires-Dist: lxml (>=5,<6)
15
+ Requires-Dist: pydantic (>=2,<3)
16
+ Requires-Dist: pyotp (>=2,<3)
17
+ Requires-Dist: python3-capsolver (>=0.9,<0.10)
18
+ Requires-Dist: yarl (>=1,<2)
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Tweepy-self
22
+ [![Telegram channel](https://img.shields.io/endpoint?url=https://runkit.io/damiankrawczyk/telegram-badge/branches/master?url=https://t.me/cum_insider)](https://t.me/cum_insider)
23
+ [![PyPI version info](https://img.shields.io/pypi/v/tweepy-self.svg)](https://pypi.python.org/pypi/tweepy-self)
24
+ [![PyPI supported Python versions](https://img.shields.io/pypi/pyversions/tweepy-self.svg)](https://pypi.python.org/pypi/tweepy-self)
25
+
26
+ A modern, easy to use, feature-rich, and async ready API wrapper for Twitter's user API written in Python.
27
+
28
+ - Docs (soon)
29
+
30
+ More libraries of the family:
31
+ - [better-web3](https://github.com/alenkimov/better_web3)
32
+ - [better-proxy](https://github.com/alenkimov/better_proxy)
33
+ - [better-automation](https://github.com/alenkimov/better_automation)
34
+
35
+ Отдельное спасибо [Кузнице Ботов](https://t.me/bots_forge) за код для авторизации и разморозки! Подписывайтесь на их Telegram :)
36
+
37
+ ## Key Features
38
+ - Modern Pythonic API using async and await.
39
+ - Prevents user account automation detection.
40
+
41
+ ## Installing
42
+ ```bash
43
+ pip install tweepy-self
44
+ ```
45
+
46
+ ## Example
47
+ ```python
48
+ import asyncio
49
+ import twitter
50
+
51
+ account = twitter.Account(auth_token="auth_token")
52
+
53
+ async def main():
54
+ async with twitter.Client(account) as twitter_client:
55
+ await twitter_client.tweet("Hello, tweepy-self! <3")
56
+
57
+ asyncio.run(main())
58
+ ```
59
+
60
+ ## More
61
+ Automating user accounts is against the Twitter ToS. This library is a proof of concept and I cannot recommend using it. Do so at your own risk
62
+
63
+ ## Документация (устаревшая)
64
+ `Код ушел немного дальше, чем эта документация.`
65
+
66
+ Библиотека позволяет работать с неофициальным API Twitter, а именно:
67
+ - Логин
68
+ - Анлок
69
+ - Привязывать сервисы (приложения).
70
+ - Устанавливать статус аккаунта (бан, лок).
71
+ - Загружать изображения на сервер и изменять баннер и аватарку.
72
+ - Изменять данные о пользователе: имя, описание профиля и другое.
73
+ - Изменять имя пользователя и пароль.
74
+ - Запрашивать информацию о подписчиках.
75
+ - Запрашивать некоторую информацию о пользователе (количество подписчиков и другое).
76
+ - Голосовать.
77
+ - Подписываться и отписываться.
78
+ - Лайкать и дизлайкать.
79
+ - Твиттить, ретвиттить с изображением и без.
80
+ - Закреплять твиты.
81
+ - Запрашивать твиты пользователей.
82
+ - Удалять твиты.
83
+ - И другое.
84
+
85
+ #### Статус аккаунта
86
+ После любого взаимодействия с Twitter устанавливается статус аккаунта:
87
+ - `BAD_TOKEN` - Неверный токен.
88
+ - `UNKNOWN` - Статус аккаунта не установлен.
89
+ - `SUSPENDED` - Действие учетной записи приостановлено (бан).
90
+ - `LOCKED` - Учетная запись заморожена (лок) (требуется прохождение капчи).
91
+ - `GOOD` - Аккаунт в порядке.
92
+
93
+ Не каждое взаимодействие с Twitter достоверно определяет статус аккаунта.
94
+ Например, простой запрос данных об аккаунте честно вернет данные, даже если ваш аккаунт заморожен.
95
+
96
+ Для достоверной установки статуса аккаунта используйте метод `establish_status()`
97
+
98
+ ### Примеры работы
99
+ Запрос информации о пользователе:
100
+ ```python
101
+ # Запрос информации о текущем пользователе:
102
+ me = await twitter_client.request_user_data()
103
+ print(f"[{account.short_auth_token}] {me}")
104
+ print(f"Аккаунт создан: {me.created_at}")
105
+ print(f"Following (подписан ты): {me.followings_count}")
106
+ print(f"Followers (подписаны на тебя): {me.followers_count}")
107
+ print(f"Прочая информация: {me.raw_data}")
108
+
109
+ # Запрос информации об ином пользователе:
110
+ elonmusk = await twitter.request_user_data("@elonmusk")
111
+ print(elonmusk)
112
+ ```
113
+
114
+ Смена имени пользователя и пароля:
115
+ ```python
116
+ account = twitter.Account("auth_token", password="password")
117
+ ...
118
+ await twitter_client.change_username("new_username")
119
+ await twitter_client.request_user_data()
120
+ print(f"New username: {account.data.username}")
121
+
122
+ await twitter_client.change_password("new_password")
123
+ print(f"New password: {account.password}")
124
+ print(f"New auth_token: {account.auth_token}")
125
+ ```
126
+
127
+ Смена данных профиля:
128
+ ```python
129
+ await twitter_client.update_birthdate(day=1, month=12, year=2000)
130
+ await twitter_client.update_profile( # Locks account!
131
+ name="New Name",
132
+ description="New description",
133
+ location="New York",
134
+ website="https://github.com/alenkimov/better_automation",
135
+ )
136
+ ```
137
+
138
+ Загрузка изображений и смена аватара и баннера:
139
+ ```python
140
+ image = open(f"image.png", "rb").read()
141
+ media_id = await twitter_client.upload_image(image)
142
+ avatar_image_url = await twitter_client.update_profile_avatar(media_id)
143
+ banner_image_url = await twitter_client.update_profile_banner(media_id)
144
+ ```
145
+
146
+ Привязка сервиса (приложения):
147
+
148
+ ```python
149
+ # Изучите запросы сервиса и найдите подобные данные для авторизации (привязки):
150
+ bind_data = {
151
+ 'response_type': 'code',
152
+ 'client_id': 'TjFVQm52ZDFGWEtNT0tKaktaSWU6MTpjaQ',
153
+ 'redirect_uri': 'https://waitlist.lens.xyz/tw/',
154
+ 'scope': 'users.read tweet.read offline.access',
155
+ 'state': 'state', # Может быть как статичным, так и динамическим.
156
+ 'code_challenge': 'challenge',
157
+ 'code_challenge_method': 'plain'
158
+ }
159
+
160
+ bind_code = await twitter_client.oauth_2(**bind_data)
161
+ # Передайте код авторизации (привязки) сервису.
162
+ # Сервис также может потребовать state, если он динамический.
163
+ ```
164
+
165
+ Отправка сообщения:
166
+ ```python
167
+ bro = await twitter_client.request_user_data("@username")
168
+ await twitter_client.send_message(bro.id, "I love you!")
169
+ ```
170
+
171
+ Запрос входящих сообщений:
172
+ ```python
173
+ messages = await twitter_client.request_messages()
174
+ for message in messages:
175
+ message_data = message["message_data"]
176
+ recipient_id = message_data["recipient_id"]
177
+ sender_id = message_data["sender_id"]
178
+ text = message_data["text"]
179
+ print(f"[id {sender_id}] -> [id {recipient_id}]: {text}")
180
+ ```
181
+
182
+ Другие методы:
183
+ ```python
184
+ # Выражение любви через твит
185
+ tweet_id = await twitter_client.tweet("I love YOU! !!!!1!1")
186
+ print(f"Любовь выражена! Tweet id: {tweet_id}")
187
+
188
+ print(f"Tweet is pined: {await twitter_client.pin_tweet(tweet_id)}")
189
+
190
+ # Лайк
191
+ print(f"Tweet {tweet_id} is liked: {await twitter_client.like(tweet_id)}")
192
+
193
+ # Репост (ретвит)
194
+ print(f"Tweet {tweet_id} is retweeted. Tweet id: {await twitter_client.repost(tweet_id)}")
195
+
196
+ # Коммент (реплай)
197
+ print(f"Tweet {tweet_id} is replied. Reply id: {await twitter_client.reply(tweet_id, 'tem razão')}")
198
+
199
+ # Подписываемся на Илона Маска
200
+ print(f"@{elonmusk.username} is followed: {await twitter_client.follow(elonmusk.id)}")
201
+
202
+ # Отписываемся от Илона Маска
203
+ print(f"@{elonmusk.username} is unfollowed: {await twitter_client.unfollow(elonmusk.id)}")
204
+
205
+ tweet_url = 'https://twitter.com/CreamIce_Cone/status/1691735090529976489'
206
+ # Цитата (Quote tweet)
207
+ quote_tweet_id = await twitter_client.quote(tweet_url, 'oh....')
208
+ print(f"Quoted! Tweet id: {quote_tweet_id}")
209
+
210
+ # Запрашиваем первых трех подписчиков
211
+ # (Параметр count по каким-то причинам работает некорректно)
212
+ followers = await twitter_client.request_followers(count=20)
213
+ print("Твои подписчики:")
214
+ for follower in followers:
215
+ print(follower)
216
+ ```
@@ -0,0 +1,15 @@
1
+ twitter/__init__.py,sha256=9KthivNBM-ooKWzAMCBOtC7HuwHLGyhrLeFvDf50vxg,581
2
+ twitter/account.py,sha256=Vb9MCRxIQRODnefchXg3m0BGzQ1mEJnaL3xbv29zGTw,3294
3
+ twitter/base/__init__.py,sha256=x0EHKv4q_FI6xEq2nL4V9s8P6VWr6IaHTqdH9sXB5d8,133
4
+ twitter/base/client.py,sha256=7byb0Psai-dvg_ww6Y7uyE2hV1pfTU653hFgVdRiqXo,478
5
+ twitter/base/session.py,sha256=6-gLhdSCaTCd_zv3YgUtVRGbfiAawXuDRBoo7s5bGSs,2234
6
+ twitter/client.py,sha256=bIwoNFTcznAzth76Xcio8eXoe7seMQyEQpJ3fnMJqGs,53114
7
+ twitter/errors.py,sha256=U6kGyNp_5tEq-RwxLjm61muJLEp5BYBq9vrPBkCxr_g,4088
8
+ twitter/models.py,sha256=3-Lft160msCqOjRPubOmxMqWUkmjlTSzHSGsvZK91nU,1817
9
+ twitter/utils/__init__.py,sha256=pyhQXwTdp0HFwV_UNF4dTyklLD9RtaefA16SrQXeNlg,589
10
+ twitter/utils/file.py,sha256=-6n8I8KWDlntfciJJsfIeOi0gmqoHRIe1ldIx1ynGUE,1118
11
+ twitter/utils/html.py,sha256=Cs55MxVyZLSKiCEj11ALUrnCW9ADZ4CEDCE0gKESzO0,1627
12
+ twitter/utils/other.py,sha256=4NaGd2CIJVrDiW17shcrDlJRqFkQNbBSTiiH7kNWcww,559
13
+ tweepy_self-1.0.0.dist-info/METADATA,sha256=RMRe1Jgbsd9csQGYeA1mhRd4d-qszaV7d46th6cw4UQ,9139
14
+ tweepy_self-1.0.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
15
+ tweepy_self-1.0.0.dist-info/RECORD,,
twitter/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ """
2
+ Twitter API Wrapper
3
+ ~~~~~~~~~~~~~~~~~~~
4
+
5
+ A basic wrapper for the Twitter user API.
6
+ """
7
+
8
+ from .client import Client
9
+ from .account import Account, AccountStatus, load_accounts_from_file, extract_accounts_to_file
10
+ from .models import Tweet, UserData
11
+ from . import errors, utils
12
+
13
+ __all__ = [
14
+ "Client",
15
+ "Account",
16
+ "AccountStatus",
17
+ "utils",
18
+ "errors",
19
+ "load_accounts_from_file",
20
+ "extract_accounts_to_file",
21
+ ]
22
+
23
+
24
+ import warnings
25
+ # HACK: Ignore event loop warnings from curl_cffi
26
+ warnings.filterwarnings('ignore', module='curl_cffi')
twitter/account.py ADDED
@@ -0,0 +1,98 @@
1
+ from pathlib import Path
2
+ from typing import Sequence, Iterable
3
+ import enum
4
+
5
+ from pydantic import BaseModel, Field
6
+ import pyotp
7
+
8
+ from .utils import hidden_value, load_lines, write_lines
9
+
10
+
11
+ class AccountStatus(enum.StrEnum):
12
+ BAD_TOKEN = "BAD_TOKEN" # (401) 32
13
+ UNKNOWN = "UNKNOWN"
14
+ SUSPENDED = "SUSPENDED" # (403) 64, (200) 141
15
+ LOCKED = "LOCKED" # (403) 326
16
+ GOOD = "GOOD"
17
+
18
+ def __str__(self):
19
+ return self.value
20
+
21
+
22
+ class Account(BaseModel):
23
+ auth_token: str | None = Field(default=None, pattern=r"^[a-f0-9]{40}$")
24
+ ct0: str | None
25
+ id: int | None
26
+ name: str | None
27
+ username: str | None
28
+ password: str | None
29
+ email: str | None
30
+ key2fa: str | None = Field(default=None, pattern=r"^[a-f0-9]{12}$")
31
+ backup_code: str | None = Field(default=None, pattern=r"^[A-Z0-9]{16}$")
32
+ status: AccountStatus = AccountStatus.UNKNOWN
33
+
34
+ @property
35
+ def hidden_auth_token(self) -> str | None:
36
+ return hidden_value(self.auth_token) if self.auth_token else None
37
+
38
+ @property
39
+ def hidden_password(self) -> str | None:
40
+ return hidden_value(self.password) if self.password else None
41
+
42
+ @property
43
+ def hidden_key2fa(self) -> str | None:
44
+ return hidden_value(self.key2fa) if self.key2fa else None
45
+
46
+ @property
47
+ def hidden_backup_code(self) -> str | None:
48
+ return hidden_value(self.backup_code) if self.backup_code else None
49
+
50
+ def __repr__(self):
51
+ return f"{self.__class__.__name__}(auth_token={self.hidden_auth_token}, username={self.username})"
52
+
53
+ def __str__(self):
54
+ return self.hidden_auth_token
55
+
56
+ def get_2fa_code(self) -> str | None:
57
+ if not self.key2fa:
58
+ raise ValueError("No key2fa")
59
+
60
+ return str(pyotp.TOTP(self.key2fa).now())
61
+
62
+
63
+ def load_accounts_from_file(
64
+ filepath: Path | str,
65
+ *,
66
+ separator: str = ":",
67
+ fields: Sequence[str] = ("auth_token", "password", "email", "username"),
68
+ ) -> list[Account]:
69
+ """
70
+ :param filepath: Путь до файла с данными об аккаунтах.
71
+ :param separator: Разделитель между данными в строке.
72
+ :param fields: Кортеж, содержащий имена полей в порядке их появления в строке.
73
+ :return: Список Twitter аккаунтов.
74
+ """
75
+ accounts = []
76
+ for line in load_lines(filepath):
77
+ data = dict(zip(fields, line.split(separator)))
78
+ data.update({key: None for key in data if not data[key]})
79
+ accounts.append(Account(**data))
80
+ return accounts
81
+
82
+
83
+ def extract_accounts_to_file(
84
+ filepath: Path | str,
85
+ accounts: Iterable[Account],
86
+ *,
87
+ separator: str = ":",
88
+ fields: Sequence[str] = ("auth_token", "password", "email", "username"),
89
+ ):
90
+ lines = []
91
+ for account in accounts:
92
+ account_data = []
93
+ for field_name in fields:
94
+ field = getattr(account, field_name)
95
+ field = field if field is not None else ""
96
+ account_data.append(field)
97
+ lines.append(separator.join(account_data))
98
+ write_lines(filepath, lines)
@@ -0,0 +1,7 @@
1
+ from .client import BaseClient
2
+ from .session import BaseAsyncSession
3
+
4
+ __all__ = [
5
+ "BaseClient",
6
+ "BaseAsyncSession",
7
+ ]
twitter/base/client.py ADDED
@@ -0,0 +1,20 @@
1
+ from .session import BaseAsyncSession
2
+
3
+
4
+ class BaseClient:
5
+ _DEFAULT_HEADERS = None
6
+
7
+ def __init__(self, **session_kwargs):
8
+ self._session = BaseAsyncSession(
9
+ headers=session_kwargs.pop("headers", None) or self._DEFAULT_HEADERS,
10
+ **session_kwargs,
11
+ )
12
+
13
+ async def __aenter__(self):
14
+ return self
15
+
16
+ async def __aexit__(self, *args):
17
+ self.close()
18
+
19
+ def close(self):
20
+ self._session.close()
@@ -0,0 +1,56 @@
1
+ from curl_cffi import requests
2
+ from better_proxy import Proxy
3
+
4
+
5
+ class BaseAsyncSession(requests.AsyncSession):
6
+ """
7
+ Базовая асинхронная сессия:
8
+ - Принимает прокси в формате URL и better-proxy.
9
+ - Отключает верификацию SSL сертификатов по умолчанию.
10
+ - По умолчанию устанавливает версию браузера chrome120.
11
+ - По умолчанию устанавливает user-agent под версию браузера chrome120.
12
+ """
13
+ proxy: Proxy | None
14
+ DEFAULT_HEADERS = {
15
+ "accept": "*/*",
16
+ "accept-language": "en-US,en",
17
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
18
+ "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
19
+ "sec-ch-ua-platform": '"Windows"',
20
+ "sec-ch-ua-mobile": "?0",
21
+ "sec-fetch-dest": "empty",
22
+ "sec-fetch-mode": "cors",
23
+ "sec-fetch-site": "same-origin",
24
+ "connection": "keep-alive",
25
+ }
26
+ DEFAULT_IMPERSONATE = requests.BrowserType.chrome120
27
+
28
+ def __init__(
29
+ self,
30
+ proxy: str | Proxy = None,
31
+ **session_kwargs,
32
+ ):
33
+ self._proxy = None
34
+ headers = session_kwargs["headers"] = session_kwargs.get("headers") or {}
35
+ headers.update(self.DEFAULT_HEADERS)
36
+ session_kwargs["impersonate"] = session_kwargs.get("impersonate") or self.DEFAULT_IMPERSONATE
37
+ session_kwargs["verify"] = session_kwargs.get("verify", False)
38
+ super().__init__(**session_kwargs)
39
+ self.proxy = proxy
40
+
41
+ @property
42
+ def user_agent(self) -> str:
43
+ return self.headers["user-agent"]
44
+
45
+ @property
46
+ def proxy(self) -> Proxy | None:
47
+ return self._proxy
48
+
49
+ @proxy.setter
50
+ def proxy(self, proxy: str | Proxy | None):
51
+ if not proxy:
52
+ self.proxies = {}
53
+ return
54
+
55
+ self._proxy = Proxy.from_str(proxy) if proxy else None
56
+ self.proxies = {"http": self._proxy.as_url, "https": self._proxy.as_url}