├── requirements.txt
├── .github
├── renovate.json
└── workflows
│ └── actions.yaml
├── LICENSE
├── README.md
├── .gitignore
└── main.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.32.5
2 | rich==14.2.0
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",
5 | ":disableDependencyDashboard"
6 | ]
7 | }
--------------------------------------------------------------------------------
/.github/workflows/actions.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy (Windows)
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build-windows:
7 | runs-on: windows-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v6
11 |
12 | - name: Set up Python
13 | uses: actions/setup-python@v6
14 | with:
15 | python-version: '3.14'
16 |
17 | - name: Install dependencies
18 | run: |
19 | python -m pip install --upgrade pip
20 | pip install -r requirements.txt
21 |
22 | - name: Package
23 | run: |
24 | pip install pyinstaller
25 | pyinstaller --onefile -n FriendsRemover main.py
26 |
27 | - name: Upload Artifact
28 | uses: actions/upload-artifact@v5
29 | with:
30 | name: FriendsRemover
31 | path: dist/FriendsRemover.exe
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Nazar
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Epic Games Friends Remover
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 | ## Preview
23 | 
24 |
25 | ## Description
26 | This is a bulk Epic Games Store friends remover. You just log in, and the script removes all friends in a single request.
27 |
28 | ## Usage
29 | Epic Games Store Friends Remover is pretty simple to use.
30 |
31 | ### Exe
32 | > [!WARNING]
33 | > **Windows Defender may detect the .exe as a virus because it’s compiled using PyInstaller and has no digital signature.**
34 |
35 | * If you're a Windows user, you can download the `.exe` from [releases](https://github.com/Nazar1ky/epic-games-store-remove-all-friends/releases).
36 | * [Alternative way](https://github.com/Nazar1ky/epic-games-store-remove-all-friends?tab=readme-ov-file#python) to run the script using Python source code.
37 |
38 | ### Python
39 | 1. Make sure [**Python**](https://www.python.org/downloads/) is installed with the ["Add Python to PATH"](https://miro.medium.com/v2/resize:fit:1344/0*7nOyowsPsGI19pZT.png) option checked.
40 | 2. [Download](https://github.com/Nazar1ky/epic-games-store-remove-all-friends/archive/refs/heads/main.zip) the ZIP or clone repository using [**Git**](https://git-scm.com/downloads):
41 | ```
42 | git clone https://github.com/Nazar1ky/epic-games-store-remove-all-friends
43 | ```
44 | 3. Install the required packages:
45 | ```
46 | pip install -U -r requirements.txt
47 | ```
48 | 4. Run the script.
49 | ```
50 | python main.py
51 | ```
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import base64
2 |
3 | import requests
4 | from rich.console import Console
5 | from rich.prompt import Confirm
6 |
7 | TIMEOUT_IN_SECONDS = 10
8 |
9 | # CLIENT_ID:SECRET
10 | CLIENT = b"98f7e42c2e3a4f86a74eb43fbb41ed39:0a2449a2-001a-451e-afec-3e812901c4d7"
11 |
12 |
13 | class FriendsRemover:
14 | def __init__(self) -> None:
15 | # https://github.com/MixV2/EpicResearch/blob/master/docs/auth/auth_clients.md
16 | self.client = base64.b64encode(
17 | CLIENT,
18 | ).decode(
19 | "utf-8",
20 | )
21 |
22 | self.session = requests.Session()
23 |
24 | self.account_id = None
25 | self.bearer = None
26 |
27 | def run(self) -> None:
28 | console = Console()
29 | console.clear()
30 |
31 | console.print("[yellow bold]Epic Games Store Friends Remover")
32 |
33 | token = self.token()["access_token"]
34 |
35 | device_code = self.device_code(token)
36 |
37 | console.input(
38 | f"[bold][green]Login (press enter to continue):[/green] [red]{device_code['verification_uri_complete']}",
39 | )
40 |
41 | try:
42 | data = self.device_code_verify(device_code["device_code"])
43 | except requests.HTTPError:
44 | console.print("[red]User not logged in!")
45 | return
46 |
47 | self.account_id = data["account_id"]
48 | self.bearer = data["access_token"]
49 |
50 | console.print(f"[bold][green]Successfully logged in [blue]{data['displayName']}")
51 |
52 | friends = self._get_friends()
53 |
54 | friends_count = len(friends["friends"])
55 |
56 | confirm = Confirm.ask(
57 | f"[bold][yellow]You have [blue]{friends_count} [yellow]friends! "
58 | "You sure you want to remove all friends?",
59 | )
60 |
61 | if confirm:
62 | self._remove_friends()
63 | console.print(f"[bold][green]Removed {friends_count} friends!")
64 |
65 | self._kill_session()
66 |
67 | console.print(
68 | "[bold][yellow]Session has been killed",
69 | )
70 |
71 | console.input()
72 |
73 | def token(self) -> dict:
74 | """https://github.com/MixV2/EpicResearch/blob/master/docs/auth/grant_types/client_credentials.md"""
75 | headers = {"Authorization": f"basic {self.client}"}
76 |
77 | body = {"grant_type": "client_credentials"}
78 |
79 | response = self.session.post(
80 | "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token",
81 | headers=headers,
82 | data=body,
83 | timeout=TIMEOUT_IN_SECONDS,
84 | )
85 |
86 | response.raise_for_status()
87 |
88 | return response.json()
89 |
90 | def device_code(self, token: str) -> dict:
91 | """https://github.com/LeleDerGrasshalmi/FortniteEndpointsDocumentation/blob/main/EpicGames/AccountService/Authentication/DeviceCode/Create.md"""
92 | headers = {
93 | "Authorization": f"bearer {token}",
94 | }
95 |
96 | response = self.session.post(
97 | "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/deviceAuthorization",
98 | headers=headers,
99 | timeout=TIMEOUT_IN_SECONDS,
100 | )
101 |
102 | response.raise_for_status()
103 |
104 | return response.json()
105 |
106 | def device_code_verify(self, device_code) -> dict:
107 | """https://github.com/MixV2/EpicResearch/blob/master/docs/auth/grant_types/device_code.md"""
108 | headers = {
109 | "Authorization": f"basic {self.client}",
110 | }
111 |
112 | body = {
113 | "grant_type": "device_code",
114 | "device_code": device_code,
115 | }
116 |
117 | response = self.session.post(
118 | "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token",
119 | headers=headers,
120 | data=body,
121 | timeout=TIMEOUT_IN_SECONDS,
122 | )
123 |
124 | response.raise_for_status()
125 |
126 | return response.json()
127 |
128 | def _get_friends(self) -> dict:
129 | """https://github.com/LeleDerGrasshalmi/FortniteEndpointsDocumentation/blob/main/EpicGames/FriendsService/Friends/FriendsList.md"""
130 | headers = {
131 | "Authorization": f"bearer {self.bearer}",
132 | }
133 |
134 | response = self.session.get(
135 | f"https://friends-public-service-prod.ol.epicgames.com/friends/api/v1/{self.account_id}/summary",
136 | headers=headers,
137 | timeout=TIMEOUT_IN_SECONDS,
138 | )
139 |
140 | response.raise_for_status()
141 |
142 | return response.json()
143 |
144 | def _remove_friends(self) -> None:
145 | """https://github.com/LeleDerGrasshalmi/FortniteEndpointsDocumentation/blob/main/EpicGames/FriendsService/Friends/ClearFriendsList.md"""
146 | headers = {
147 | "Authorization": f"bearer {self.bearer}",
148 | }
149 |
150 | self.session.delete(
151 | f"https://friends-public-service-prod.ol.epicgames.com/friends/api/v1/{self.account_id}/friends",
152 | headers=headers,
153 | timeout=TIMEOUT_IN_SECONDS,
154 | )
155 |
156 | def _kill_session(self) -> None:
157 | """https://github.com/LeleDerGrasshalmi/FortniteEndpointsDocumentation/blob/main/EpicGames/AccountService/Authentication/Kill/Session.md"""
158 | headers = {
159 | "Authorization": f"bearer {self.bearer}",
160 | }
161 |
162 | self.session.delete(
163 | f"https://account-public-service-prod.ol.epicgames.com/account/api/oauth/sessions/kill/{self.bearer}",
164 | headers=headers,
165 | timeout=TIMEOUT_IN_SECONDS,
166 | )
167 |
168 | def main() -> None:
169 | friends_remover = FriendsRemover()
170 | friends_remover.run()
171 |
172 | if __name__ == "__main__":
173 | main()
174 |
--------------------------------------------------------------------------------