├── twAuto.egg-info ├── dependency_links.txt ├── top_level.txt ├── requires.txt ├── SOURCES.txt └── PKG-INFO ├── twAuto ├── __init__.py └── twauto.py ├── MANIFEST.in ├── .gitignore ├── setup.py ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── python-publish.yml ├── LICENCE.MD ├── CODE_OF_CONDUCT.md ├── README.md └── CONTRIBUTING.md /twAuto.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /twAuto.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | twAuto 2 | -------------------------------------------------------------------------------- /twAuto/__init__.py: -------------------------------------------------------------------------------- 1 | from .twauto import twAuto 2 | -------------------------------------------------------------------------------- /twAuto.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | selenium 2 | bs4 3 | webdriver-manager 4 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.txt 3 | global-include *.txt *.py 4 | -------------------------------------------------------------------------------- /twAuto.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | LICENCE.MD 2 | MANIFEST.in 3 | README.md 4 | setup.py 5 | twAuto/__init__.py 6 | twAuto/twauto.py 7 | twAuto.egg-info/PKG-INFO 8 | twAuto.egg-info/SOURCES.txt 9 | twAuto.egg-info/dependency_links.txt 10 | twAuto.egg-info/requires.txt 11 | twAuto.egg-info/top_level.txt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/twAuto-0.1-py3-none-any.whl 2 | dist/twAuto-0.1.tar.gz 3 | dist/twAuto-0.2-py3-none-any.whl 4 | dist/twAuto-0.2.2-py3-none-any.whl 5 | dist/twAuto-0.2.2.tar.gz 6 | dist/twAuto-0.2.tar.gz 7 | dist/twAuto-0.3.4-py3-none-any.whl 8 | dist/twAuto-0.3.4.tar.gz 9 | dist/twAuto-0.3.4-py3-none-any.whl 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='twAuto', 5 | version='0.4.0', 6 | packages=find_packages(exclude=['tests*']), 7 | license='MIT', 8 | description='TwAuto is a library for testing your code on pre-Twitter API application stage.', 9 | long_description=open('README.md', encoding="utf8").read(), 10 | long_description_content_type='text/markdown', 11 | install_requires=['selenium==4.4.3', 'bs4', 'webdriver-manager'], 12 | url='https://github.com/EKOzkan/twAuto', 13 | author='Ekin Kagan Ozkan', 14 | author_email='ekinkagan@gmail.com' 15 | ) 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Code** 24 | If applicable, add the code to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /LICENCE.MD: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct - twAuto 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behaviour that contributes to a positive environment for our 15 | community include: 16 | 17 | * Demonstrating empathy and kindness toward other people 18 | * Being respectful of differing opinions, viewpoints, and experiences 19 | * Giving and gracefully accepting constructive feedback 20 | * Accepting responsibility and apologising to those affected by our mistakes, 21 | and learning from the experience 22 | * Focusing on what is best not just for us as individuals, but for the 23 | overall community 24 | 25 | Examples of unacceptable behaviour include: 26 | 27 | * The use of sexualised language or imagery, and sexual attention or advances 28 | * Trolling, insulting or derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or email 31 | address, without their explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying and enforcing our standards of 38 | acceptable behaviour and will take appropriate and fair corrective action in 39 | response to any instances of unacceptable behaviour. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or reject 42 | comments, commits, code, wiki edits, issues, and other contributions that are 43 | not aligned to this Code of Conduct, or to ban 44 | temporarily or permanently any contributor for other behaviours that they deem 45 | inappropriate, threatening, offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies within all community spaces, and also applies when 50 | an individual is officially representing the community in public spaces. 51 | Examples of representing our community include using an official e-mail address, 52 | posting via an official social media account, or acting as an appointed 53 | representative at an online or offline event. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behaviour may be 58 | reported to the community leaders responsible for enforcement at . 59 | All complaints will be reviewed and investigated promptly and fairly. 60 | 61 | All community leaders are obligated to respect the privacy and security of the 62 | reporter of any incident. 63 | 64 | ## Attribution 65 | 66 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version 67 | [1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and 68 | [2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md), 69 | and was generated by [contributing-gen](https://github.com/bttger/contributing-gen). 70 | -------------------------------------------------------------------------------- /twAuto.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: twAuto 3 | Version: 0.3.4 4 | Summary: TwAuto is a library for testing your code on pre-Twitter API application stage. 5 | Home-page: https://github.com/EKOzkan/twAuto 6 | Author: Ekin Kagan Ozkan 7 | Author-email: ekinkagan@gmail.com 8 | License: MIT 9 | Description-Content-Type: text/markdown 10 | License-File: LICENCE.MD 11 | 12 | # twAuto - Twitter Automation Tool v0.3.4 🦆 13 | 14 | twAuto is a library for "Tweeting", "Retweeting", "Replying", "Tweet Quoting", "Tweet Liking" without any API requirements using Selenium. 15 | 16 |
17 | 18 | Note: While using this library I didnt encounter any problems/bad response from Twitter like banning account etc. but please use at your own risk. 19 | 20 | ## Requirements 21 | 22 | - Python 3.7+ 23 | 24 | - [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) 25 | 26 | - [selenium](https://pypi.org/project/selenium/) 27 | 28 | - [webdriver-manager](https://pypi.org/project/webdriver-manager/) 29 | 30 | ## Installation 31 | 32 | **Pip:** 33 | 34 | ```bash 35 | pip3 install twAuto 36 | ``` 37 | 38 | ## Functions 39 | 40 | **- Import:** 41 | 42 | ```python 43 | import twAuto 44 | ``` 45 | 46 |
47 | 48 | **- Configure:** 49 | 50 | ```python 51 | tw = twAuto.twAuto( 52 | username="Your Twitter Username", 53 | email="Your Twitter E-Mail", 54 | password="Your Twitter Password", 55 | chromeDriverMode="manual" or "auto", #if you use auto twAuto will automatically download the chrome driver for you, 56 | #if you use the manual option, you need to provide the driver path in driverPath parameter. 57 | pathType="testId" or "xPath", #It is testId by default. If you had any problems with library you can try the xPath mode too. 58 | headless=True/False, #Headless is true by default. 59 | debugMode= True/False #Really poorly implemented debug mode, this is for reading occured errors. 60 | #It is not realiable right now but you can give it a try if you want to. 61 | ) 62 | 63 | ``` 64 | 65 |
66 | 67 | **- Start:** Start functions runs the selenium driver. 68 | 69 | ```python 70 | tw.start() 71 | ``` 72 | 73 |
74 | 75 | **- Login:** Logs in to the Twitter account 76 | 77 | ```python 78 | tw.login() 79 | ``` 80 | 81 |
82 | 83 | **- Login Errors:** If you encounter any error in the login process, you can use manualCookieCreation() to get your cookie file manually. 84 | Run the function after tw.start() line. Then after the browser window opened, login to account you want to automate, then enter any character in the terminal. This will create a cookie file after this, you can use the library. 85 | 86 | Note: Headless must be False to use this function. 87 | 88 | ```python 89 | tw.manualCookieCreation() 90 | ``` 91 | 92 | Example: 93 | 94 | First run this code to get your cookie file. 95 | 96 | ```python 97 | tw = twAuto.twAuto( 98 | username="Your Twitter Username", 99 | email="Your Twitter E-Mail", 100 | password="Your Twitter Password", 101 | chromeDriverMode="auto", 102 | pathType="xPath", 103 | headless=False #Headless must be False to use this function. 104 | ) 105 | 106 | tw.start() 107 | tw.manualCookieCreation() 108 | ``` 109 | 110 | After doing the steps that is described above, you can run your main code. 111 | 112 | ```python 113 | tw = twAuto.twAuto( 114 | username="Your Twitter Username", 115 | email="Your Twitter E-Mail", 116 | password="Your Twitter Password", 117 | chromeDriverMode="auto", 118 | pathType="xPath" 119 | ) 120 | 121 | tw.start() 122 | tw.login() 123 | #other functions... 124 | ``` 125 | 126 |
127 | 128 | **- Like:** Likes tweet in the given url \ 129 | ->Returns: True/False as Success/Failed 130 | 131 | ```python 132 | tw.like(url="") 133 | ``` 134 | 135 |
136 | 137 | **- Reply:** Replies to the tweet in the given url with given text.\ 138 | ->Returns: Reply URL/False 139 | 140 | ```python 141 | tw.reply(url="", imgpath="", text="") 142 | ``` 143 | 144 |
145 | 146 | **- Tweet:** Tweets the text and image if given.\ 147 | ->Returns: Tweet URL/False 148 | 149 | ```python 150 | tw.tweet(text="",imgpath="") 151 | ``` 152 | 153 |
154 | 155 | **- Quote Tweet:** Quotes the tweet in the given url with the given text.\ 156 | ->Returns: Quoted Tweet URL/False 157 | 158 | ```python 159 | tw.quoteTweet(url="", imgpath="" ,text="") 160 | ``` 161 | 162 |
163 | 164 | **- Retweet:** Retweets the tweet in the given url.\ 165 | ->Returns: True/False as Success/Failed 166 | 167 | ```python 168 | tw.retweet(url="") 169 | ``` 170 | 171 |
172 | 173 | **- Unretweet:** Unretweets the tweet in the given url.\ 174 | ->Returns: True/False as Success/Failed 175 | 176 | ```python 177 | tw.unretweet(url="") 178 | ``` 179 | 180 |
181 | 182 | **- Logout:** Logs out from current Twitter account and deletes the cookies file. 183 | 184 | ```python 185 | tw.logout() 186 | ``` 187 | 188 |
189 | 190 | **- Quit/Close:** Ends the session, closes the selenium driver application 191 | 192 | ```python 193 | tw.close() 194 | ``` 195 | 196 | ## Example Code 197 | 198 | ```python 199 | tw = twAuto.twAuto( 200 | username="", 201 | email="", 202 | password="", 203 | headless=True, 204 | chromeDriverMode="auto", 205 | pathType="testId") 206 | 207 | tw.start() 208 | tw.login() 209 | 210 | tw.reply(url="",imgpath="", text="") 211 | 212 | tw.close() 213 | 214 | ``` 215 | 216 | ## To Do's 📝 : 217 | 218 | - [x] Send image with Quote, Reply. 219 | - [ ] Send gif with Quote, Reply. 220 | - [x] Retweet without adding url at the end. 221 | - [ ] Linux integration(not tested yet). 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # twAuto - Twitter Automation Tool v0.4.0 🦆 ![PyPI](https://img.shields.io/pypi/v/twauto) [![Downloads](https://static.pepy.tech/badge/twauto)](https://pepy.tech/project/twauto) 2 | 3 | twAuto is a library for "Tweeting", "Retweeting", "Replying", "Tweet Quoting", "Tweet Liking" without any API requirements using Selenium. 4 | 5 |
6 | 7 | Note: While using this library I didnt encounter any problems/bad response from Twitter like banning account etc. but please use at your own risk. 8 | 9 | ## Requirements 10 | 11 | - Python 3.7+ 12 | 13 | - [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) 14 | 15 | - [selenium 4.4.3](https://pypi.org/project/selenium/4.4.3/) 16 | 17 | - [webdriver-manager](https://pypi.org/project/webdriver-manager/) 18 | 19 | ## Installation 20 | 21 | **Pip:** 22 | 23 | ```bash 24 | pip3 install twAuto 25 | ``` 26 | 27 | ## Functions 28 | 29 | **- Import:** 30 | 31 | ```python 32 | import twAuto 33 | ``` 34 | 35 |
36 | 37 | **- Configure:** 38 | 39 | ```python 40 | tw = twAuto.twAuto( 41 | username="Your Twitter Username", 42 | email="Your Twitter E-Mail", 43 | password="Your Twitter Password", 44 | chromeDriverMode="manual" or "auto", #if you use auto twAuto will automatically download the chrome driver for you, 45 | #if you use the manual option, you need to provide the driver path in driverPath parameter. 46 | driverPath="your drivers path", #use only if you are using the chromeDriverMode in manual mode 47 | pathType="testId" or "xPath", #It is testId by default. I highly recommend you to use testId instead of xPath. If you had any problems with library you can try the xPath mode too. 48 | headless=True/False, #Headless is true by default. 49 | debugMode= True/False #Really poorly implemented debug mode, this is for reading occured errors. 50 | #It is not reliable right now but you can give it a try if you want to. 51 | createCookies= True/False #True by default. 52 | ) 53 | 54 | ``` 55 | 56 |
57 | 58 | **- Start:** Start functions runs the selenium driver. 59 | 60 | ```python 61 | tw.start() 62 | ``` 63 | 64 |
65 | 66 | **- Login:** Logs in to the Twitter account 67 | 68 | ```python 69 | tw.login() 70 | ``` 71 | 72 |
73 | 74 | **- Login Errors:** If you encounter any errors in the login process, you can use manualCookieCreation() to get your cookie file manually. 75 | Run the function after tw.start() line. Then after the browser window opened, login to account you want to automate, then enter any character in the terminal. This will create a cookie file after this, you can use the library. 76 | 77 | Note: Headless must be False to use this function. 78 | 79 | ```python 80 | tw.manualCookieCreation() 81 | ``` 82 | 83 | Example: 84 | 85 | First run this code to get your cookie file. 86 | 87 | ```python 88 | tw = twAuto.twAuto( 89 | username="Your Twitter Username", 90 | email="Your Twitter E-Mail", 91 | password="Your Twitter Password", 92 | chromeDriverMode="auto", 93 | pathType="xPath", 94 | headless=False #Headless must be False to use this function. 95 | ) 96 | 97 | tw.start() 98 | tw.manualCookieCreation() 99 | ``` 100 | 101 | After doing the steps that is described above, you can run your main code. 102 | 103 | ```python 104 | tw = twAuto.twAuto( 105 | username="Your Twitter Username", 106 | email="Your Twitter E-Mail", 107 | password="Your Twitter Password", 108 | chromeDriverMode="auto", 109 | pathType="xPath" 110 | ) 111 | 112 | tw.start() 113 | tw.login() 114 | #other functions... 115 | ``` 116 | 117 |
118 | 119 | **- Like:** Likes tweet in the given url \ 120 | ->Returns: True/False as Success/Failed 121 | 122 | ```python 123 | tw.like(url="") 124 | ``` 125 | 126 |
127 | 128 | **- Reply:** Replies to the tweet in the given url with given text.\ 129 | ->Returns: Reply URL/False 130 | 131 | ```python 132 | tw.reply(url="", imgpath="", text="") 133 | ``` 134 | 135 |
136 | 137 | **- Tweet:** Tweets the text and image if given.\ 138 | ->Returns: Tweet URL/False 139 | 140 | ```python 141 | tw.tweet(text="",imgpath="") 142 | ``` 143 | 144 |
145 | 146 | **- Quote Tweet:** Quotes the tweet in the given url with the given text.\ 147 | ->Returns: Quoted Tweet URL/False 148 | 149 | ```python 150 | tw.quoteTweet(url="", imgpath="" ,text="") 151 | ``` 152 | 153 |
154 | 155 | **- Retweet:** Retweets the tweet in the given url.\ 156 | ->Returns: True/False as Success/Failed 157 | 158 | ```python 159 | tw.retweet(url="") 160 | ``` 161 | 162 |
163 | 164 | **- Unretweet:** Unretweets the tweet in the given url.\ 165 | ->Returns: True/False as Success/Failed 166 | 167 | ```python 168 | tw.unretweet(url="") 169 | ``` 170 | 171 |
172 | 173 | **- Notifications:** Scrapes the latest notifications.\ 174 | ->Returns: An array that contains notification text. 175 | 176 | ```python 177 | tw.scrapeNotifications() 178 | ``` 179 | 180 |
181 | **- Logout:** Logs out from current Twitter account and deletes the cookies file. 182 | 183 | ```python 184 | tw.logout() 185 | ``` 186 | 187 |
188 | 189 | **- Quit/Close:** Ends the session, closes the selenium driver application 190 | 191 | ```python 192 | tw.close() 193 | ``` 194 | 195 | ## Example Code 196 | 197 | ```python 198 | tw = twAuto.twAuto( 199 | username="", 200 | email="", 201 | password="", 202 | headless=True, 203 | chromeDriverMode="auto", 204 | pathType="testId") 205 | 206 | tw.start() 207 | tw.login() 208 | 209 | tw.reply(url="",imgpath="", text="") 210 | 211 | tw.close() 212 | 213 | ``` 214 | 215 | ## To Do's 📝 : 216 | 217 | - [x] Send image with Quote, Reply. 218 | - [ ] Send gif with Quote, Reply. 219 | - [x] Retweet without adding url at the end. 220 | - [ ] Linux integration(not tested yet). 221 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contributing to twAuto🦆 4 | 5 | First off, thanks for taking the time to contribute! ❤️ 6 | 7 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 8 | 9 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: 10 | > - Star the project 11 | > - Refer this project in your project's readme 12 | 13 | 14 | 15 | ## Table of Contents 16 | 17 | - [Code of Conduct](#code-of-conduct) 18 | - [I Have a Question](#i-have-a-question) 19 | - [I Want To Contribute](#i-want-to-contribute) 20 | - [Reporting Bugs](#reporting-bugs) 21 | - [Suggesting Enhancements](#suggesting-enhancements) 22 | - [Improving The Documentation](#improving-the-documentation) 23 | 24 | 25 | ## Code of Conduct 26 | 27 | This project and everyone participating in it is governed by the 28 | [twAuto Code of Conduct](https://github.com/EKOzkan/twAuto/blob//CODE_OF_CONDUCT.md). 29 | By participating, you are expected to uphold this code. Please report unacceptable behaviour 30 | to . 31 | 32 | 33 | ## I Have a Question 34 | 35 | > If you want to ask a question, we assume that you have read the available [Documentation](). 36 | 37 | Before you ask a question, it is best to search for existing [Issues](https://github.com/EKOzkan/twAuto/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. 38 | 39 | If you then still feel the need to ask a question and need clarification, we recommend the following: 40 | 41 | - Open an [Issue](https://github.com/EKOzkan/twAuto/issues/new). 42 | - Provide as much context as you can about what you're running into. 43 | - Provide project, selenium and driver versions. 44 | 45 | We will then take care of the issue as soon as possible. 46 | 47 | 48 | 49 | ## I Want To Contribute 50 | 51 | > ### Legal Notice 52 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project licence. 53 | 54 | ### How to Contribute 55 | 1. Fork the repository to your own GitHub account. 56 | 57 | 2. Clone the forked repository to your local machine. 58 | 59 | 60 | `git clone https://github.com/EKOzkan/twAuto.git` 61 | 62 | 3. Create a new branch for your contribution. 63 | 64 | `git checkout -b feature/your-feature-name` 65 | 66 | 5. Make your changes or additions to the codebase. 67 | 68 | 6. Test your changes thoroughly to ensure they work as expected. 69 | 70 | 7. Commit your changes with a clear and descriptive commit message. 71 | 72 | 73 | `git commit -m "Add feature: your-feature-name"` 74 | 75 | 8. Push your changes to your GitHub fork. 76 | 77 | 78 | `git push origin feature/your-feature-name` 79 | 80 | 9. Open a pull request (PR) to the original repository. 81 | 82 | ### Pull Request Guidelines 83 | When submitting a pull request, please make sure to: 84 | 85 | - Provide a clear and concise title for your PR. 86 | - Describe the purpose of your changes in the PR description. 87 | - Reference any related issues or discussions in your PR description. 88 | - Be open to feedback and be prepared to make additional changes if needed. 89 | 90 | 91 | ### Notes for Contributors 92 | - Please try to use `testId` in the testId mode and `xPath` in the xPath mode. I know that not every element has a `testId`, so at some point you may be forced to use `xPaths`, but please try to avoid it as much as possible for writing the code for the testId mode. 93 | - If you want to add a new feature to the project, please implement it for both modes (xPath and testId). If you cant, no problem, I will check if it possible to do after your pull request. 94 | - Try to avoid functions that affect the user's system, such as copy and paste functions, etc. 95 | 96 | 97 | #### Before Submitting a Bug Report 98 | 99 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. 100 | 101 | - Make sure that you are using the latest version. 102 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)). 103 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/EKOzkan/twAuto/issues?q=label%3Abug). 104 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. 105 | 106 | - Collect information about the bug: 107 | - Stack trace (Traceback) 108 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) 109 | - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. 110 | - Possibly your input and the output 111 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 112 | 113 | #### How Do I Submit a Good Bug Report? 114 | 115 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . 116 | 117 | 118 | We use GitHub issues to track bugs and errors. If you run into an issue with the project: 119 | 120 | - Open an [Issue](https://github.com/EKOzkan/twAuto/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) 121 | - Explain the behaviour you would expect and the actual behaviour. 122 | - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. 123 | - Please provide your code, **DONT FORGET TO DELETE YOUR LOGIN INFO!** 124 | - Try our [Debug Mode](https://github.com/EKOzkan/twAuto#:~:text=debugMode%3D%20True/False%20%23Really%20poorly%20implemented%20debug%20mode%2C%20this%20is%20for%20reading%20occured%20errors.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23It%20is%20not%20reliable%20right%20now%20but%20you%20can%20give%20it%20a%20try%20if%20you%20want%20to.) to provide as much as info you can. 125 | - Provide the information you collected in the previous section. 126 | 127 | 128 | 129 | 130 | ### Suggesting Enhancements 131 | 132 | This section guides you through submitting an enhancement suggestion for twAuto, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. 133 | 134 | #### Before Submitting an Enhancement 135 | 136 | - Make sure that you are using the latest version. 137 | - Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration. 138 | - Perform a [search](https://github.com/EKOzkan/twAuto/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. 139 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. 140 | 141 | #### How Do I Submit a Good Enhancement Suggestion? 142 | 143 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/EKOzkan/twAuto/issues). 144 | 145 | - Use a **clear and descriptive title** for the issue to identify the suggestion. 146 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. 147 | - **Describe the current behaviour** and **explain which behaviour you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. 148 | - **Explain why this enhancement would be useful** to most twAuto users. You may also want to point out the other projects that solved it better and which could serve as inspiration. 149 | 150 | 151 | -------------------------------------------------------------------------------- /twAuto/twauto.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | from selenium import webdriver 3 | from selenium.webdriver.common.keys import Keys 4 | from selenium.common.exceptions import TimeoutException 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | from selenium.webdriver.common.by import By 8 | import time 9 | from socket import timeout 10 | from os.path import exists 11 | import os 12 | import requests 13 | import bs4 14 | from bs4 import BeautifulSoup 15 | from urllib import request 16 | 17 | from selenium.webdriver.chrome.service import Service as ChromiumService 18 | from webdriver_manager.chrome import ChromeDriverManager 19 | from webdriver_manager.core.os_manager import ChromeType 20 | import chromedriver_autoinstaller 21 | 22 | 23 | class twAuto: 24 | driver = None 25 | cookies_exists = exists('cookies.pkl') 26 | chrome_options = webdriver.ChromeOptions() 27 | chrome_options.add_argument("--disable-extensions") 28 | chrome_options.add_argument('--disable-gpu') 29 | chrome_options.add_argument('--ignore-certificate-errors') 30 | chrome_options.add_experimental_option( 31 | "excludeSwitches", ["enable-logging"]) 32 | 33 | def __init__( 34 | self, 35 | password="", 36 | username="", 37 | email="", 38 | headless=True, 39 | debugMode=False, 40 | chromeDriverMode="auto", #manual or auto 41 | driverPath = "./chrome.exe", #if you use manual, pass the driverPath 42 | pathType = "testId", #xPath or testId 43 | createCookies = True 44 | ): 45 | self.email = email 46 | self.username = username 47 | self.password = password 48 | self.chromeDriverMode = chromeDriverMode 49 | self.driverPath = driverPath 50 | self.pathType = pathType 51 | self.headless = headless 52 | self.debugMode = debugMode 53 | self.createCookies = createCookies 54 | if headless: 55 | twAuto.chrome_options.add_argument('--headless') 56 | if debugMode: 57 | print("twAuto started.") 58 | 59 | # start selenium driver 60 | def start(self): 61 | print("Starting twAuto...") 62 | try: 63 | if self.chromeDriverMode == "auto": 64 | print("Downloading Chrome Driver...") 65 | #chromedriver_autoinstaller.install() 66 | 67 | twAuto.driver = webdriver.Chrome(ChromeDriverManager().install(), options=twAuto.chrome_options) 68 | print("Chrome Driver Downloaded Successfully") 69 | else: 70 | print("Using Chrome Driver from the path: "+self.driverPath) 71 | twAuto.driver = webdriver.Chrome(self.driverPath, options=twAuto.chrome_options) 72 | except Exception as e: 73 | if self.debugMode: 74 | print("twAuto Error: ", e) 75 | # test function to open twitter on chrome 76 | def openTw(self): 77 | twAuto.driver.get("https://x.com/home") 78 | 79 | # login to twitter 80 | def login(self): 81 | try: 82 | twAuto.driver.get("https://x.com/") 83 | # this cookie importing prevents 'New login notification" in every action 84 | if twAuto.cookies_exists: 85 | cookies = pickle.load(open("cookies.pkl", "rb")) 86 | for cookie in cookies: 87 | twAuto.driver.add_cookie(cookie) 88 | if twAuto.cookies_exists: 89 | twAuto.driver.get("https://x.com/") 90 | else: 91 | twAuto.driver.get("https://x.com/login") 92 | try: 93 | wait = WebDriverWait(twAuto.driver, 120) 94 | wait.until(EC.presence_of_element_located( 95 | (By.XPATH, "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div/div/div/div[5]/label/div/div[2]/div/input"))) 96 | except TimeoutException: 97 | pass 98 | mailInput = twAuto.driver.find_element( 99 | 'xpath', "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div/div/div/div[5]/label/div/div[2]/div/input") 100 | mailInput.send_keys(self.email) 101 | twAuto.driver.find_element( 102 | 'xpath', "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div/div/div/div[6]/div").click() 103 | time.sleep(3) 104 | #'xpath', "//input[@autocomplete=username']").click() 105 | try: 106 | userNameInput = twAuto.driver.find_element( 107 | 'xpath', "/html/body/div/div/div/div[1]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div/div[3]/div/label/div/div[2]/div[1]/input") 108 | except: 109 | userNameInput = twAuto.driver.find_element( 110 | 'xpath', "/html/body/div/div/div/div[1]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div[2]/label/div/div[2]/div/input") 111 | 112 | userNameInput.send_keys(self.username) 113 | 114 | twAuto.driver.find_element( 115 | 'xpath', "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[2]/div/div/div/div").click() 116 | 117 | try: 118 | wait = WebDriverWait(twAuto.driver, 120) 119 | wait.until(EC.presence_of_element_located( 120 | (By.XPATH, "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div/div[3]/div/label/div/div[2]/div[1]/input"))) 121 | except TimeoutException: 122 | pass 123 | passwordInput = twAuto.driver.find_element( 124 | 'xpath', "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div/div[3]/div/label/div/div[2]/div[1]/input") 125 | passwordInput.send_keys(self.password) 126 | twAuto.driver.find_element( 127 | 'xpath', "//*[@id='layers']/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[2]/div/div[1]/div/div/div").click() 128 | 129 | try: 130 | wait = WebDriverWait(twAuto.driver, 120) 131 | wait.until(EC.presence_of_element_located( 132 | (By.XPATH, "//*[@id='react-root']/div/div/div[2]/header/div/div/div/div[1]/div[2]/nav/a[1]"))) 133 | print("Succesfully Logged In") 134 | except Exception as e: 135 | if self.debugMode: 136 | print("twAuto Error: ", e) 137 | 138 | twAuto.driver.get("https://x.com/Twitter/") 139 | 140 | if not twAuto.cookies_exists: 141 | if self.createCookies: 142 | pickle.dump(twAuto.driver.get_cookies(), open("cookies.pkl", "wb")) 143 | 144 | except Exception as e: 145 | if self.debugMode: 146 | print("twAuto Error: ", e) 147 | return False 148 | 149 | #this functions is for the ones that having trouble in the login process, 150 | #you must pass the headless parameter as true to use this function 151 | #after you login manually, open the console window and enter any key to continue 152 | #this function will save the cookies to the cookies.pkl file 153 | #and after that you can use the login function and module without any problem 154 | def manualCookieCreation(self): 155 | if not self.headless: 156 | twAuto.driver.get("https://x.com/login") 157 | input("Please login to your account. After you login, press any key to save your cookies to current folder.") 158 | pickle.dump(twAuto.driver.get_cookies(), open("cookies.pkl", "wb")) 159 | else: 160 | print("Please pass the headless parameter as False to use this function") 161 | sleep(3) 162 | self.close() 163 | 164 | # tweet text 165 | def tweet(self, imgpath=None, text=""): 166 | # load tweeting page 167 | twAuto.driver.get("https://x.com/home") 168 | urlWithText = "https://x.com/compose/tweet?text="+text 169 | twAuto.driver.get(urlWithText) 170 | if self.pathType=="xPath": 171 | try: 172 | try: 173 | wait = WebDriverWait(twAuto.driver, 120) 174 | wait.until(EC.presence_of_element_located( 175 | (By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div/div[2]/div[2]/div/div/div/div[3]/div/div[1]/div/div/div/div/div[2]/div[3]/div/div/div[2]/div[4]"))) 176 | except TimeoutException: 177 | print('Couldnt tweet.') 178 | 179 | if imgpath != None: 180 | element = twAuto.driver.find_element( 181 | By.XPATH, "//input[@type='file']") 182 | '''//*[@id="layers"]/div[2]/div/div/div/div/div/div[2]/div[2]/div/div/div/div[3]/div/div[1]/div/div/div/div/div[2]/div[3]/div/div/div[1]/div[1]''' 183 | twAuto.driver.execute_script( 184 | "arguments[0].style.display = 'block';", element) 185 | 186 | element.send_keys(imgpath) 187 | 188 | twAuto.driver.find_element(By.XPATH, 189 | '//*[@id="layers"]/div[2]/div/div/div/div/div/div[2]/div[2]/div/div/div/div[3]/div/div[1]/div/div/div/div/div[2]/div[3]/div/div/div[2]/div[4]').click() 190 | try: 191 | wait = WebDriverWait(twAuto.driver, 5) 192 | wait.until(EC.presence_of_element_located( 193 | (By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span"))) 194 | twAuto.driver.find_element( 195 | 'xpath', "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span").click() 196 | tweetUrl = twAuto.driver.current_url 197 | print("Tweeted Successfully") 198 | print("Tweet URL:"+tweetUrl) 199 | return tweetUrl 200 | except TimeoutException: 201 | try: 202 | twAuto.driver.find_element( 203 | 'xpath', "//*[@id='layers']/div[3]/div/div/div/div/div[1]") 204 | print('Couldnt Tweet.') 205 | return None 206 | except: 207 | try: 208 | wait = WebDriverWait(twAuto.driver, 5) 209 | wait.until(EC.presence_of_element_located( 210 | (By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span"))) 211 | twAuto.driver.find_element( 212 | By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span").click() 213 | tweetUrl = twAuto.driver.current_url 214 | print("Tweeted Successfully") 215 | print("Tweet URL:"+tweetUrl) 216 | except: 217 | print('Couldnt Tweet.') 218 | return None 219 | except Exception as e: 220 | if self.debugMode: 221 | print("twAuto Error: ", e) 222 | return False 223 | 224 | if self.pathType == "testId": 225 | try: 226 | try: 227 | wait = WebDriverWait(twAuto.driver, 120) 228 | wait.until(EC.presence_of_element_located((By.XPATH, '//div[@data-testid="tweetButton"]'))) 229 | 230 | except TimeoutException: 231 | print('Couldnt tweet.' ) 232 | 233 | if imgpath != None: 234 | element = twAuto.driver.find_element( 235 | By.XPATH, "//input[@type='file']") 236 | 237 | twAuto.driver.execute_script( 238 | "arguments[0].style.display = 'block';", element) 239 | 240 | element.send_keys(imgpath) 241 | 242 | #find tweet button 243 | tweetButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetButton"]') 244 | 245 | #click tweet button 246 | twAuto.driver.execute_script("arguments[0].click()", tweetButton) 247 | 248 | try: 249 | wait = WebDriverWait(twAuto.driver, 5) 250 | wait.until(EC.presence_of_element_located( 251 | (By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span"))) 252 | twAuto.driver.find_element( 253 | 'xpath', "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span").click() 254 | tweetUrl = twAuto.driver.current_url 255 | print("Tweeted Successfully") 256 | print("Tweet URL:"+tweetUrl) 257 | return tweetUrl 258 | except TimeoutException: 259 | try: 260 | twAuto.driver.find_element( 261 | 'xpath', "//*[@id='layers']/div[3]/div/div/div/div/div[1]") 262 | print('Couldnt Tweet.') 263 | return None 264 | except: 265 | try: 266 | wait = WebDriverWait(twAuto.driver, 5) 267 | wait.until(EC.presence_of_element_located( 268 | (By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span"))) 269 | twAuto.driver.find_element( 270 | By.XPATH, "//*[@id='layers']/div[2]/div/div/div/div/div[2]/a/span").click() 271 | tweetUrl = twAuto.driver.current_url 272 | print("Tweeted Successfully") 273 | print("Tweet URL:"+tweetUrl) 274 | except: 275 | print('Couldnt Tweet.') 276 | return None 277 | except Exception as e: 278 | if self.debugMode: 279 | print("twAuto Error: ", e) 280 | return False 281 | 282 | 283 | # quote tweet. this function uses the "adding the quoted tweets url to end of the text method" but maybe i will add the another version of this function that uses the quote tweet function later 284 | def quoteTweet(self, url="", imgpath="", text=""): 285 | 286 | try: 287 | twAuto.driver.get("https://x.com/home") 288 | fixUrl=url+"?s=20" 289 | twAuto.driver.get(fixUrl) 290 | container_element = self.findTweet(url=url) 291 | 292 | if self.pathType == "testId": 293 | try: 294 | 295 | body_element = container_element.find_element(By.XPATH, './/div[@data-testid="retweet"]') 296 | twAuto.driver.execute_script("arguments[0].click()", body_element) 297 | 298 | except Exception as e: 299 | body_element = container_element.find_element(By.XPATH, './/div[@data-testid="unretweet"]') 300 | twAuto.driver.execute_script("arguments[0].click()", body_element) 301 | try: 302 | quoteButton = twAuto.driver.find_element( 303 | By.XPATH, '//*[@id="layers"]/div[2]/div/div/div/div[2]/div/div[3]/div/div/div/a') 304 | twAuto.driver.execute_script("arguments[0].click()", quoteButton) 305 | input_field = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetTextarea_0"]') 306 | input_field.send_keys(text+" ") 307 | if imgpath != None: 308 | element = twAuto.driver.find_element( 309 | By.XPATH, "//input[@type='file']") 310 | 311 | twAuto.driver.execute_script( 312 | "arguments[0].style.display = 'block';", element) 313 | 314 | element.send_keys(imgpath) 315 | tweetButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetButton"]') 316 | twAuto.driver.execute_script("arguments[0].click()", tweetButton) 317 | return True 318 | except: 319 | return False 320 | try: 321 | quoteButton = twAuto.driver.find_element( 322 | By.XPATH, '//*[@id="layers"]/div[2]/div/div/div/div[2]/div/div[3]/div/div/div/a') 323 | twAuto.driver.execute_script("arguments[0].click()", quoteButton) 324 | input_field = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetTextarea_0"]') 325 | input_field.send_keys(text+" ") 326 | if imgpath != None: 327 | element = twAuto.driver.find_element( 328 | By.XPATH, "//input[@type='file']") 329 | 330 | twAuto.driver.execute_script( 331 | "arguments[0].style.display = 'block';", element) 332 | 333 | element.send_keys(imgpath) 334 | tweetButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetButton"]') 335 | twAuto.driver.execute_script("arguments[0].click()", tweetButton) 336 | return True 337 | except: 338 | return False 339 | 340 | if self.pathType == "xPath": 341 | modified_text = text + "\n" + url 342 | result = self.tweet(text=modified_text, imgpath=imgpath) 343 | if result == None: 344 | print('Quote Tweet Failed') 345 | return None 346 | else: 347 | print('Quoted Tweet Successfully') 348 | print('Quote Tweet URL:'+result) 349 | return result 350 | #old method for qutoing, this method adds url of the tweet at the end of the text for quoting, then tweets the text. 351 | '''modified_text = text + "\n" + url 352 | result = self.tweet(text=modified_text) 353 | if result == None: 354 | print('Quote Tweet Failed') 355 | return None 356 | else: 357 | print('Quoted Tweet Successfully') 358 | print('Quote Tweet URL:'+result) 359 | return result 360 | ''' 361 | except Exception as e: 362 | if self.debugMode: 363 | print("twAuto Error: ", e) 364 | return False 365 | 366 | # retweet and like functions are not working with tweets or replies with no text. I will fix it in the future. 367 | def retweet(self, url=""): 368 | try: 369 | twAuto.driver.get("https://x.com/home") 370 | fixUrl=url+"?s=20" 371 | twAuto.driver.get(fixUrl) 372 | container_element = self.findTweet(url=url) 373 | if self.pathType=="xPath": 374 | try: 375 | try: 376 | body_element = container_element.find_element( 377 | By.XPATH, './/div[3]/div[8]/div/div[2]/div') 378 | except: 379 | body_element = container_element.find_element( 380 | By.XPATH, './/div[3]/div[7]/div/div[2]/div') 381 | body_element.click() 382 | try: 383 | retweetButton = twAuto.driver.find_element( 384 | By.XPATH, '/html/body/div[1]/div/div/div[1]/div[2]/div/div/div/div[2]/div/div[3]/div/div/div/div') 385 | retweetButton.click() 386 | return True 387 | except: 388 | return False 389 | except: 390 | return False 391 | if self.pathType == "testId": 392 | try: 393 | 394 | body_element = container_element.find_element(By.XPATH, './/div[@data-testid="retweet"]') 395 | twAuto.driver.execute_script("arguments[0].click()", body_element) 396 | except Exception as e: 397 | print("This account already retweeted this tweet.") 398 | try: 399 | retweetButton = twAuto.driver.find_element( 400 | By.XPATH, './/div[@data-testid="retweetConfirm"]') 401 | twAuto.driver.execute_script("arguments[0].click()", retweetButton) 402 | return True 403 | except: 404 | return False 405 | except Exception as e: 406 | if self.debugMode: 407 | print("twAuto Error: ", e) 408 | return False 409 | 410 | 411 | # likes tweet 412 | def like(self, url=""): 413 | try: 414 | twAuto.driver.get("https://x.com/home") 415 | fixUrl=url+"?s=20" 416 | twAuto.driver.get(fixUrl) 417 | container_element = self.findTweet(url=url) 418 | if self.pathType == "xPath": 419 | try: 420 | try: 421 | body_element = container_element.find_element( 422 | By.XPATH, './/div[3]/div[8]/div/div[3]/div') 423 | except: 424 | body_element = container_element.find_element( 425 | By.XPATH, './/div[3]/div[7]/div/div[3]/div') 426 | body_element.click() 427 | return True 428 | except: 429 | return False 430 | if self.pathType == "testId": 431 | try: 432 | body_element = container_element.find_element(By.XPATH, './/div[@data-testid="like"]') 433 | twAuto.driver.execute_script("arguments[0].click()", body_element) 434 | except: 435 | return False 436 | except Exception as e: 437 | if self.debugMode: 438 | print("twAuto Error: ", e) 439 | return False 440 | # reply to a tweet 441 | def reply(self, url="", imgpath="", text=""): 442 | try: 443 | twAuto.driver.get("https://x.com/home") 444 | tweet_id = self.extract_tweet_id(url) 445 | urlWithText = "https://x.com/intent/tweet?in_reply_to="+tweet_id+"&text="+text 446 | twAuto.driver.get(urlWithText) 447 | if self.pathType == "testId" or self.pathType == "xPath": 448 | #data-testid="tweetTextarea_0_label"]tweetButton 449 | try: 450 | try: 451 | if imgpath != "": 452 | element = twAuto.driver.find_element( 453 | By.XPATH, "//input[@type='file']") 454 | 455 | twAuto.driver.execute_script( 456 | "arguments[0].style.display = 'block';", element) 457 | 458 | element.send_keys(imgpath) 459 | 460 | #check if there is a mask 461 | try: 462 | maskClose = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="app-bar-close"]') 463 | twAuto.driver.execute_script("arguments[0].click()", maskClose) 464 | except: 465 | pass 466 | #find tweet button 467 | time.sleep(1) 468 | wait = WebDriverWait(twAuto.driver, 120) 469 | 470 | try: 471 | wait.until(EC.presence_of_element_located( 472 | (By.XPATH, '//div[@data-testid="tweetButton"]'))) 473 | tweetButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetButton"]') 474 | #click tweet button 475 | twAuto.driver.execute_script("arguments[0].click()", tweetButton) 476 | except Exception as e: 477 | wait.until(EC.presence_of_element_located( 478 | (By.XPATH, '//div[@data-testid="tweetButton"]'))) 479 | 480 | tweetButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="tweetButton"]') 481 | #click tweet button 482 | twAuto.driver.execute_script("arguments[0].click()", tweetButton) 483 | wait = WebDriverWait(twAuto.driver, 120) 484 | wait.until(EC.presence_of_element_located( 485 | (By.XPATH, '//div[@data-testid="toast"]'))) 486 | replyURLButton = twAuto.driver.find_element( 487 | By.XPATH, '//div[@data-testid="toast"]') 488 | replyURLElement = replyURLButton.find_element(By.XPATH, './/div[2]/a[1]').get_attribute("href") 489 | return replyURLElement 490 | except Exception as e: 491 | if self.debugMode : print("twAuto Error: ", e) 492 | return False 493 | except Exception as e: 494 | if self.debugMode : print("twAuto Error: ", e) 495 | return False 496 | except Exception as e: 497 | if self.debugMode : print("twAuto Error: ", e) 498 | return False 499 | 500 | # locates tweet in the page based on the tweets content 501 | def findTweet(self, url=""): 502 | try: 503 | #twAuto.driver.get(url) 504 | try: 505 | wait = WebDriverWait(twAuto.driver, 120) 506 | wait.until(EC.presence_of_element_located( 507 | (By.CSS_SELECTOR, ".css-1dbjc4n.r-18u37iz.r-15zivkp"))) 508 | time.sleep(1) 509 | tmp_element = twAuto.driver.find_element( 510 | By.CSS_SELECTOR, ".css-1dbjc4n.r-18u37iz.r-15zivkp") 511 | container_element = tmp_element.find_element(By.XPATH, '..') 512 | return container_element 513 | except TimeoutException: 514 | return None 515 | except Exception as e: 516 | if self.debugMode: 517 | print("twAuto Error: ", e) 518 | return False 519 | def scrapeNotifications(self): 520 | print("Scraping notifications...") 521 | twAuto.driver.get("https://x.com/notifications") 522 | try: 523 | wait = WebDriverWait(twAuto.driver, 120) 524 | wait.until(EC.presence_of_element_located( 525 | (By.XPATH, "//*[@id='react-root']/div/div/div[2]/main/div/div/div/div[1]/div/div[3]/section/div/div"))) 526 | except TimeoutException: 527 | print('Couldnt find notifications container') 528 | notifications = twAuto.driver.find_elements(By.XPATH, '//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[1]/div/div[3]/section/div/div/div') 529 | notificationList = [] 530 | for notification in notifications: 531 | notificationList.append(notification.text) 532 | print("Notifications scraped successfully") 533 | print("Notification list:") 534 | return notificationList 535 | # undo retweet action - !!!Unstable!!! 536 | def unretweet(self, url=""): 537 | twAuto.driver.get("https://x.com/home") 538 | fixUrl=url+"?s=20" 539 | twAuto.driver.get(fixUrl) 540 | container_element = self.findTweet(url=url) 541 | #unretweetConfirm 542 | try: 543 | wait = WebDriverWait(twAuto.driver, 120) 544 | wait.until(EC.presence_of_element_located( 545 | (By.XPATH, '//div[@data-testid="unretweet"]'))) 546 | 547 | #find unretweet button 548 | unretweetButton = container_element.find_element(By.XPATH, './/div[@data-testid="unretweet"]') 549 | 550 | #click unretweet button 551 | twAuto.driver.execute_script("arguments[0].click()", unretweetButton) 552 | 553 | #find unretweet button 554 | unretweetConfirmButton = twAuto.driver.find_element(By.XPATH, '//div[@data-testid="unretweetConfirm"]') 555 | 556 | #click unretweet button 557 | twAuto.driver.execute_script("arguments[0].click()", unretweetConfirmButton) 558 | return True 559 | except Exception as e: 560 | if self.debugMode: 561 | print("twAuto Error: ", e) 562 | return False 563 | 564 | # logs out from twitter and deletes the cookies 565 | def logout(self): 566 | twAuto.driver.get("https://x.com/logout") 567 | try: 568 | wait = WebDriverWait(twAuto.driver, 120) 569 | wait.until(EC.presence_of_element_located( 570 | (By.XPATH, "/html/body/div[1]/div/div/div[1]/div[2]/div/div/div/div/div/div[2]/div[2]/div[2]/div[1]"))) 571 | except TimeoutException: 572 | print('Couldnt log out.') 573 | twAuto.driver.find_element( 574 | 'xpath', "/html/body/div[1]/div/div/div[1]/div[2]/div/div/div/div/div/div[2]/div[2]/div[2]/div[1]").click() 575 | os.remove("cookies.pkl") 576 | print("Succesfully logged out") 577 | 578 | # closes selenium driver 579 | def close(self): 580 | twAuto.driver.quit() 581 | def extract_tweet_id(self, url): 582 | url = url.replace("https://", "") # Remove "https://" 583 | url = url.replace("http://", "") # Remove "http://" 584 | print("url:", url) 585 | url_parts = url.split("/") 586 | if "x.com" in url_parts: 587 | url_parts.remove("x.com") 588 | return url_parts[2] if len(url_parts) >= 3 else None 589 | --------------------------------------------------------------------------------